@mycompbox/compbox-cli 1.13.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/.tsbuildinfo +1 -0
- package/commands/configure.d.ts +24 -0
- package/commands/configure.d.ts.map +1 -0
- package/commands/configure.js +170 -0
- package/commands/configure.js.map +1 -0
- package/commands/doctor.d.ts +136 -0
- package/commands/doctor.d.ts.map +1 -0
- package/commands/doctor.js +606 -0
- package/commands/doctor.js.map +1 -0
- package/commands/install.d.ts +3 -0
- package/commands/install.d.ts.map +1 -0
- package/commands/install.js +88 -0
- package/commands/install.js.map +1 -0
- package/commands/list.d.ts +3 -0
- package/commands/list.d.ts.map +1 -0
- package/commands/list.js +141 -0
- package/commands/list.js.map +1 -0
- package/commands/login.d.ts +15 -0
- package/commands/login.d.ts.map +1 -0
- package/commands/login.js +102 -0
- package/commands/login.js.map +1 -0
- package/commands/publish.d.ts +3 -0
- package/commands/publish.d.ts.map +1 -0
- package/commands/publish.js +102 -0
- package/commands/publish.js.map +1 -0
- package/commands/run.d.ts +76 -0
- package/commands/run.d.ts.map +1 -0
- package/commands/run.js +417 -0
- package/commands/run.js.map +1 -0
- package/commands/service.d.ts +29 -0
- package/commands/service.d.ts.map +1 -0
- package/commands/service.js +142 -0
- package/commands/service.js.map +1 -0
- package/commands/update.d.ts +3 -0
- package/commands/update.d.ts.map +1 -0
- package/commands/update.js +141 -0
- package/commands/update.js.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +8508 -0
- package/index.js.map +1 -0
- package/package.json +30 -0
- package/utils/agent-invoker.d.ts +393 -0
- package/utils/agent-invoker.d.ts.map +1 -0
- package/utils/agent-invoker.js +1632 -0
- package/utils/agent-invoker.js.map +1 -0
- package/utils/api-client.d.ts +54 -0
- package/utils/api-client.d.ts.map +1 -0
- package/utils/api-client.js +214 -0
- package/utils/api-client.js.map +1 -0
- package/utils/auth-client.d.ts +61 -0
- package/utils/auth-client.d.ts.map +1 -0
- package/utils/auth-client.js +135 -0
- package/utils/auth-client.js.map +1 -0
- package/utils/claude-runtime.d.ts +116 -0
- package/utils/claude-runtime.d.ts.map +1 -0
- package/utils/claude-runtime.js +291 -0
- package/utils/claude-runtime.js.map +1 -0
- package/utils/config-store.d.ts +128 -0
- package/utils/config-store.d.ts.map +1 -0
- package/utils/config-store.js +137 -0
- package/utils/config-store.js.map +1 -0
- package/utils/file-manager.d.ts +32 -0
- package/utils/file-manager.d.ts.map +1 -0
- package/utils/file-manager.js +208 -0
- package/utils/file-manager.js.map +1 -0
- package/utils/keychain.d.ts +46 -0
- package/utils/keychain.d.ts.map +1 -0
- package/utils/keychain.js +167 -0
- package/utils/keychain.js.map +1 -0
- package/utils/loopback.d.ts +45 -0
- package/utils/loopback.d.ts.map +1 -0
- package/utils/loopback.js +95 -0
- package/utils/loopback.js.map +1 -0
- package/utils/mailbox-client.d.ts +98 -0
- package/utils/mailbox-client.d.ts.map +1 -0
- package/utils/mailbox-client.js +178 -0
- package/utils/mailbox-client.js.map +1 -0
- package/utils/mcp-config-writer.d.ts +133 -0
- package/utils/mcp-config-writer.d.ts.map +1 -0
- package/utils/mcp-config-writer.js +184 -0
- package/utils/mcp-config-writer.js.map +1 -0
- package/utils/pack-bundler.d.ts +30 -0
- package/utils/pack-bundler.d.ts.map +1 -0
- package/utils/pack-bundler.js +141 -0
- package/utils/pack-bundler.js.map +1 -0
- package/utils/prompt.d.ts +31 -0
- package/utils/prompt.d.ts.map +1 -0
- package/utils/prompt.js +86 -0
- package/utils/prompt.js.map +1 -0
- package/utils/table-formatter.d.ts +42 -0
- package/utils/table-formatter.d.ts.map +1 -0
- package/utils/table-formatter.js +125 -0
- package/utils/table-formatter.js.map +1 -0
- package/utils/version-resolver.d.ts +31 -0
- package/utils/version-resolver.d.ts.map +1 -0
- package/utils/version-resolver.js +75 -0
- package/utils/version-resolver.js.map +1 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-runtime (ENG-135) — shared resolver + probe for the local `claude`
|
|
3
|
+
* CLI binary.
|
|
4
|
+
*
|
|
5
|
+
* Root bug this fixes: the `compbox run` daemon spawns `claude` by bare name
|
|
6
|
+
* with the daemon's inherited PATH. When the daemon is launched by launchd /
|
|
7
|
+
* a GUI / a Homebrew service, that PATH does NOT include `~/.local/bin` (the
|
|
8
|
+
* default Claude Code install location), so web-initiated subscription-mode
|
|
9
|
+
* interviews fail with "claude CLI not found on PATH" even though `claude` is
|
|
10
|
+
* installed and works fine in the user's interactive shell.
|
|
11
|
+
*
|
|
12
|
+
* The fix, in one place, reused by every spawn site:
|
|
13
|
+
* 1. Recover the *login shell* PATH (what the user actually has), not just
|
|
14
|
+
* the daemon's inherited PATH.
|
|
15
|
+
* 2. Search that PATH plus a set of well-known install directories for an
|
|
16
|
+
* executable `claude`, and return its ABSOLUTE path.
|
|
17
|
+
* 3. Probe that the resolved binary is actually invocable, with a bounded
|
|
18
|
+
* timeout that can never hang (mirrors doctor.ts `probeLaunchTarget`).
|
|
19
|
+
*
|
|
20
|
+
* Everything here is pure-ish and dependency-injectable so it is unit-testable
|
|
21
|
+
* without a real `claude` install or spawning real processes.
|
|
22
|
+
*/
|
|
23
|
+
import { spawn } from 'node:child_process';
|
|
24
|
+
/**
|
|
25
|
+
* Well-known absolute directories where `claude` is commonly installed,
|
|
26
|
+
* searched IN ADDITION to (and after) the login-shell PATH. Exported so the
|
|
27
|
+
* "not found" error message and the unit tests can reference the exact set of
|
|
28
|
+
* locations that were searched.
|
|
29
|
+
*
|
|
30
|
+
* Order: the canonical Claude Code install dir first, then the two Homebrew
|
|
31
|
+
* bin dirs, then npm/pnpm global bin dirs derived from the environment.
|
|
32
|
+
*/
|
|
33
|
+
export declare const WELL_KNOWN_DIRS: readonly string[];
|
|
34
|
+
/** Signature of the exec used to recover the login-shell PATH (injectable for tests). */
|
|
35
|
+
export type ExecFileSyncFn = (file: string, args: string[], options: {
|
|
36
|
+
timeout: number;
|
|
37
|
+
encoding: 'utf-8';
|
|
38
|
+
}) => string;
|
|
39
|
+
/** Test-only: clear the login-shell PATH cache so caching behavior can be exercised. */
|
|
40
|
+
export declare function resetLoginShellPathCacheForTests(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Returns the PATH as seen by the user's LOGIN shell (`$SHELL -lic 'echo
|
|
43
|
+
* $PATH'`), which is what actually contains `~/.local/bin` and Homebrew dirs
|
|
44
|
+
* for most installs — unlike the possibly-stripped PATH a launchd/GUI-spawned
|
|
45
|
+
* daemon inherits. Cached after the first successful (or failed) call.
|
|
46
|
+
*
|
|
47
|
+
* Falls back to `process.env.PATH ?? ''` on ANY failure: missing `$SHELL`,
|
|
48
|
+
* a non-zero exit, a spawn error, or a timeout. Never throws.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getLoginShellPath(exec?: ExecFileSyncFn): string;
|
|
51
|
+
/**
|
|
52
|
+
* The ordered, de-duplicated list of directories searched for `claude`:
|
|
53
|
+
* every entry on the login-shell PATH first, then WELL_KNOWN_DIRS. Exposed so
|
|
54
|
+
* the child-env PATH augmentation and tests can reuse the exact search set.
|
|
55
|
+
*/
|
|
56
|
+
export declare function claudeSearchDirs(getPath?: () => string): string[];
|
|
57
|
+
/** Injectable dependencies for resolveClaudeBinary (all default to real impls). */
|
|
58
|
+
export interface ResolveClaudeDeps {
|
|
59
|
+
/** Source of the search directories (defaults to login-shell PATH + WELL_KNOWN_DIRS). */
|
|
60
|
+
dirs?: readonly string[];
|
|
61
|
+
/** Predicate for "this absolute path is an executable file". */
|
|
62
|
+
isExecutableFile?: (p: string) => boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolves `claude` to an ABSOLUTE path by scanning the login-shell PATH and
|
|
66
|
+
* the well-known install dirs, returning the first executable match. Returns
|
|
67
|
+
* `null` when `claude` is not found anywhere. Never throws.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveClaudeBinary(deps?: ResolveClaudeDeps): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Builds the PATH string to hand a spawned `claude` child: the search dirs
|
|
72
|
+
* (login-shell PATH + well-known dirs) merged ahead of whatever PATH the
|
|
73
|
+
* child would otherwise inherit, de-duplicated. This makes even a bare-name
|
|
74
|
+
* spawn resolvable and lets `claude` itself find any helper binaries it needs.
|
|
75
|
+
*/
|
|
76
|
+
export declare function claudeChildEnvPath(currentPath?: string | undefined): string;
|
|
77
|
+
/**
|
|
78
|
+
* Actionable "claude not found" message: names every location that was
|
|
79
|
+
* searched plus the install link. Used both by the runtime probe and by the
|
|
80
|
+
* subscription-mode spawn's ENOENT handler so the user knows exactly where to
|
|
81
|
+
* put `claude` (or how to install it).
|
|
82
|
+
*/
|
|
83
|
+
export declare function claudeNotFoundMessage(): string;
|
|
84
|
+
/** Bounded wall-clock budget for `claude --version`; the probe can never hang. */
|
|
85
|
+
export declare const AGENT_RUNTIME_PROBE_TIMEOUT_MS = 5000;
|
|
86
|
+
export interface AgentRuntimeProbe {
|
|
87
|
+
/**
|
|
88
|
+
* - `not-found`: `claude` could not be resolved to any path.
|
|
89
|
+
* - `ok`: the resolved binary ran `--version` and exited 0.
|
|
90
|
+
* - `error`: the binary was found but failed to run (crash, non-zero exit,
|
|
91
|
+
* spawn error, or timeout).
|
|
92
|
+
*/
|
|
93
|
+
status: 'ok' | 'error' | 'not-found';
|
|
94
|
+
/** Human-readable detail: version string on ok, the reason otherwise. */
|
|
95
|
+
detail: string;
|
|
96
|
+
/** ISO-8601 timestamp of when the probe settled. */
|
|
97
|
+
checkedAt: string;
|
|
98
|
+
}
|
|
99
|
+
/** Minimal shape of the spawn function so tests can inject a fake child. */
|
|
100
|
+
export type SpawnFn = typeof spawn;
|
|
101
|
+
/** Injectable dependencies for probeAgentRuntime (all default to real impls). */
|
|
102
|
+
export interface ProbeAgentRuntimeDeps {
|
|
103
|
+
timeoutMs?: number;
|
|
104
|
+
/** Resolver override (defaults to resolveClaudeBinary). */
|
|
105
|
+
resolve?: () => string | null;
|
|
106
|
+
/** Spawn override (defaults to node:child_process spawn). */
|
|
107
|
+
spawnFn?: SpawnFn;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Probes whether the local `claude` runtime is invocable. Resolves the binary
|
|
111
|
+
* first; if absent, returns `not-found` without spawning. Otherwise spawns
|
|
112
|
+
* `<abs> --version` with a bounded timeout, ALWAYS killing the child on
|
|
113
|
+
* settle (mirrors doctor.ts `probeLaunchTarget`). Never rejects.
|
|
114
|
+
*/
|
|
115
|
+
export declare function probeAgentRuntime(deps?: ProbeAgentRuntimeDeps): Promise<AgentRuntimeProbe>;
|
|
116
|
+
//# sourceMappingURL=claude-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runtime.d.ts","sourceRoot":"","sources":["../../src/utils/claude-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAM9D;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAyB,CAAC;AA0BvE,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,KAC5C,MAAM,CAAC;AAaZ,wFAAwF;AACxF,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,cAAoC,GAAG,MAAM,CA6BpF;AAcD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,MAAM,MAA0B,GAAG,MAAM,EAAE,CAcpF;AAeD,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,iBAAsB,GAAG,MAAM,GAAG,IAAI,CAe/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,GAAE,MAAM,GAAG,SAA4B,GAAG,MAAM,CAc7F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAMD,kFAAkF;AAClF,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,WAAW,CAAC;IACrC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC;AAEnC,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8E9F"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-runtime (ENG-135) — shared resolver + probe for the local `claude`
|
|
3
|
+
* CLI binary.
|
|
4
|
+
*
|
|
5
|
+
* Root bug this fixes: the `compbox run` daemon spawns `claude` by bare name
|
|
6
|
+
* with the daemon's inherited PATH. When the daemon is launched by launchd /
|
|
7
|
+
* a GUI / a Homebrew service, that PATH does NOT include `~/.local/bin` (the
|
|
8
|
+
* default Claude Code install location), so web-initiated subscription-mode
|
|
9
|
+
* interviews fail with "claude CLI not found on PATH" even though `claude` is
|
|
10
|
+
* installed and works fine in the user's interactive shell.
|
|
11
|
+
*
|
|
12
|
+
* The fix, in one place, reused by every spawn site:
|
|
13
|
+
* 1. Recover the *login shell* PATH (what the user actually has), not just
|
|
14
|
+
* the daemon's inherited PATH.
|
|
15
|
+
* 2. Search that PATH plus a set of well-known install directories for an
|
|
16
|
+
* executable `claude`, and return its ABSOLUTE path.
|
|
17
|
+
* 3. Probe that the resolved binary is actually invocable, with a bounded
|
|
18
|
+
* timeout that can never hang (mirrors doctor.ts `probeLaunchTarget`).
|
|
19
|
+
*
|
|
20
|
+
* Everything here is pure-ish and dependency-injectable so it is unit-testable
|
|
21
|
+
* without a real `claude` install or spawning real processes.
|
|
22
|
+
*/
|
|
23
|
+
import { spawn } from 'node:child_process';
|
|
24
|
+
import { execFileSync } from 'node:child_process';
|
|
25
|
+
import { accessSync, constants as fsConstants, statSync } from 'node:fs';
|
|
26
|
+
import { homedir } from 'node:os';
|
|
27
|
+
import { delimiter, join } from 'node:path';
|
|
28
|
+
/**
|
|
29
|
+
* Well-known absolute directories where `claude` is commonly installed,
|
|
30
|
+
* searched IN ADDITION to (and after) the login-shell PATH. Exported so the
|
|
31
|
+
* "not found" error message and the unit tests can reference the exact set of
|
|
32
|
+
* locations that were searched.
|
|
33
|
+
*
|
|
34
|
+
* Order: the canonical Claude Code install dir first, then the two Homebrew
|
|
35
|
+
* bin dirs, then npm/pnpm global bin dirs derived from the environment.
|
|
36
|
+
*/
|
|
37
|
+
export const WELL_KNOWN_DIRS = buildWellKnownDirs();
|
|
38
|
+
function buildWellKnownDirs() {
|
|
39
|
+
const home = homedir();
|
|
40
|
+
const dirs = [
|
|
41
|
+
join(home, '.local', 'bin'), // default Claude Code install location
|
|
42
|
+
'/opt/homebrew/bin', // Homebrew on Apple Silicon
|
|
43
|
+
'/usr/local/bin', // Homebrew on Intel / common Unix prefix
|
|
44
|
+
];
|
|
45
|
+
// npm global bin — respects an explicit prefix, else the conventional dir.
|
|
46
|
+
if (process.env.npm_config_prefix) {
|
|
47
|
+
dirs.push(join(process.env.npm_config_prefix, 'bin'));
|
|
48
|
+
}
|
|
49
|
+
dirs.push(join(home, '.npm-global', 'bin'));
|
|
50
|
+
// pnpm global bin — PNPM_HOME when set, else the per-OS defaults.
|
|
51
|
+
if (process.env.PNPM_HOME) {
|
|
52
|
+
dirs.push(process.env.PNPM_HOME);
|
|
53
|
+
}
|
|
54
|
+
dirs.push(join(home, 'Library', 'pnpm')); // macOS default
|
|
55
|
+
dirs.push(join(home, '.local', 'share', 'pnpm')); // Linux default
|
|
56
|
+
return dirs;
|
|
57
|
+
}
|
|
58
|
+
const LOGIN_SHELL_PATH_TIMEOUT_MS = 2_000;
|
|
59
|
+
/**
|
|
60
|
+
* Module-local cache of the login-shell PATH. Resolved at most once per
|
|
61
|
+
* process: a login shell can take a noticeable fraction of a second to start
|
|
62
|
+
* (it sources the user's full rc/profile), so we pay that cost once. The
|
|
63
|
+
* cached value is an immutable string — this is a memoised pure result, not
|
|
64
|
+
* shared mutable state across agents (see the Sub-Agent Isolation Rule).
|
|
65
|
+
*/
|
|
66
|
+
let cachedLoginShellPath = null;
|
|
67
|
+
/** Test-only: clear the login-shell PATH cache so caching behavior can be exercised. */
|
|
68
|
+
export function resetLoginShellPathCacheForTests() {
|
|
69
|
+
cachedLoginShellPath = null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns the PATH as seen by the user's LOGIN shell (`$SHELL -lic 'echo
|
|
73
|
+
* $PATH'`), which is what actually contains `~/.local/bin` and Homebrew dirs
|
|
74
|
+
* for most installs — unlike the possibly-stripped PATH a launchd/GUI-spawned
|
|
75
|
+
* daemon inherits. Cached after the first successful (or failed) call.
|
|
76
|
+
*
|
|
77
|
+
* Falls back to `process.env.PATH ?? ''` on ANY failure: missing `$SHELL`,
|
|
78
|
+
* a non-zero exit, a spawn error, or a timeout. Never throws.
|
|
79
|
+
*/
|
|
80
|
+
export function getLoginShellPath(exec = defaultExecFileSync) {
|
|
81
|
+
if (cachedLoginShellPath !== null) {
|
|
82
|
+
return cachedLoginShellPath;
|
|
83
|
+
}
|
|
84
|
+
const shell = process.env.SHELL;
|
|
85
|
+
const fallback = process.env.PATH ?? '';
|
|
86
|
+
if (!shell) {
|
|
87
|
+
cachedLoginShellPath = fallback;
|
|
88
|
+
return cachedLoginShellPath;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
// -l = login shell (sources profile), -i = interactive (sources rc),
|
|
92
|
+
// -c = run the command. This mirrors how the user's terminal builds PATH.
|
|
93
|
+
const out = exec(shell, ['-lic', 'echo $PATH'], {
|
|
94
|
+
timeout: LOGIN_SHELL_PATH_TIMEOUT_MS,
|
|
95
|
+
encoding: 'utf-8',
|
|
96
|
+
});
|
|
97
|
+
// The shell may print rc-file noise before our echo; the PATH is the last
|
|
98
|
+
// non-empty line.
|
|
99
|
+
const line = out
|
|
100
|
+
.split('\n')
|
|
101
|
+
.map((l) => l.trim())
|
|
102
|
+
.filter((l) => l.length > 0)
|
|
103
|
+
.pop();
|
|
104
|
+
cachedLoginShellPath = line ?? fallback;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
cachedLoginShellPath = fallback;
|
|
108
|
+
}
|
|
109
|
+
return cachedLoginShellPath;
|
|
110
|
+
}
|
|
111
|
+
function defaultExecFileSync(file, args, options) {
|
|
112
|
+
return execFileSync(file, args, {
|
|
113
|
+
timeout: options.timeout,
|
|
114
|
+
encoding: options.encoding,
|
|
115
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The ordered, de-duplicated list of directories searched for `claude`:
|
|
120
|
+
* every entry on the login-shell PATH first, then WELL_KNOWN_DIRS. Exposed so
|
|
121
|
+
* the child-env PATH augmentation and tests can reuse the exact search set.
|
|
122
|
+
*/
|
|
123
|
+
export function claudeSearchDirs(getPath = getLoginShellPath) {
|
|
124
|
+
const pathDirs = getPath()
|
|
125
|
+
.split(delimiter)
|
|
126
|
+
.map((d) => d.trim())
|
|
127
|
+
.filter((d) => d.length > 0);
|
|
128
|
+
const seen = new Set();
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const dir of [...pathDirs, ...WELL_KNOWN_DIRS]) {
|
|
131
|
+
if (!seen.has(dir)) {
|
|
132
|
+
seen.add(dir);
|
|
133
|
+
out.push(dir);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
/** True when `p` exists, is a regular file, and is executable by this process. */
|
|
139
|
+
function isExecutableFile(p) {
|
|
140
|
+
try {
|
|
141
|
+
if (!statSync(p).isFile()) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
accessSync(p, fsConstants.X_OK);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolves `claude` to an ABSOLUTE path by scanning the login-shell PATH and
|
|
153
|
+
* the well-known install dirs, returning the first executable match. Returns
|
|
154
|
+
* `null` when `claude` is not found anywhere. Never throws.
|
|
155
|
+
*/
|
|
156
|
+
export function resolveClaudeBinary(deps = {}) {
|
|
157
|
+
const dirs = deps.dirs ?? claudeSearchDirs();
|
|
158
|
+
const isExec = deps.isExecutableFile ?? isExecutableFile;
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
for (const dir of dirs) {
|
|
161
|
+
if (!dir || seen.has(dir)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
seen.add(dir);
|
|
165
|
+
const candidate = join(dir, 'claude');
|
|
166
|
+
if (isExec(candidate)) {
|
|
167
|
+
return candidate;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Builds the PATH string to hand a spawned `claude` child: the search dirs
|
|
174
|
+
* (login-shell PATH + well-known dirs) merged ahead of whatever PATH the
|
|
175
|
+
* child would otherwise inherit, de-duplicated. This makes even a bare-name
|
|
176
|
+
* spawn resolvable and lets `claude` itself find any helper binaries it needs.
|
|
177
|
+
*/
|
|
178
|
+
export function claudeChildEnvPath(currentPath = process.env.PATH) {
|
|
179
|
+
const existing = (currentPath ?? '')
|
|
180
|
+
.split(delimiter)
|
|
181
|
+
.map((d) => d.trim())
|
|
182
|
+
.filter((d) => d.length > 0);
|
|
183
|
+
const seen = new Set();
|
|
184
|
+
const merged = [];
|
|
185
|
+
for (const dir of [...claudeSearchDirs(), ...existing]) {
|
|
186
|
+
if (!seen.has(dir)) {
|
|
187
|
+
seen.add(dir);
|
|
188
|
+
merged.push(dir);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return merged.join(delimiter);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Actionable "claude not found" message: names every location that was
|
|
195
|
+
* searched plus the install link. Used both by the runtime probe and by the
|
|
196
|
+
* subscription-mode spawn's ENOENT handler so the user knows exactly where to
|
|
197
|
+
* put `claude` (or how to install it).
|
|
198
|
+
*/
|
|
199
|
+
export function claudeNotFoundMessage() {
|
|
200
|
+
return [
|
|
201
|
+
'The `claude` CLI could not be located. Searched every directory on the login-shell PATH, plus these well-known install locations:',
|
|
202
|
+
...WELL_KNOWN_DIRS.map((d) => ` - ${d}`),
|
|
203
|
+
'Install Claude Code to use subscription mode: https://code.claude.com',
|
|
204
|
+
].join('\n');
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------
|
|
207
|
+
// Runtime probe — is the resolved `claude` actually invocable?
|
|
208
|
+
// ---------------------------------------------------------------------
|
|
209
|
+
/** Bounded wall-clock budget for `claude --version`; the probe can never hang. */
|
|
210
|
+
export const AGENT_RUNTIME_PROBE_TIMEOUT_MS = 5_000;
|
|
211
|
+
/**
|
|
212
|
+
* Probes whether the local `claude` runtime is invocable. Resolves the binary
|
|
213
|
+
* first; if absent, returns `not-found` without spawning. Otherwise spawns
|
|
214
|
+
* `<abs> --version` with a bounded timeout, ALWAYS killing the child on
|
|
215
|
+
* settle (mirrors doctor.ts `probeLaunchTarget`). Never rejects.
|
|
216
|
+
*/
|
|
217
|
+
export function probeAgentRuntime(deps = {}) {
|
|
218
|
+
const timeoutMs = deps.timeoutMs ?? AGENT_RUNTIME_PROBE_TIMEOUT_MS;
|
|
219
|
+
const resolveBin = deps.resolve ?? (() => resolveClaudeBinary());
|
|
220
|
+
const spawnFn = deps.spawnFn ?? spawn;
|
|
221
|
+
const abs = resolveBin();
|
|
222
|
+
if (!abs) {
|
|
223
|
+
return Promise.resolve({
|
|
224
|
+
status: 'not-found',
|
|
225
|
+
detail: claudeNotFoundMessage(),
|
|
226
|
+
checkedAt: new Date().toISOString(),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return new Promise((resolve) => {
|
|
230
|
+
let settled = false;
|
|
231
|
+
let child;
|
|
232
|
+
let stdout = '';
|
|
233
|
+
let stderr = '';
|
|
234
|
+
const settle = (result) => {
|
|
235
|
+
if (settled) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
settled = true;
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
try {
|
|
241
|
+
child.kill('SIGKILL');
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// Child already gone — nothing to clean up.
|
|
245
|
+
}
|
|
246
|
+
resolve({ ...result, checkedAt: new Date().toISOString() });
|
|
247
|
+
};
|
|
248
|
+
// `settle` closes over `timer`; it can only fire after the handlers below
|
|
249
|
+
// are attached, by which point `timer` is initialized.
|
|
250
|
+
const timer = setTimeout(() => {
|
|
251
|
+
settle({
|
|
252
|
+
status: 'error',
|
|
253
|
+
detail: `\`claude --version\` timed out after ${String(timeoutMs / 1000)}s`,
|
|
254
|
+
});
|
|
255
|
+
}, timeoutMs);
|
|
256
|
+
try {
|
|
257
|
+
child = spawnFn(abs, ['--version'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
resolve({
|
|
262
|
+
status: 'error',
|
|
263
|
+
detail: error instanceof Error ? error.message : 'failed to spawn claude',
|
|
264
|
+
checkedAt: new Date().toISOString(),
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
child.stdout?.on('data', (chunk) => {
|
|
269
|
+
stdout += chunk.toString('utf-8');
|
|
270
|
+
});
|
|
271
|
+
child.stderr?.on('data', (chunk) => {
|
|
272
|
+
stderr += chunk.toString('utf-8');
|
|
273
|
+
});
|
|
274
|
+
child.on('error', (error) => {
|
|
275
|
+
settle({ status: 'error', detail: error.message });
|
|
276
|
+
});
|
|
277
|
+
child.on('exit', (code) => {
|
|
278
|
+
if (code === 0) {
|
|
279
|
+
settle({ status: 'ok', detail: stdout.trim() || abs });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
settle({
|
|
283
|
+
status: 'error',
|
|
284
|
+
detail: stderr.trim() ||
|
|
285
|
+
stdout.trim() ||
|
|
286
|
+
`\`claude --version\` exited with code ${String(code ?? 'null')}`,
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=claude-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runtime.js","sourceRoot":"","sources":["../../src/utils/claude-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local configuration store for the CompBox employee agent.
|
|
3
|
+
*
|
|
4
|
+
* Persists NON-SECRET pairing configuration to `~/.compbox/config.json`:
|
|
5
|
+
* - the central API base URL
|
|
6
|
+
* - the per-employee CompBox API token (an `st_...` pairing credential)
|
|
7
|
+
* - resolved identity (userId / email / name) for display and doctor checks
|
|
8
|
+
* - the selected AI mode
|
|
9
|
+
*
|
|
10
|
+
* The AI provider secret (e.g. an Anthropic API key) is NEVER stored here — it
|
|
11
|
+
* lives in the OS keychain (see `keychain.ts`) and is never transmitted to
|
|
12
|
+
* central. The API token is a pairing credential and is written to this file
|
|
13
|
+
* with `0600` permissions (owner-only), mirroring how tools like npm persist
|
|
14
|
+
* registry tokens in `.npmrc`.
|
|
15
|
+
*
|
|
16
|
+
* The config directory can be overridden with the `COMPBOX_CONFIG_DIR`
|
|
17
|
+
* environment variable (used by tests to avoid touching the real home dir).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* How the employee's Claude/AI access is provided (the ENG-119 3-mode
|
|
21
|
+
* agent-invoker decision). `claude-subscription` covers BOTH a personal
|
|
22
|
+
* Pro/Max login and a company Team-seat login — the daemon cannot tell which
|
|
23
|
+
* account is logged into the local `claude` CLI, so at the config level they
|
|
24
|
+
* are one mode. `api-key` is the always-safe default requiring no
|
|
25
|
+
* acknowledgment; `claude-subscription` requires `agentModeAcknowledged`.
|
|
26
|
+
*/
|
|
27
|
+
export type AiMode = 'claude-subscription' | 'api-key';
|
|
28
|
+
/** Non-secret local configuration written to `~/.compbox/config.json`. */
|
|
29
|
+
export interface CompboxConfig {
|
|
30
|
+
/** Central CompBox API base URL (e.g. https://compbox.example.com). */
|
|
31
|
+
apiBaseUrl?: string;
|
|
32
|
+
/** Per-employee CompBox API token (`st_...`). Pairing credential. */
|
|
33
|
+
apiToken?: string;
|
|
34
|
+
/** ID of the API token record (used to rotate/revoke later). */
|
|
35
|
+
apiTokenId?: string;
|
|
36
|
+
/** Resolved user id from central (attribution). */
|
|
37
|
+
userId?: string;
|
|
38
|
+
/** Resolved user email from central. */
|
|
39
|
+
email?: string;
|
|
40
|
+
/** Resolved user display name from central. */
|
|
41
|
+
userName?: string;
|
|
42
|
+
/** Selected AI mode. */
|
|
43
|
+
aiMode?: AiMode;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the user has explicitly acknowledged entitlement to use their
|
|
46
|
+
* Claude Code CLI login for automated/unattended daemon invocation
|
|
47
|
+
* (required before `claude-subscription` mode may run — ENG-119 decision).
|
|
48
|
+
* Never set for `api-key` mode, which needs no such gate.
|
|
49
|
+
*/
|
|
50
|
+
agentModeAcknowledged?: boolean;
|
|
51
|
+
/** ID of this machine's pairing record on central (Machine.id). */
|
|
52
|
+
machineId?: string;
|
|
53
|
+
/** Display name given to this machine at pairing time. */
|
|
54
|
+
machineName?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Route this machine's agent model calls through the local pxpipe proxy to
|
|
57
|
+
* cut token usage (ENG-E26). Read at daemon startup and promoted to
|
|
58
|
+
* PXPIPE_ENABLED so it works even when the daemon runs as a background
|
|
59
|
+
* service (which does not inherit the user's shell env).
|
|
60
|
+
*/
|
|
61
|
+
pxpipeEnabled?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Absolute path to the CompBox project repository checkout this machine runs
|
|
64
|
+
* agent pipelines against (ENG-164). A `start_execution`/`plan` job spawns
|
|
65
|
+
* the local `claude` CLI in this directory so the repo's `.mcp.json`
|
|
66
|
+
* (CompBox MCP) and `CLAUDE.md` (@agent routing) load. Set via
|
|
67
|
+
* `compbox run --project-dir <path>` (persisted) or the `COMPBOX_PROJECT_DIR`
|
|
68
|
+
* env fallback; a job fails with an actionable message when neither resolves
|
|
69
|
+
* to a git repository.
|
|
70
|
+
*/
|
|
71
|
+
projectDir?: string;
|
|
72
|
+
}
|
|
73
|
+
/** Environment fallback for the project repo directory (ENG-164 / H2). */
|
|
74
|
+
export declare const PROJECT_DIR_ENV = "COMPBOX_PROJECT_DIR";
|
|
75
|
+
/** Absolute path to the CompBox config directory (`~/.compbox` by default). */
|
|
76
|
+
export declare function getConfigDir(): string;
|
|
77
|
+
/** Absolute path to the config file. */
|
|
78
|
+
export declare function getConfigPath(): string;
|
|
79
|
+
/**
|
|
80
|
+
* Read the local config. Returns an empty object if the file is missing or
|
|
81
|
+
* unreadable/corrupt — callers treat a missing config as "not configured yet".
|
|
82
|
+
*/
|
|
83
|
+
export declare function readConfig(): CompboxConfig;
|
|
84
|
+
/**
|
|
85
|
+
* Write the full config object, creating the directory if needed and enforcing
|
|
86
|
+
* owner-only (`0600`) permissions since it contains the API token.
|
|
87
|
+
*/
|
|
88
|
+
export declare function writeConfig(config: CompboxConfig): void;
|
|
89
|
+
/**
|
|
90
|
+
* Shallow-merge a partial patch into the existing config and persist it.
|
|
91
|
+
* Returns the merged config.
|
|
92
|
+
*/
|
|
93
|
+
export declare function updateConfig(patch: Partial<CompboxConfig>): CompboxConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Promote the persisted pxpipe enable flag into the environment (ENG-E26) so
|
|
96
|
+
* the daemon routes agent calls through pxpipe even when running as a
|
|
97
|
+
* background service (which does not source the user's shell profile). A pre-set
|
|
98
|
+
* PXPIPE_ENABLED in the environment always wins and is left untouched.
|
|
99
|
+
*/
|
|
100
|
+
export declare function applyPxpipeEnv(config: CompboxConfig, env?: NodeJS.ProcessEnv): void;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the project repository directory for a `start_execution` job (ENG-164
|
|
103
|
+
* / H2), in a fixed, unit-tested precedence order:
|
|
104
|
+
*
|
|
105
|
+
* 1. An explicit `--project-dir <path>` flag (highest — an operator override
|
|
106
|
+
* for this run; run.ts also persists it so later runs inherit it).
|
|
107
|
+
* 2. The `COMPBOX_PROJECT_DIR` environment variable.
|
|
108
|
+
* 3. The persisted `config.projectDir` (set by a prior `--project-dir`).
|
|
109
|
+
*
|
|
110
|
+
* Returns the first non-empty candidate (trimmed), or `undefined` when none is
|
|
111
|
+
* set. This is a pure resolution helper — it does NOT check that the path is a
|
|
112
|
+
* git repository; validation is the caller's job so the failure can carry an
|
|
113
|
+
* actionable, path-specific message (see the invoker's git-repo check).
|
|
114
|
+
*/
|
|
115
|
+
export declare function resolveProjectDir(opts: {
|
|
116
|
+
flag?: string | undefined;
|
|
117
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
118
|
+
config?: CompboxConfig | undefined;
|
|
119
|
+
}): string | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* True when `dir` looks like a git repository — a `.git` entry (directory for a
|
|
122
|
+
* normal checkout, or a file for a worktree/submodule) exists inside it. Used to
|
|
123
|
+
* validate {@link resolveProjectDir}'s result before spawning the planner so a
|
|
124
|
+
* misconfigured path fails fast with an actionable message rather than letting
|
|
125
|
+
* `claude` start in a non-repo (where `.mcp.json`/`CLAUDE.md` would be absent).
|
|
126
|
+
*/
|
|
127
|
+
export declare function isGitRepoDir(dir: string): boolean;
|
|
128
|
+
//# sourceMappingURL=config-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/utils/config-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAAC;AAEvD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,0EAA0E;AAC1E,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAErD,+EAA+E;AAC/E,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAc1C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAcvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAKzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,IAAI,CAIN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IACpC,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CACpC,GAAG,MAAM,GAAG,SAAS,CASrB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMjD"}
|