mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-15 21:11:08 +02:00
fix(code-mode): resolve claude/codex installed via version managers
commonInstallPaths only checked ~/.nvm/versions/node/<binary>, never descending into nvm's versioned vX.Y.Z/bin subdirs, so an nvm-installed codex/claude showed "not installed" in Settings and failed code-mode chat with "CLI not found" on GUI launches (where the login-shell PATH isn't inherited). Enumerate each installed Node version's bin dir for nvm/fnm/asdf, and add pnpm global (PNPM_HOME / platform default) and Claude Code's legacy ~/.claude/local install location. Shared by both the Settings status check and the chat/tab binary resolvers, so it covers all code-mode entry points. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5332ebcb06
commit
0d50373576
1 changed files with 42 additions and 4 deletions
|
|
@ -3,7 +3,7 @@ import { promisify } from 'util';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync, readdirSync } from 'fs';
|
||||||
import { CodeModeAgentStatus } from './types.js';
|
import { CodeModeAgentStatus } from './types.js';
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
@ -28,16 +28,54 @@ export function commonInstallPaths(binary: string): string[] {
|
||||||
path.join(home, '.volta', 'bin', `${binary}.cmd`),
|
path.join(home, '.volta', 'bin', `${binary}.cmd`),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [
|
const dirs = [
|
||||||
'/usr/local/bin',
|
'/usr/local/bin',
|
||||||
'/opt/homebrew/bin', // Apple Silicon Homebrew
|
'/opt/homebrew/bin', // Apple Silicon Homebrew
|
||||||
'/usr/bin',
|
'/usr/bin',
|
||||||
path.join(home, '.npm-global', 'bin'),
|
path.join(home, '.npm-global', 'bin'),
|
||||||
path.join(home, '.local', 'bin'),
|
path.join(home, '.local', 'bin'),
|
||||||
path.join(home, '.volta', 'bin'),
|
path.join(home, '.volta', 'bin'),
|
||||||
path.join(home, '.nvm', 'versions', 'node'), // partial; nvm has versioned subdirs
|
|
||||||
path.join(home, 'bin'),
|
path.join(home, 'bin'),
|
||||||
].map(dir => path.join(dir, binary));
|
// Claude Code's legacy local installer / `claude migrate-installer` target.
|
||||||
|
path.join(home, '.claude', 'local'),
|
||||||
|
// pnpm global bin: PNPM_HOME if set, else the platform default
|
||||||
|
// (~/Library/pnpm on macOS, ~/.local/share/pnpm on Linux).
|
||||||
|
process.env.PNPM_HOME ||
|
||||||
|
(process.platform === 'darwin'
|
||||||
|
? path.join(home, 'Library', 'pnpm')
|
||||||
|
: path.join(home, '.local', 'share', 'pnpm')),
|
||||||
|
// Node version managers install into versioned subdirs (e.g.
|
||||||
|
// ~/.nvm/versions/node/v24.16.0/bin) — enumerate each version's bin dir.
|
||||||
|
...versionManagerBinDirs(home),
|
||||||
|
];
|
||||||
|
return dirs.map(dir => path.join(dir, binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
// nvm/fnm/asdf keep a `bin` dir per installed Node version. A static path can't
|
||||||
|
// match (the version segment is unknown), so list the version dirs and append
|
||||||
|
// `bin`. Returns [] when a manager isn't present — readdir failures are ignored.
|
||||||
|
function versionManagerBinDirs(home: string): string[] {
|
||||||
|
const versionRoots = [
|
||||||
|
path.join(home, '.nvm', 'versions', 'node'), // nvm
|
||||||
|
path.join(home, '.local', 'share', 'fnm', 'node-versions'), // fnm (Linux)
|
||||||
|
path.join(home, 'Library', 'Application Support', 'fnm', 'node-versions'),// fnm (macOS)
|
||||||
|
path.join(home, '.asdf', 'installs', 'nodejs'), // asdf
|
||||||
|
];
|
||||||
|
const dirs: string[] = [];
|
||||||
|
for (const root of versionRoots) {
|
||||||
|
let versions: string[];
|
||||||
|
try {
|
||||||
|
versions = readdirSync(root);
|
||||||
|
} catch {
|
||||||
|
continue; // manager not installed
|
||||||
|
}
|
||||||
|
for (const version of versions) {
|
||||||
|
// fnm nests another `installation` dir; nvm/asdf put bin directly under the version.
|
||||||
|
dirs.push(path.join(root, version, 'bin'));
|
||||||
|
dirs.push(path.join(root, version, 'installation', 'bin'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function probeShell(binary: string): Promise<boolean> {
|
async function probeShell(binary: string): Promise<boolean> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue