aicodeman 1.19.7 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/git-clone.d.ts +7 -0
- package/dist/git-clone.d.ts.map +1 -1
- package/dist/git-clone.js +10 -2
- package/dist/git-clone.js.map +1 -1
- package/dist/ralph-loop.d.ts.map +1 -1
- package/dist/ralph-loop.js +8 -1
- package/dist/ralph-loop.js.map +1 -1
- package/dist/session.d.ts +34 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +61 -1
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +11 -8
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/session.d.ts +8 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/types/update.d.ts +45 -0
- package/dist/types/update.d.ts.map +1 -1
- package/dist/utils/antigravity-cli-resolver.d.ts +4 -0
- package/dist/utils/antigravity-cli-resolver.d.ts.map +1 -1
- package/dist/utils/antigravity-cli-resolver.js +17 -29
- package/dist/utils/antigravity-cli-resolver.js.map +1 -1
- package/dist/utils/claude-cli-resolver.d.ts +1 -0
- package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
- package/dist/utils/claude-cli-resolver.js +15 -28
- package/dist/utils/claude-cli-resolver.js.map +1 -1
- package/dist/utils/cli-executable-resolver.d.ts +98 -0
- package/dist/utils/cli-executable-resolver.d.ts.map +1 -0
- package/dist/utils/cli-executable-resolver.js +229 -0
- package/dist/utils/cli-executable-resolver.js.map +1 -0
- package/dist/utils/codex-cli-resolver.d.ts +1 -0
- package/dist/utils/codex-cli-resolver.d.ts.map +1 -1
- package/dist/utils/codex-cli-resolver.js +8 -30
- package/dist/utils/codex-cli-resolver.js.map +1 -1
- package/dist/utils/gemini-cli-resolver.d.ts +1 -0
- package/dist/utils/gemini-cli-resolver.d.ts.map +1 -1
- package/dist/utils/gemini-cli-resolver.js +8 -29
- package/dist/utils/gemini-cli-resolver.js.map +1 -1
- package/dist/utils/index.d.ts +6 -6
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +6 -6
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/opencode-cli-resolver.d.ts +1 -0
- package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
- package/dist/utils/opencode-cli-resolver.js +8 -31
- package/dist/utils/opencode-cli-resolver.js.map +1 -1
- package/dist/utils/pi-cli-resolver.d.ts +12 -3
- package/dist/utils/pi-cli-resolver.d.ts.map +1 -1
- package/dist/utils/pi-cli-resolver.js +40 -55
- package/dist/utils/pi-cli-resolver.js.map +1 -1
- package/dist/web/approval-inbox.d.ts +45 -8
- package/dist/web/approval-inbox.d.ts.map +1 -1
- package/dist/web/approval-inbox.js +116 -28
- package/dist/web/approval-inbox.js.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.56c520ff.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.dc5d04bb.js → constants.5abbd823.js} +53 -0
- package/dist/web/public/constants.5abbd823.js.br +0 -0
- package/dist/web/public/constants.5abbd823.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/i18n.196a992e.js +1 -0
- package/dist/web/public/i18n.196a992e.js.br +0 -0
- package/dist/web/public/i18n.196a992e.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +18 -5
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.0454a3e9.js.gz +0 -0
- package/dist/web/public/mobile-handlers.0f83a608.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.49aeb17e.css.gz +0 -0
- package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/panels-ui.5e97a36e.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.71a0ce9a.js.gz +0 -0
- package/dist/web/public/settings-ui.1a62e935.js +67 -0
- package/dist/web/public/settings-ui.1a62e935.js.br +0 -0
- package/dist/web/public/settings-ui.1a62e935.js.gz +0 -0
- package/dist/web/public/styles.68f9e118.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.5f4158b7.js +2 -0
- package/dist/web/public/terminal-ui.5f4158b7.js.br +0 -0
- package/dist/web/public/terminal-ui.5f4158b7.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.4ec670b0.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/repo-status.d.ts +124 -0
- package/dist/web/repo-status.d.ts.map +1 -0
- package/dist/web/repo-status.js +353 -0
- package/dist/web/repo-status.js.map +1 -0
- package/dist/web/response-viewer-transcript.d.ts +12 -0
- package/dist/web/response-viewer-transcript.d.ts.map +1 -0
- package/dist/web/response-viewer-transcript.js +345 -0
- package/dist/web/response-viewer-transcript.js.map +1 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +41 -22
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +5 -0
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +1 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +26 -3
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/constants.dc5d04bb.js.br +0 -0
- package/dist/web/public/constants.dc5d04bb.js.gz +0 -0
- package/dist/web/public/i18n.6a46f014.js +0 -1
- package/dist/web/public/i18n.6a46f014.js.br +0 -0
- package/dist/web/public/i18n.6a46f014.js.gz +0 -0
- package/dist/web/public/settings-ui.b62c70a3.js +0 -67
- package/dist/web/public/settings-ui.b62c70a3.js.br +0 -0
- package/dist/web/public/settings-ui.b62c70a3.js.gz +0 -0
- package/dist/web/public/terminal-ui.4723f5d4.js +0 -4
- package/dist/web/public/terminal-ui.4723f5d4.js.br +0 -0
- package/dist/web/public/terminal-ui.4723f5d4.js.gz +0 -0
|
@@ -6,20 +6,27 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module utils/antigravity-cli-resolver
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import { existsSync } from 'node:fs';
|
|
11
|
-
import { dirname, join } from 'node:path';
|
|
9
|
+
import { join } from 'node:path';
|
|
12
10
|
import { homedir } from 'node:os';
|
|
13
|
-
import {
|
|
11
|
+
import { createCliExecutableResolver, formatCliNotFoundMessage, } from './cli-executable-resolver.js';
|
|
14
12
|
/** Common directories where the Antigravity CLI binary may be installed */
|
|
15
13
|
const ANTIGRAVITY_SEARCH_DIRS = [
|
|
16
14
|
join(homedir(), '.local', 'bin'),
|
|
17
15
|
join(homedir(), '.antigravity', 'bin'),
|
|
18
16
|
'/usr/local/bin',
|
|
17
|
+
join(homedir(), '.bun', 'bin'),
|
|
18
|
+
join(homedir(), '.npm-global', 'bin'),
|
|
19
19
|
join(homedir(), 'bin'),
|
|
20
20
|
];
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const ANTIGRAVITY_NOT_FOUND = 'Antigravity CLI not found. Install with: curl -fsSL https://antigravity.google/cli/install.sh | bash';
|
|
22
|
+
function createAntigravityResolver(host, now) {
|
|
23
|
+
return createCliExecutableResolver({ binary: 'agy', searchDirs: ANTIGRAVITY_SEARCH_DIRS, now }, host);
|
|
24
|
+
}
|
|
25
|
+
/** Creates an isolated Antigravity wrapper around an injected resolver host and clock. */
|
|
26
|
+
export function createAntigravityResolverForTest(host, now) {
|
|
27
|
+
return createAntigravityResolver(host, now);
|
|
28
|
+
}
|
|
29
|
+
const antigravityResolver = createAntigravityResolver();
|
|
23
30
|
/**
|
|
24
31
|
* Finds the directory containing the `agy` binary.
|
|
25
32
|
* Checks `which agy` first, then falls back to common install locations.
|
|
@@ -27,29 +34,7 @@ let _antigravityDir = null;
|
|
|
27
34
|
* @returns Directory path, or null if not found
|
|
28
35
|
*/
|
|
29
36
|
export function resolveAntigravityDir() {
|
|
30
|
-
|
|
31
|
-
return _antigravityDir || null;
|
|
32
|
-
try {
|
|
33
|
-
const result = execSync('which agy', {
|
|
34
|
-
encoding: 'utf-8',
|
|
35
|
-
timeout: EXEC_TIMEOUT_MS,
|
|
36
|
-
}).trim();
|
|
37
|
-
if (result && existsSync(result)) {
|
|
38
|
-
_antigravityDir = dirname(result);
|
|
39
|
-
return _antigravityDir;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// agy not in PATH, will check common locations
|
|
44
|
-
}
|
|
45
|
-
for (const dir of ANTIGRAVITY_SEARCH_DIRS) {
|
|
46
|
-
if (existsSync(join(dir, 'agy'))) {
|
|
47
|
-
_antigravityDir = dir;
|
|
48
|
-
return _antigravityDir;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
_antigravityDir = '';
|
|
52
|
-
return null;
|
|
37
|
+
return antigravityResolver.resolve()?.directory ?? null;
|
|
53
38
|
}
|
|
54
39
|
/**
|
|
55
40
|
* Check if the Antigravity CLI is available on the system.
|
|
@@ -57,4 +42,7 @@ export function resolveAntigravityDir() {
|
|
|
57
42
|
export function isAntigravityAvailable() {
|
|
58
43
|
return resolveAntigravityDir() !== null;
|
|
59
44
|
}
|
|
45
|
+
export function getAntigravityNotFoundMessage() {
|
|
46
|
+
return formatCliNotFoundMessage(ANTIGRAVITY_NOT_FOUND, antigravityResolver.diagnostics());
|
|
47
|
+
}
|
|
60
48
|
//# sourceMappingURL=antigravity-cli-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"antigravity-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/antigravity-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"antigravity-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/antigravity-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,GAEzB,MAAM,8BAA8B,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,uBAAuB,GAAG;IAC9B,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC;IACtC,gBAAgB;IAChB,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC;IAC9B,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC;IACrC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC;CACvB,CAAC;AAEF,MAAM,qBAAqB,GACzB,sGAAsG,CAAC;AAEzG,SAAS,yBAAyB,CAAC,IAAsB,EAAE,GAAkB;IAC3E,OAAO,2BAA2B,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,uBAAuB,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACxG,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gCAAgC,CAAC,IAAqB,EAAE,GAAkB;IACxF,OAAO,yBAAyB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,mBAAmB,GAAG,yBAAyB,EAAE,CAAC;AAExD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,mBAAmB,CAAC,OAAO,EAAE,EAAE,SAAS,IAAI,IAAI,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,qBAAqB,EAAE,KAAK,IAAI,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO,wBAAwB,CAAC,qBAAqB,EAAE,mBAAmB,CAAC,WAAW,EAAE,CAAC,CAAC;AAC5F,CAAC"}
|
|
@@ -21,6 +21,7 @@ export declare function isClaudeAvailable(): boolean;
|
|
|
21
21
|
* @returns Directory path, or null if not found
|
|
22
22
|
*/
|
|
23
23
|
export declare function findClaudeDir(): string | null;
|
|
24
|
+
export declare function getClaudeNotFoundMessage(): string;
|
|
24
25
|
/**
|
|
25
26
|
* Returns an absolute path to the `claude` binary, falling back to the bare
|
|
26
27
|
* name `'claude'` when it cannot be located (so PATH resolution still gets a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-cli-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/claude-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"claude-cli-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/claude-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAezC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;CACvB;AAOD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,uBAAuB,EAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,MAAM,GAAG,IAAI,GACzB,MAAM,GAAG,IAAI,CAoBf;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAOnD"}
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module utils/claude-cli-resolver
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { delimiter, dirname, join } from 'node:path';
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { delimiter, join } from 'node:path';
|
|
13
12
|
import { homedir } from 'node:os';
|
|
14
13
|
import { EXEC_TIMEOUT_MS } from '../config/exec-timeout.js';
|
|
14
|
+
import { createCliExecutableResolver, formatCliNotFoundMessage } from './cli-executable-resolver.js';
|
|
15
15
|
/** Common directories where the Claude CLI binary may be installed */
|
|
16
16
|
const CLAUDE_SEARCH_DIRS = [
|
|
17
17
|
join(homedir(), '.local', 'bin'),
|
|
@@ -20,8 +20,8 @@ const CLAUDE_SEARCH_DIRS = [
|
|
|
20
20
|
join(homedir(), '.npm-global', 'bin'),
|
|
21
21
|
join(homedir(), 'bin'),
|
|
22
22
|
];
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const claudeResolver = createCliExecutableResolver({ binary: 'claude', searchDirs: CLAUDE_SEARCH_DIRS });
|
|
24
|
+
const CLAUDE_NOT_FOUND = 'Claude CLI not found. Install it with: curl -fsSL https://claude.ai/install.sh | bash';
|
|
25
25
|
/**
|
|
26
26
|
* Returns true if the Claude CLI binary can be located (via `which` or one of
|
|
27
27
|
* the common install directories). Mirrors `isGeminiAvailable`/`isAntigravityAvailable`/`isOpenCodeAvailable`/
|
|
@@ -38,28 +38,10 @@ export function isClaudeAvailable() {
|
|
|
38
38
|
* @returns Directory path, or null if not found
|
|
39
39
|
*/
|
|
40
40
|
export function findClaudeDir() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const result = execSync('which claude', { encoding: 'utf-8', timeout: EXEC_TIMEOUT_MS }).trim();
|
|
46
|
-
if (result && existsSync(result)) {
|
|
47
|
-
_claudeDir = dirname(result);
|
|
48
|
-
return _claudeDir;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// Claude not in PATH, will check common locations
|
|
53
|
-
}
|
|
54
|
-
// Fallback: check common installation directories
|
|
55
|
-
for (const dir of CLAUDE_SEARCH_DIRS) {
|
|
56
|
-
if (existsSync(join(dir, 'claude'))) {
|
|
57
|
-
_claudeDir = dir;
|
|
58
|
-
return _claudeDir;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
_claudeDir = ''; // mark as searched, not found
|
|
62
|
-
return null;
|
|
41
|
+
return claudeResolver.resolve()?.directory ?? null;
|
|
42
|
+
}
|
|
43
|
+
export function getClaudeNotFoundMessage() {
|
|
44
|
+
return formatCliNotFoundMessage(CLAUDE_NOT_FOUND, claudeResolver.diagnostics());
|
|
63
45
|
}
|
|
64
46
|
/**
|
|
65
47
|
* Returns an absolute path to the `claude` binary, falling back to the bare
|
|
@@ -89,7 +71,9 @@ export function getAugmentedPath() {
|
|
|
89
71
|
return _augmentedPath;
|
|
90
72
|
const currentPath = process.env.PATH || '';
|
|
91
73
|
const claudeDir = findClaudeDir();
|
|
92
|
-
if (
|
|
74
|
+
if (!claudeDir)
|
|
75
|
+
return currentPath;
|
|
76
|
+
if (!currentPath.split(delimiter).includes(claudeDir)) {
|
|
93
77
|
_augmentedPath = `${claudeDir}${delimiter}${currentPath}`;
|
|
94
78
|
return _augmentedPath;
|
|
95
79
|
}
|
|
@@ -159,6 +143,9 @@ function probeClaudeCliVersion() {
|
|
|
159
143
|
encoding: 'utf-8',
|
|
160
144
|
timeout: EXEC_TIMEOUT_MS,
|
|
161
145
|
env: { ...process.env, PATH: getAugmentedPath() },
|
|
146
|
+
// execFileSync's timeout only SENDS the signal and then keeps waiting; a
|
|
147
|
+
// child that ignores SIGTERM would block the server thread permanently.
|
|
148
|
+
killSignal: 'SIGKILL',
|
|
162
149
|
});
|
|
163
150
|
const match = out.match(/(\d+\.\d+\.\d+)/);
|
|
164
151
|
return match ? match[1] : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/claude-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"claude-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/claude-cli-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared CLI executable resolution for the per-CLI resolvers.
|
|
3
|
+
*
|
|
4
|
+
* One lookup chain behind all six *-cli-resolver modules (claude, opencode,
|
|
5
|
+
* codex, gemini, antigravity, pi): the server process PATH first, then the
|
|
6
|
+
* CLI's common install directories in order, then — last, because it is the
|
|
7
|
+
* only step that spawns anything — an interactive login shell, which is what
|
|
8
|
+
* finds nvm/Homebrew/user-npm installs when Codeman runs as a systemd/launchd
|
|
9
|
+
* service with a minimal PATH (launchd hands a job `/usr/bin:/bin:/usr/sbin:/sbin`).
|
|
10
|
+
*
|
|
11
|
+
* Caching is asymmetric, same shape as `resolveClaudeCliVersion` in
|
|
12
|
+
* claude-cli-resolver.ts: a successful resolution is cached for the process
|
|
13
|
+
* lifetime, a MISS is negative-cached and retried only after a doubling backoff
|
|
14
|
+
* (`cliResolveRetryDelayMs`). The callers are request-facing (the per-CLI
|
|
15
|
+
* status endpoints in system-routes.ts, the availability gates in
|
|
16
|
+
* session-routes.ts, and tmux-manager's spawn path), and the login-shell probe
|
|
17
|
+
* is a SYNCHRONOUS spawn bounded by `EXEC_TIMEOUT_MS` — without the negative
|
|
18
|
+
* cache, a missing CLI re-ran the whole chain and stalled the event loop for up
|
|
19
|
+
* to 5s on every request, forever.
|
|
20
|
+
*
|
|
21
|
+
* Test hermeticity: under vitest (`process.env.VITEST`) the production host
|
|
22
|
+
* short-circuits — IO primitives that were not injected become inert stubs, so
|
|
23
|
+
* a suite can never scan the machine's PATH or spawn login shells (the same
|
|
24
|
+
* rule as `IS_TEST_MODE` in tmux-manager and the VITEST gate in
|
|
25
|
+
* `getClaudeCliVersion`). Tests opt back in through the injection hooks
|
|
26
|
+
* (`runCommand`/`isExecutableFile` fakes do no real IO by construction) or, for
|
|
27
|
+
* fixtures that need the real filesystem predicate against their own temp
|
|
28
|
+
* files, via `allowRealIoUnderVitest`.
|
|
29
|
+
*
|
|
30
|
+
* @module utils/cli-executable-resolver
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* How long to wait before re-running the resolution chain after `failures`
|
|
34
|
+
* consecutive misses: 1min, 2min, 4min… capped at 5min. Mirrors
|
|
35
|
+
* `claudeVersionRetryDelayMs` in claude-cli-resolver.ts. Exported for tests.
|
|
36
|
+
*/
|
|
37
|
+
export declare function cliResolveRetryDelayMs(failures: number): number;
|
|
38
|
+
export type CliResolutionSource = 'process-path' | 'common-directory' | 'login-shell';
|
|
39
|
+
export interface CliResolutionDiagnostics {
|
|
40
|
+
binary: string;
|
|
41
|
+
processPath: string;
|
|
42
|
+
shellPath: string;
|
|
43
|
+
shellArgs: string[];
|
|
44
|
+
searchDirs: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface CliResolverHost {
|
|
47
|
+
processPath: string;
|
|
48
|
+
shellPath: string;
|
|
49
|
+
shellArgs: string[];
|
|
50
|
+
findOnProcessPath(binary: string): string | null;
|
|
51
|
+
findInLoginShell(binary: string): string | null;
|
|
52
|
+
exists(path: string): boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface CandidateValidation<T> {
|
|
55
|
+
accepted: boolean;
|
|
56
|
+
metadata?: T;
|
|
57
|
+
}
|
|
58
|
+
export interface CliResolution<T = undefined> {
|
|
59
|
+
binaryPath: string;
|
|
60
|
+
directory: string;
|
|
61
|
+
source: CliResolutionSource;
|
|
62
|
+
metadata?: T;
|
|
63
|
+
}
|
|
64
|
+
export interface CliExecutableResolver<T = undefined> {
|
|
65
|
+
resolve(): CliResolution<T> | null;
|
|
66
|
+
diagnostics(): CliResolutionDiagnostics;
|
|
67
|
+
}
|
|
68
|
+
export interface CliResolverCommandOptions {
|
|
69
|
+
encoding: 'utf8';
|
|
70
|
+
timeout: number;
|
|
71
|
+
stdio: ['ignore', 'pipe', 'ignore'];
|
|
72
|
+
killSignal: 'SIGKILL';
|
|
73
|
+
}
|
|
74
|
+
export type CliResolverCommandRunner = (file: string, args: string[], options: CliResolverCommandOptions) => string;
|
|
75
|
+
export interface ProductionCliResolverHostOptions {
|
|
76
|
+
processPath?: string;
|
|
77
|
+
shellPath?: string;
|
|
78
|
+
shellArgs?: string[];
|
|
79
|
+
runCommand?: CliResolverCommandRunner;
|
|
80
|
+
isExecutableFile?: (path: string) => boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Test-only escape hatch: keep the REAL IO primitives even under vitest.
|
|
83
|
+
* For tests that exercise `isExecutableRegularFile` against their own temp
|
|
84
|
+
* fixtures. Such a test must still inject `runCommand` if it can reach the
|
|
85
|
+
* login-shell step, or it would spawn a real interactive shell.
|
|
86
|
+
*/
|
|
87
|
+
allowRealIoUnderVitest?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export declare function createProductionCliResolverHost(options?: ProductionCliResolverHostOptions): CliResolverHost;
|
|
90
|
+
export declare function createCliExecutableResolver<T = undefined>(options: {
|
|
91
|
+
binary: string;
|
|
92
|
+
searchDirs: string[];
|
|
93
|
+
validateCandidate?: (path: string) => CandidateValidation<T>;
|
|
94
|
+
/** Clock injection for tests driving the failure backoff. Defaults to `Date.now`. */
|
|
95
|
+
now?: () => number;
|
|
96
|
+
}, host?: CliResolverHost): CliExecutableResolver<T>;
|
|
97
|
+
export declare function formatCliNotFoundMessage(base: string, diagnostics: CliResolutionDiagnostics): string;
|
|
98
|
+
//# sourceMappingURL=cli-executable-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-executable-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/cli-executable-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAwBH;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,kBAAkB,GAAG,aAAa,CAAC;AAEtF,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACjD,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,CAAC;CACd;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,SAAS;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,SAAS;IAClD,OAAO,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACnC,WAAW,IAAI,wBAAwB,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpC,UAAU,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,yBAAyB,KAAK,MAAM,CAAC;AAEpH,MAAM,WAAW,gCAAgC;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAiCD,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,gCAAqC,GAAG,eAAe,CAiD/G;AAED,wBAAgB,2BAA2B,CAAC,CAAC,GAAG,SAAS,EACvD,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAC7D,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,EACD,IAAI,GAAE,eAAmD,GACxD,qBAAqB,CAAC,CAAC,CAAC,CA6D1B;AAgBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,wBAAwB,GAAG,MAAM,CAQpG"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared CLI executable resolution for the per-CLI resolvers.
|
|
3
|
+
*
|
|
4
|
+
* One lookup chain behind all six *-cli-resolver modules (claude, opencode,
|
|
5
|
+
* codex, gemini, antigravity, pi): the server process PATH first, then the
|
|
6
|
+
* CLI's common install directories in order, then — last, because it is the
|
|
7
|
+
* only step that spawns anything — an interactive login shell, which is what
|
|
8
|
+
* finds nvm/Homebrew/user-npm installs when Codeman runs as a systemd/launchd
|
|
9
|
+
* service with a minimal PATH (launchd hands a job `/usr/bin:/bin:/usr/sbin:/sbin`).
|
|
10
|
+
*
|
|
11
|
+
* Caching is asymmetric, same shape as `resolveClaudeCliVersion` in
|
|
12
|
+
* claude-cli-resolver.ts: a successful resolution is cached for the process
|
|
13
|
+
* lifetime, a MISS is negative-cached and retried only after a doubling backoff
|
|
14
|
+
* (`cliResolveRetryDelayMs`). The callers are request-facing (the per-CLI
|
|
15
|
+
* status endpoints in system-routes.ts, the availability gates in
|
|
16
|
+
* session-routes.ts, and tmux-manager's spawn path), and the login-shell probe
|
|
17
|
+
* is a SYNCHRONOUS spawn bounded by `EXEC_TIMEOUT_MS` — without the negative
|
|
18
|
+
* cache, a missing CLI re-ran the whole chain and stalled the event loop for up
|
|
19
|
+
* to 5s on every request, forever.
|
|
20
|
+
*
|
|
21
|
+
* Test hermeticity: under vitest (`process.env.VITEST`) the production host
|
|
22
|
+
* short-circuits — IO primitives that were not injected become inert stubs, so
|
|
23
|
+
* a suite can never scan the machine's PATH or spawn login shells (the same
|
|
24
|
+
* rule as `IS_TEST_MODE` in tmux-manager and the VITEST gate in
|
|
25
|
+
* `getClaudeCliVersion`). Tests opt back in through the injection hooks
|
|
26
|
+
* (`runCommand`/`isExecutableFile` fakes do no real IO by construction) or, for
|
|
27
|
+
* fixtures that need the real filesystem predicate against their own temp
|
|
28
|
+
* files, via `allowRealIoUnderVitest`.
|
|
29
|
+
*
|
|
30
|
+
* @module utils/cli-executable-resolver
|
|
31
|
+
*/
|
|
32
|
+
import { execFileSync } from 'node:child_process';
|
|
33
|
+
import { accessSync, constants, statSync } from 'node:fs';
|
|
34
|
+
import { basename, delimiter, dirname, isAbsolute, join } from 'node:path';
|
|
35
|
+
import { EXEC_TIMEOUT_MS } from '../config/exec-timeout.js';
|
|
36
|
+
import { loginShellArgs, resolveLocalShell } from './shell-resolver.js';
|
|
37
|
+
const SAFE_BINARY_NAME = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
38
|
+
const LOGIN_SHELL_BEGIN_MARKER = '__CODEMAN_CLI_RESOLVE_BEGIN__';
|
|
39
|
+
const LOGIN_SHELL_END_MARKER = '__CODEMAN_CLI_RESOLVE_END__';
|
|
40
|
+
/** Maximum rendered length of each bounded diagnostic field, excluding its label. */
|
|
41
|
+
const DIAGNOSTIC_FIELD_MAX_LENGTH = 1024;
|
|
42
|
+
/** First retry window after a full-chain resolution miss. */
|
|
43
|
+
const RESOLVE_RETRY_BASE_MS = 60_000;
|
|
44
|
+
/**
|
|
45
|
+
* Ceiling for the doubling backoff. Deliberately shorter than the 15min cap on
|
|
46
|
+
* the claude version probe: that one is cosmetic, while this gates the Run
|
|
47
|
+
* flow, and "installing a CLI while the server is running is picked up without
|
|
48
|
+
* a restart" should stay true within minutes.
|
|
49
|
+
*/
|
|
50
|
+
const RESOLVE_RETRY_MAX_MS = 5 * 60_000;
|
|
51
|
+
/**
|
|
52
|
+
* How long to wait before re-running the resolution chain after `failures`
|
|
53
|
+
* consecutive misses: 1min, 2min, 4min… capped at 5min. Mirrors
|
|
54
|
+
* `claudeVersionRetryDelayMs` in claude-cli-resolver.ts. Exported for tests.
|
|
55
|
+
*/
|
|
56
|
+
export function cliResolveRetryDelayMs(failures) {
|
|
57
|
+
if (failures <= 0)
|
|
58
|
+
return 0;
|
|
59
|
+
return Math.min(RESOLVE_RETRY_BASE_MS * 2 ** (failures - 1), RESOLVE_RETRY_MAX_MS);
|
|
60
|
+
}
|
|
61
|
+
function isExecutableRegularFile(path) {
|
|
62
|
+
try {
|
|
63
|
+
if (!statSync(path).isFile())
|
|
64
|
+
return false;
|
|
65
|
+
accessSync(path, constants.X_OK);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseLoginShellResult(output, binary) {
|
|
73
|
+
const lines = output.split(/\r?\n/).map((line) => line.trim());
|
|
74
|
+
const begin = lines.indexOf(LOGIN_SHELL_BEGIN_MARKER);
|
|
75
|
+
if (begin === -1)
|
|
76
|
+
return null;
|
|
77
|
+
const end = lines.indexOf(LOGIN_SHELL_END_MARKER, begin + 1);
|
|
78
|
+
if (end === -1)
|
|
79
|
+
return null;
|
|
80
|
+
for (const candidate of lines.slice(begin + 1, end)) {
|
|
81
|
+
if (isAbsolute(candidate) && basename(candidate) === binary)
|
|
82
|
+
return candidate;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function loginShellCommand(binary) {
|
|
87
|
+
return [
|
|
88
|
+
`printf '%s\\n' '${LOGIN_SHELL_BEGIN_MARKER}'`,
|
|
89
|
+
`command -v -- ${binary}`,
|
|
90
|
+
`printf '%s\\n' '${LOGIN_SHELL_END_MARKER}'`,
|
|
91
|
+
].join('; ');
|
|
92
|
+
}
|
|
93
|
+
export function createProductionCliResolverHost(options = {}) {
|
|
94
|
+
const shellPath = options.shellPath ?? resolveLocalShell();
|
|
95
|
+
const shellArgs = options.shellArgs ?? loginShellArgs(shellPath).trim().split(/\s+/).filter(Boolean);
|
|
96
|
+
const processPath = options.processPath ?? process.env.PATH ?? '';
|
|
97
|
+
// Hermeticity gate (see @fileoverview): under vitest, any IO primitive the
|
|
98
|
+
// caller did not inject is replaced by an inert stub. The suites must never
|
|
99
|
+
// depend on — or execute — whatever happens to be installed on the machine
|
|
100
|
+
// running them, and route tests hitting the per-CLI status endpoints would
|
|
101
|
+
// otherwise scan the real PATH and spawn real login shells on CI.
|
|
102
|
+
const inert = Boolean(process.env.VITEST) && options.allowRealIoUnderVitest !== true;
|
|
103
|
+
const isExecutableFile = options.isExecutableFile ?? (inert ? () => false : isExecutableRegularFile);
|
|
104
|
+
const runCommand = options.runCommand ?? (inert ? () => '' : (file, args, commandOptions) => execFileSync(file, args, commandOptions));
|
|
105
|
+
const run = (file, args) => {
|
|
106
|
+
try {
|
|
107
|
+
return runCommand(file, args, {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
110
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
111
|
+
// SIGKILL is load-bearing: execFileSync's `timeout` only SENDS the kill
|
|
112
|
+
// signal and then keeps waiting for the child to exit. Interactive bash
|
|
113
|
+
// ignores SIGTERM (the default), so a login shell stuck in a blocking
|
|
114
|
+
// .bash_profile would survive the timeout and block the server forever.
|
|
115
|
+
killSignal: 'SIGKILL',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
processPath,
|
|
124
|
+
shellPath,
|
|
125
|
+
shellArgs: [...shellArgs],
|
|
126
|
+
findOnProcessPath: (binary) => {
|
|
127
|
+
if (!SAFE_BINARY_NAME.test(binary))
|
|
128
|
+
return null;
|
|
129
|
+
for (const directory of processPath.split(delimiter).filter(Boolean)) {
|
|
130
|
+
const candidate = join(directory, binary);
|
|
131
|
+
if (isAbsolute(candidate) && isExecutableFile(candidate))
|
|
132
|
+
return candidate;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
},
|
|
136
|
+
findInLoginShell: (binary) => {
|
|
137
|
+
if (!SAFE_BINARY_NAME.test(binary))
|
|
138
|
+
return null;
|
|
139
|
+
const candidate = parseLoginShellResult(run(shellPath, [...shellArgs, '-c', loginShellCommand(binary)]), binary);
|
|
140
|
+
return candidate && isExecutableFile(candidate) ? candidate : null;
|
|
141
|
+
},
|
|
142
|
+
exists: isExecutableFile,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function createCliExecutableResolver(options, host = createProductionCliResolverHost()) {
|
|
146
|
+
if (!SAFE_BINARY_NAME.test(options.binary)) {
|
|
147
|
+
throw new Error(`Unsafe CLI binary name: ${options.binary}`);
|
|
148
|
+
}
|
|
149
|
+
const now = options.now ?? Date.now;
|
|
150
|
+
/** Successful resolution, cached for the process lifetime. */
|
|
151
|
+
let cached = null;
|
|
152
|
+
/** Consecutive full-chain misses (drives the retry backoff). */
|
|
153
|
+
let failures = 0;
|
|
154
|
+
/** Timestamp of the most recent miss. */
|
|
155
|
+
let lastFailureAt = 0;
|
|
156
|
+
const accept = (path, source) => {
|
|
157
|
+
if (!path || !isAbsolute(path) || !host.exists(path))
|
|
158
|
+
return null;
|
|
159
|
+
const validation = options.validateCandidate?.(path) ?? { accepted: true };
|
|
160
|
+
if (!validation.accepted)
|
|
161
|
+
return null;
|
|
162
|
+
return {
|
|
163
|
+
binaryPath: path,
|
|
164
|
+
directory: dirname(path),
|
|
165
|
+
source,
|
|
166
|
+
metadata: validation.metadata,
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
resolve() {
|
|
171
|
+
if (cached)
|
|
172
|
+
return cached;
|
|
173
|
+
// Negative cache: a miss is remembered and the chain — whose login-shell
|
|
174
|
+
// tail is a synchronous 5s-bounded spawn — is not re-run until the
|
|
175
|
+
// backoff elapses. Without this, every status poll and Run click against
|
|
176
|
+
// a missing CLI froze the event loop for the full probe, forever.
|
|
177
|
+
if (failures > 0 && now() - lastFailureAt < cliResolveRetryDelayMs(failures))
|
|
178
|
+
return null;
|
|
179
|
+
cached = accept(host.findOnProcessPath(options.binary), 'process-path');
|
|
180
|
+
if (!cached) {
|
|
181
|
+
for (const dir of options.searchDirs) {
|
|
182
|
+
cached = accept(join(dir, options.binary), 'common-directory');
|
|
183
|
+
if (cached)
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!cached) {
|
|
188
|
+
cached = accept(host.findInLoginShell(options.binary), 'login-shell');
|
|
189
|
+
}
|
|
190
|
+
if (cached) {
|
|
191
|
+
failures = 0;
|
|
192
|
+
lastFailureAt = 0;
|
|
193
|
+
return cached;
|
|
194
|
+
}
|
|
195
|
+
failures += 1;
|
|
196
|
+
lastFailureAt = now();
|
|
197
|
+
return null;
|
|
198
|
+
},
|
|
199
|
+
diagnostics: () => ({
|
|
200
|
+
binary: options.binary,
|
|
201
|
+
processPath: host.processPath,
|
|
202
|
+
shellPath: host.shellPath,
|
|
203
|
+
shellArgs: [...host.shellArgs],
|
|
204
|
+
searchDirs: [...options.searchDirs],
|
|
205
|
+
}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function sanitizeDiagnosticField(value, emptyMarker) {
|
|
209
|
+
const flattened = Array.from(value, (character) => {
|
|
210
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
211
|
+
const isControl = codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
212
|
+
return isControl || codePoint === 0x2028 || codePoint === 0x2029 ? ' ' : character;
|
|
213
|
+
})
|
|
214
|
+
.join('')
|
|
215
|
+
.replace(/ +/g, ' ')
|
|
216
|
+
.trim();
|
|
217
|
+
if (!flattened)
|
|
218
|
+
return emptyMarker;
|
|
219
|
+
if (flattened.length <= DIAGNOSTIC_FIELD_MAX_LENGTH)
|
|
220
|
+
return flattened;
|
|
221
|
+
return `${flattened.slice(0, DIAGNOSTIC_FIELD_MAX_LENGTH - 1)}…`;
|
|
222
|
+
}
|
|
223
|
+
export function formatCliNotFoundMessage(base, diagnostics) {
|
|
224
|
+
const processPath = sanitizeDiagnosticField(diagnostics.processPath, '(empty)');
|
|
225
|
+
const shell = sanitizeDiagnosticField([diagnostics.shellPath, ...diagnostics.shellArgs].filter(Boolean).join(' '), '(none)');
|
|
226
|
+
const dirs = sanitizeDiagnosticField(diagnostics.searchDirs.join(', '), '(none)');
|
|
227
|
+
return `${base}\nServer PATH: ${processPath}\nLogin shell: ${shell}\nChecked directories: ${dirs}`;
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=cli-executable-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-executable-resolver.js","sourceRoot":"","sources":["../../src/utils/cli-executable-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,4 +18,5 @@ export declare function resolveCodexDir(): string | null;
|
|
|
18
18
|
* Check if Codex CLI is available on the system.
|
|
19
19
|
*/
|
|
20
20
|
export declare function isCodexAvailable(): boolean;
|
|
21
|
+
export declare function getCodexNotFoundMessage(): string;
|
|
21
22
|
//# sourceMappingURL=codex-cli-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-cli-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/codex-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"codex-cli-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/codex-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmBH;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}
|
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module utils/codex-cli-resolver
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import { existsSync } from 'node:fs';
|
|
11
|
-
import { dirname, join } from 'node:path';
|
|
9
|
+
import { join } from 'node:path';
|
|
12
10
|
import { homedir } from 'node:os';
|
|
13
|
-
import {
|
|
11
|
+
import { createCliExecutableResolver, formatCliNotFoundMessage } from './cli-executable-resolver.js';
|
|
14
12
|
/** Common directories where the Codex CLI binary may be installed */
|
|
15
13
|
const CODEX_SEARCH_DIRS = [
|
|
16
14
|
join(homedir(), '.codex', 'bin'), // Default install location
|
|
@@ -20,8 +18,8 @@ const CODEX_SEARCH_DIRS = [
|
|
|
20
18
|
join(homedir(), '.npm-global', 'bin'), // npm global
|
|
21
19
|
join(homedir(), 'bin'), // User bin
|
|
22
20
|
];
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const codexResolver = createCliExecutableResolver({ binary: 'codex', searchDirs: CODEX_SEARCH_DIRS });
|
|
22
|
+
const CODEX_NOT_FOUND = 'Codex CLI not found. Install with: npm install -g @openai/codex';
|
|
25
23
|
/**
|
|
26
24
|
* Finds the directory containing the `codex` binary.
|
|
27
25
|
* Checks `which codex` first, then falls back to common install locations.
|
|
@@ -30,30 +28,7 @@ let _codexDir = null;
|
|
|
30
28
|
* @returns Directory path, or null if not found
|
|
31
29
|
*/
|
|
32
30
|
export function resolveCodexDir() {
|
|
33
|
-
|
|
34
|
-
return _codexDir || null;
|
|
35
|
-
// Try `which` first (respects current PATH)
|
|
36
|
-
try {
|
|
37
|
-
const result = execSync('which codex', {
|
|
38
|
-
encoding: 'utf-8',
|
|
39
|
-
timeout: EXEC_TIMEOUT_MS,
|
|
40
|
-
}).trim();
|
|
41
|
-
if (result && existsSync(result)) {
|
|
42
|
-
_codexDir = dirname(result);
|
|
43
|
-
return _codexDir;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// Codex not in PATH, will check common locations
|
|
48
|
-
}
|
|
49
|
-
for (const dir of CODEX_SEARCH_DIRS) {
|
|
50
|
-
if (existsSync(join(dir, 'codex'))) {
|
|
51
|
-
_codexDir = dir;
|
|
52
|
-
return _codexDir;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
_codexDir = ''; // mark as searched, not found
|
|
56
|
-
return null;
|
|
31
|
+
return codexResolver.resolve()?.directory ?? null;
|
|
57
32
|
}
|
|
58
33
|
/**
|
|
59
34
|
* Check if Codex CLI is available on the system.
|
|
@@ -61,4 +36,7 @@ export function resolveCodexDir() {
|
|
|
61
36
|
export function isCodexAvailable() {
|
|
62
37
|
return resolveCodexDir() !== null;
|
|
63
38
|
}
|
|
39
|
+
export function getCodexNotFoundMessage() {
|
|
40
|
+
return formatCliNotFoundMessage(CODEX_NOT_FOUND, codexResolver.diagnostics());
|
|
41
|
+
}
|
|
64
42
|
//# sourceMappingURL=codex-cli-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/codex-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"codex-cli-resolver.js","sourceRoot":"","sources":["../../src/utils/codex-cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAErG,qEAAqE;AACrE,MAAM,iBAAiB,GAAG;IACxB,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,2BAA2B;IAC7D,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,+BAA+B;IACjE,gBAAgB,EAAE,oBAAoB;IACtC,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa;IAC7C,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,aAAa;IACpD,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,WAAW;CACpC,CAAC;AAEF,MAAM,aAAa,GAAG,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACtG,MAAM,eAAe,GAAG,iEAAiE,CAAC;AAE1F;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,aAAa,CAAC,OAAO,EAAE,EAAE,SAAS,IAAI,IAAI,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,EAAE,KAAK,IAAI,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,wBAAwB,CAAC,eAAe,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -17,4 +17,5 @@ export declare function resolveGeminiDir(): string | null;
|
|
|
17
17
|
* Check if Gemini CLI is available on the system.
|
|
18
18
|
*/
|
|
19
19
|
export declare function isGeminiAvailable(): boolean;
|
|
20
|
+
export declare function getGeminiNotFoundMessage(): string;
|
|
20
21
|
//# sourceMappingURL=gemini-cli-resolver.d.ts.map
|