@addai/node 0.4.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.
Files changed (133) hide show
  1. package/README.md +96 -0
  2. package/dist/agent-auth.d.ts +9 -0
  3. package/dist/agent-auth.js +96 -0
  4. package/dist/attachments.d.ts +20 -0
  5. package/dist/attachments.js +164 -0
  6. package/dist/capabilities.d.ts +64 -0
  7. package/dist/capabilities.js +406 -0
  8. package/dist/claude-binary.d.ts +16 -0
  9. package/dist/claude-binary.js +145 -0
  10. package/dist/claude-config.d.ts +11 -0
  11. package/dist/claude-config.js +144 -0
  12. package/dist/claude-print.d.ts +35 -0
  13. package/dist/claude-print.js +312 -0
  14. package/dist/claude-spawn.d.ts +40 -0
  15. package/dist/claude-spawn.js +220 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +137 -0
  18. package/dist/codex-binary.d.ts +4 -0
  19. package/dist/codex-binary.js +70 -0
  20. package/dist/codex-spawn.d.ts +47 -0
  21. package/dist/codex-spawn.js +320 -0
  22. package/dist/command-runner.d.ts +9 -0
  23. package/dist/command-runner.js +592 -0
  24. package/dist/config.d.ts +7 -0
  25. package/dist/config.js +38 -0
  26. package/dist/control-server.d.ts +5 -0
  27. package/dist/control-server.js +292 -0
  28. package/dist/diskguard.d.ts +96 -0
  29. package/dist/diskguard.js +435 -0
  30. package/dist/error-classify.d.ts +20 -0
  31. package/dist/error-classify.js +159 -0
  32. package/dist/events.d.ts +26 -0
  33. package/dist/events.js +97 -0
  34. package/dist/gemini-binary.d.ts +4 -0
  35. package/dist/gemini-binary.js +64 -0
  36. package/dist/gemini-spawn.d.ts +52 -0
  37. package/dist/gemini-spawn.js +369 -0
  38. package/dist/git-identity.d.ts +18 -0
  39. package/dist/git-identity.js +104 -0
  40. package/dist/grok-binary.d.ts +4 -0
  41. package/dist/grok-binary.js +70 -0
  42. package/dist/grok-spawn.d.ts +41 -0
  43. package/dist/grok-spawn.js +495 -0
  44. package/dist/guardrails.d.ts +32 -0
  45. package/dist/guardrails.js +125 -0
  46. package/dist/hang-watchdog.d.ts +17 -0
  47. package/dist/hang-watchdog.js +48 -0
  48. package/dist/harness-registry.d.ts +55 -0
  49. package/dist/harness-registry.js +180 -0
  50. package/dist/heartbeat.d.ts +13 -0
  51. package/dist/heartbeat.js +95 -0
  52. package/dist/index.d.ts +12 -0
  53. package/dist/index.js +440 -0
  54. package/dist/install.d.ts +63 -0
  55. package/dist/install.js +467 -0
  56. package/dist/jsonl-tail.d.ts +20 -0
  57. package/dist/jsonl-tail.js +192 -0
  58. package/dist/kimi-binary.d.ts +4 -0
  59. package/dist/kimi-binary.js +68 -0
  60. package/dist/kimi-spawn.d.ts +47 -0
  61. package/dist/kimi-spawn.js +319 -0
  62. package/dist/lockfile.d.ts +30 -0
  63. package/dist/lockfile.js +169 -0
  64. package/dist/mcp-config.d.ts +23 -0
  65. package/dist/mcp-config.js +133 -0
  66. package/dist/memory-capture.d.ts +20 -0
  67. package/dist/memory-capture.js +45 -0
  68. package/dist/memory-pack.d.ts +34 -0
  69. package/dist/memory-pack.js +171 -0
  70. package/dist/model-filter.d.ts +4 -0
  71. package/dist/model-filter.js +34 -0
  72. package/dist/node-health.d.ts +18 -0
  73. package/dist/node-health.js +74 -0
  74. package/dist/pairing.d.ts +20 -0
  75. package/dist/pairing.js +129 -0
  76. package/dist/paths.d.ts +9 -0
  77. package/dist/paths.js +48 -0
  78. package/dist/precompact-hook.d.ts +27 -0
  79. package/dist/precompact-hook.js +136 -0
  80. package/dist/projects.d.ts +25 -0
  81. package/dist/projects.js +255 -0
  82. package/dist/prompt-keystrokes.d.ts +3 -0
  83. package/dist/prompt-keystrokes.js +56 -0
  84. package/dist/pty-helper.d.ts +26 -0
  85. package/dist/pty-helper.js +156 -0
  86. package/dist/request-pump.d.ts +12 -0
  87. package/dist/request-pump.js +249 -0
  88. package/dist/run-retry.d.ts +28 -0
  89. package/dist/run-retry.js +81 -0
  90. package/dist/self-update.d.ts +75 -0
  91. package/dist/self-update.js +192 -0
  92. package/dist/session-runner.d.ts +130 -0
  93. package/dist/session-runner.js +2271 -0
  94. package/dist/sleep-detector.d.ts +11 -0
  95. package/dist/sleep-detector.js +32 -0
  96. package/dist/state-migrate.d.ts +8 -0
  97. package/dist/state-migrate.js +91 -0
  98. package/dist/store.d.ts +5 -0
  99. package/dist/store.js +82 -0
  100. package/dist/supabase-client.d.ts +15 -0
  101. package/dist/supabase-client.js +119 -0
  102. package/dist/time-context.d.ts +10 -0
  103. package/dist/time-context.js +71 -0
  104. package/dist/tokens.d.ts +4 -0
  105. package/dist/tokens.js +33 -0
  106. package/dist/tui/app.d.ts +35 -0
  107. package/dist/tui/app.js +82 -0
  108. package/dist/tui/console-capture.d.ts +13 -0
  109. package/dist/tui/console-capture.js +62 -0
  110. package/dist/tui/dashboard.d.ts +42 -0
  111. package/dist/tui/dashboard.js +205 -0
  112. package/dist/tui/data.d.ts +57 -0
  113. package/dist/tui/data.js +62 -0
  114. package/dist/tui/harnesses.d.ts +16 -0
  115. package/dist/tui/harnesses.js +354 -0
  116. package/dist/tui/render.d.ts +21 -0
  117. package/dist/tui/render.js +116 -0
  118. package/dist/tui/requests.d.ts +18 -0
  119. package/dist/tui/requests.js +127 -0
  120. package/dist/tui/run.d.ts +13 -0
  121. package/dist/tui/run.js +178 -0
  122. package/dist/tui/transcript.d.ts +39 -0
  123. package/dist/tui/transcript.js +234 -0
  124. package/dist/tui.d.ts +1 -0
  125. package/dist/tui.js +314 -0
  126. package/dist/types.d.ts +65 -0
  127. package/dist/types.js +3 -0
  128. package/dist/win.d.ts +67 -0
  129. package/dist/win.js +317 -0
  130. package/package.json +60 -0
  131. package/scripts/fix-pty-helper.js +28 -0
  132. package/scripts/precompact-capture.js +292 -0
  133. package/scripts/smoke-test.sh +74 -0
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ // Spawn a Claude Code session in a pseudoterminal. The runtime never
3
+ // renders a TUI, so we only need:
4
+ // - the PTY (node-pty) so Claude thinks it has a terminal
5
+ // - onData callback so the request runner can observe startup output
6
+ // and inject Enter to submit the initial prompt
7
+ // - onExit so sessions clean up
8
+ // - write() so callers can send prompts
9
+ // - the JSONL path on disk so the tail can stream clean events
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.spawnClaudeForRuntime = spawnClaudeForRuntime;
45
+ const pty = __importStar(require("node-pty"));
46
+ const path = __importStar(require("path"));
47
+ const crypto_1 = require("crypto");
48
+ const claude_binary_1 = require("./claude-binary");
49
+ const win_1 = require("./win");
50
+ const mcp_config_1 = require("./mcp-config");
51
+ const paths_1 = require("./paths");
52
+ function resolveMcpFilter(workingDirectory, enabled) {
53
+ if (!enabled)
54
+ return null;
55
+ const all = (0, mcp_config_1.listMcps)(workingDirectory).map(s => s.name);
56
+ // If the request lists every installed server, no filter needed.
57
+ if (enabled.length === all.length && enabled.every(n => all.includes(n)))
58
+ return null;
59
+ return enabled;
60
+ }
61
+ function spawnClaudeForRuntime(input) {
62
+ const ptyId = (0, crypto_1.randomUUID)();
63
+ const sessionId = (0, crypto_1.randomUUID)();
64
+ const claudeBin = (0, claude_binary_1.findClaudeBinary)();
65
+ // No claude on this runtime — fail THIS request cleanly rather than
66
+ // spawning a bare 'claude' that ENOENTs and crashes the daemon. The
67
+ // caller (runClaudeTui) wraps this in try/catch → per-request 'spawn_failed'.
68
+ if (!claudeBin) {
69
+ throw new Error('claude CLI not found on this runtime — install it from Studio → Runtimes → Harnesses, ' +
70
+ 'or run `npm i -g @anthropic-ai/claude-code`.');
71
+ }
72
+ const isWin = process.platform === 'win32';
73
+ // Build CLI flags.
74
+ const permissionFlags = [];
75
+ if (input.permissionMode === 'bypassPermissions') {
76
+ permissionFlags.push('--dangerously-skip-permissions');
77
+ }
78
+ else if (input.permissionMode) {
79
+ permissionFlags.push('--permission-mode', input.permissionMode);
80
+ }
81
+ const effortFlags = input.effortLevel ? ['--effort', input.effortLevel] : [];
82
+ // Must include 'user' so the pre-written `~/.claude/settings.json` with
83
+ // `skipDangerousModePermissionPrompt: true` is honored. Without user
84
+ // scope, Claude re-prompts the "WARNING: Bypass Permissions" dialog on
85
+ // every spawn → the TUI hangs forever waiting for confirmation → the
86
+ // daemon sees `ready` then `transcript_slow` 30s later and nothing
87
+ // else (the smoking-gun pattern we were chasing).
88
+ //
89
+ // We exclude 'local' on purpose: ~/.claude/settings.local.json can
90
+ // declare its own mcpServers that would leak through --strict-mcp-config.
91
+ // 'user' (settings.json only) is safe — mcpServers in user settings are
92
+ // still overridden by our --mcp-config + --strict-mcp-config flags.
93
+ const settingSources = ['--setting-sources', 'user,project'];
94
+ const mcpFlags = [];
95
+ let mcpConfigPath = null;
96
+ if (input.mcpConfigPathOverride) {
97
+ // install.ts already wrote a complete .mcp.json from the registry;
98
+ // skip the local-catalog filter entirely.
99
+ mcpConfigPath = input.mcpConfigPathOverride;
100
+ mcpFlags.push('--mcp-config', mcpConfigPath, '--strict-mcp-config');
101
+ }
102
+ else {
103
+ const filter = resolveMcpFilter(input.workingDirectory, input.enabledMcps);
104
+ if (filter !== null) {
105
+ try {
106
+ mcpConfigPath = (0, mcp_config_1.writeSessionMcpConfig)(ptyId, input.workingDirectory, filter);
107
+ if (mcpConfigPath) {
108
+ mcpFlags.push('--mcp-config', mcpConfigPath, '--strict-mcp-config');
109
+ }
110
+ }
111
+ catch (err) {
112
+ console.error('[runtime] failed to write session MCP config', err);
113
+ }
114
+ }
115
+ }
116
+ // --resume <id> attaches to an existing transcript; otherwise we
117
+ // mint a fresh session-id so the JSONL path is predictable for tailing.
118
+ const effectiveSessionId = input.resumeSessionId || sessionId;
119
+ const entityFlags = [];
120
+ if (input.appendSystemPrompt && input.appendSystemPrompt.trim().length > 0) {
121
+ entityFlags.push('--append-system-prompt', input.appendSystemPrompt);
122
+ }
123
+ if (Array.isArray(input.allowedTools) && input.allowedTools.length > 0) {
124
+ // Mirror claude-print: skip the flag entirely for an empty array.
125
+ // An empty `--allowed-tools ` value would hand claude an EMPTY allowlist
126
+ // (no tools at all) instead of inheriting its defaults — tool-blocking
127
+ // the entity. Empty array = "inherit defaults", same as undefined.
128
+ entityFlags.push('--allowed-tools', input.allowedTools.join(','));
129
+ }
130
+ // Model override — without this, claude uses its CLI default regardless
131
+ // of what the user picked in the composer.
132
+ const modelFlags = (input.model && input.model.trim())
133
+ ? ['--model', input.model.trim()]
134
+ : [];
135
+ // We never pass the prompt as argv anymore. Claude's TUI behavior with
136
+ // `--resume <id> <prompt>` was concatenating turn 2's prompt onto the
137
+ // previous turn instead of submitting it as a fresh user message. The
138
+ // daemon now types the prompt directly into the PTY after the TUI has
139
+ // settled — same input path for first-turn and continuation-turn, so
140
+ // both are reliable.
141
+ const claudeArgs = input.resumeSessionId
142
+ ? ['--resume', input.resumeSessionId]
143
+ : ['--session-id', sessionId];
144
+ const effectiveArgs = [
145
+ ...permissionFlags,
146
+ ...effortFlags,
147
+ ...modelFlags,
148
+ ...settingSources,
149
+ ...mcpFlags,
150
+ ...entityFlags,
151
+ ...claudeArgs,
152
+ ];
153
+ let shell;
154
+ let shellArgs;
155
+ if (isWin) {
156
+ // NO cmd.exe: node-pty joins argv with CommandLineToArgvW quoting, which
157
+ // cmd.exe re-parses by its own rules — paths with spaces break, and
158
+ // --append-system-prompt text (quotes/%/newlines) would be interpreted
159
+ // as shell syntax. resolveCliInvocation unwraps an npm .cmd shim to its
160
+ // node script (run via process.execPath) or passes an .exe straight
161
+ // through; either target parses argv via CommandLineToArgvW, matching
162
+ // node-pty's quoting. ConPTY handles both fine.
163
+ const inv = (0, win_1.resolveCliInvocation)(claudeBin, effectiveArgs);
164
+ shell = inv.file;
165
+ shellArgs = inv.args;
166
+ }
167
+ else {
168
+ // `exec "$@"` passes positional args straight through without shell escaping.
169
+ shell = process.env.SHELL || '/bin/bash';
170
+ shellArgs = ['-l', '-c', 'exec "$@"', '_', claudeBin, ...effectiveArgs];
171
+ }
172
+ const proc = pty.spawn(shell, shellArgs, {
173
+ name: 'xterm-256color',
174
+ cols: 120,
175
+ rows: 30,
176
+ cwd: input.workingDirectory,
177
+ env: {
178
+ ...process.env,
179
+ TERM: 'xterm-256color',
180
+ COLORTERM: 'truecolor',
181
+ ...(input.extraEnv ?? {}),
182
+ },
183
+ });
184
+ const dataListeners = [];
185
+ proc.onData((data) => {
186
+ for (const l of dataListeners)
187
+ try {
188
+ l(data);
189
+ }
190
+ catch { }
191
+ });
192
+ const exitListeners = [];
193
+ proc.onExit(({ exitCode }) => {
194
+ for (const l of exitListeners)
195
+ try {
196
+ l(exitCode);
197
+ }
198
+ catch { }
199
+ // best-effort cleanup of any temp MCP config
200
+ if (mcpConfigPath) {
201
+ try {
202
+ require('fs').unlinkSync(mcpConfigPath);
203
+ }
204
+ catch { }
205
+ }
206
+ });
207
+ const jsonlPath = path.join(paths_1.CLAUDE_PROJECTS_DIR, (0, claude_binary_1.encodeProjectPath)(input.workingDirectory), `${effectiveSessionId}.jsonl`);
208
+ return {
209
+ ptyId,
210
+ sessionId: effectiveSessionId,
211
+ jsonlPath,
212
+ write: (data) => proc.write(data),
213
+ kill: () => { try {
214
+ proc.kill();
215
+ }
216
+ catch { } },
217
+ onData: cb => { dataListeners.push(cb); },
218
+ onExit: cb => { exitListeners.push(cb); },
219
+ };
220
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ // ainode CLI — the +Ai Node console.
4
+ //
5
+ // ainode run this node: daemon + live dashboard
6
+ // ainode harnesses interactive harness manager (install / login)
7
+ // ainode unpair --yes disconnect from +Ai, cancel in-flight work
8
+ // ainode version print version
9
+ //
10
+ // With no args on a terminal, the daemon starts and the dashboard takes
11
+ // over the screen. Piped or headless (nohup/launchd) it prints plain log
12
+ // lines instead, so the daemon log never fills with escape sequences. If a
13
+ // daemon is already running, this attaches to it read-only rather than
14
+ // refusing to start.
15
+ //
16
+ // Unpair reaches Supabase via the daemon token in ~/.ainode/state.json and
17
+ // does not require the daemon to be running.
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ const lockfile_1 = require("./lockfile");
20
+ const index_1 = require("./index");
21
+ const store_1 = require("./store");
22
+ const supabase_client_1 = require("./supabase-client");
23
+ const harnesses_1 = require("./tui/harnesses");
24
+ const run_1 = require("./tui/run");
25
+ const args = process.argv.slice(2);
26
+ function token() {
27
+ const p = (0, store_1.readPairing)();
28
+ if (!p) {
29
+ console.error('Not paired. Run `ainode` (no args) to start the pairing flow.');
30
+ process.exit(1);
31
+ }
32
+ return p.daemonToken;
33
+ }
34
+ async function cmdUnpair() {
35
+ if (!(0, store_1.isPaired)()) {
36
+ console.log('Not paired — nothing to do.');
37
+ return;
38
+ }
39
+ const confirm = args.includes('--yes') || args.includes('-y');
40
+ if (!confirm) {
41
+ console.log('This will disconnect the runtime from +Ai and cancel any in-flight requests.');
42
+ console.log('Re-run with --yes to confirm.');
43
+ return;
44
+ }
45
+ try {
46
+ const r = await (0, supabase_client_1.rpc)('runtime_self_unpair', { p_token: token() });
47
+ if (r?.cancelled_requests) {
48
+ console.log(`Cancelled ${r.cancelled_requests} in-flight request(s).`);
49
+ }
50
+ }
51
+ catch (err) {
52
+ console.error(`server unpair failed (continuing with local cleanup): ${err.message}`);
53
+ }
54
+ (0, store_1.clearPairing)();
55
+ console.log('Unpaired. Run `ainode` to pair again.');
56
+ }
57
+ async function main() {
58
+ const cmd = args[0];
59
+ if (!cmd || cmd === 'run' || cmd === 'start' || cmd === '--run') {
60
+ const tui = (0, run_1.shouldRenderTui)({
61
+ isTTY: Boolean(process.stdout.isTTY),
62
+ stdinTTY: Boolean(process.stdin.isTTY),
63
+ noTui: process.env.AINODE_NO_TUI === '1',
64
+ });
65
+ // A daemon may already own this node. Rather than refusing, attach
66
+ // read-only so "is it running?" is answerable from any terminal.
67
+ //
68
+ // Only when it is genuinely still running, though. A crashed daemon
69
+ // leaves its lockfile behind, and treating that as an owner sent every
70
+ // later `ainode` into the viewer and returned before start() — so a node
71
+ // could never come back from a crash without someone deleting the file
72
+ // by hand. Same liveness check acquireLockfile() uses.
73
+ const existing = (0, lockfile_1.readLockfile)();
74
+ if (existing && (0, lockfile_1.processAlive)(existing.pid) && tui) {
75
+ await (0, run_1.runDashboard)({
76
+ viewerMode: true,
77
+ pid: existing.pid,
78
+ startedAt: existing.startedAt ?? null,
79
+ version: index_1.VERSION,
80
+ });
81
+ return;
82
+ }
83
+ const runtime = await (0, index_1.start)();
84
+ if (!tui) {
85
+ console.log(`ainode v${index_1.VERSION} running (pid ${runtime.pid}, port ${runtime.port})`);
86
+ if (!(0, store_1.isPaired)())
87
+ console.log('Not paired yet — pairing flow comes online in the next build.');
88
+ await new Promise(() => { });
89
+ return;
90
+ }
91
+ await (0, run_1.runDashboard)({
92
+ viewerMode: false,
93
+ pid: runtime.pid,
94
+ startedAt: Date.now(),
95
+ version: index_1.VERSION,
96
+ });
97
+ return;
98
+ }
99
+ switch (cmd) {
100
+ case 'harnesses':
101
+ await (0, harnesses_1.runHarnessesTui)();
102
+ return;
103
+ case 'unpair':
104
+ await cmdUnpair();
105
+ return;
106
+ case 'version':
107
+ case '--version':
108
+ case '-v':
109
+ console.log(index_1.VERSION);
110
+ return;
111
+ case 'help':
112
+ case '--help':
113
+ case '-h':
114
+ console.log(`ainode v${index_1.VERSION}
115
+
116
+ usage:
117
+ ainode run this +Ai Node — daemon + live dashboard
118
+ ainode harnesses install / log in agent harnesses (interactive)
119
+ ainode unpair --yes disconnect from +Ai
120
+ ainode version print version
121
+
122
+ Piped or headless output falls back to plain log lines. Set AINODE_NO_TUI=1
123
+ to force that on a terminal.
124
+
125
+ Pair the node by running it with no args and following the prompt
126
+ to vault.add.ai/entity/connect/<CODE>.
127
+ `);
128
+ return;
129
+ }
130
+ console.error(`unknown command: ${cmd}`);
131
+ console.error("try 'ainode help'");
132
+ process.exit(2);
133
+ }
134
+ main().catch(err => {
135
+ console.error(err.message || err);
136
+ process.exit(1);
137
+ });
@@ -0,0 +1,4 @@
1
+ /** Forget the cached lookup — call after an install so a fresh probe
2
+ * can see a binary that was missing at daemon start. */
3
+ export declare function resetCache(): void;
4
+ export declare function findCodexBinary(): string | null;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ // Locate the `codex` binary (OpenAI codex-cli) on the host.
3
+ // `npm install -g @openai/codex-cli` puts it on PATH as `codex`
4
+ // (a .cmd shim under %APPDATA%\npm on Windows).
5
+ //
6
+ // Before this existed, codex-spawn.ts called `spawn('codex', ...)` bare —
7
+ // which fails with ENOENT when the daemon's PATH (especially under
8
+ // launchd / desktop-app spawn) doesn't include npm-global's bin dir.
9
+ // The ENOENT rejected the spawn promise BEFORE session_end could be
10
+ // emitted, leaving the request stuck at 'running' on the DB.
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.resetCache = resetCache;
46
+ exports.findCodexBinary = findCodexBinary;
47
+ const path = __importStar(require("path"));
48
+ const os = __importStar(require("os"));
49
+ const win_1 = require("./win");
50
+ const POSIX_CANDIDATES = [
51
+ '/usr/local/bin/codex',
52
+ '/opt/homebrew/bin/codex',
53
+ path.join(os.homedir(), '.npm-global/bin/codex'),
54
+ ];
55
+ const WIN32_CANDIDATES = [
56
+ path.join(os.homedir(), '.npm-global', 'codex.cmd'),
57
+ ];
58
+ /** Forget the cached lookup — call after an install so a fresh probe
59
+ * can see a binary that was missing at daemon start. */
60
+ function resetCache() { cached = undefined; }
61
+ let cached;
62
+ function findCodexBinary() {
63
+ if (cached !== undefined)
64
+ return cached;
65
+ cached = (0, win_1.findCliBinary)('codex', {
66
+ posixCandidates: POSIX_CANDIDATES,
67
+ win32Candidates: WIN32_CANDIDATES,
68
+ });
69
+ return cached;
70
+ }
@@ -0,0 +1,47 @@
1
+ import type { RuntimeEvent, RuntimeEffortLevel } from './types';
2
+ /** What we pass into codex for one MCP server. Shape matches the
3
+ * install.ts ResolvedMcp env-resolved structure. */
4
+ export interface CodexMcpServer {
5
+ slug: string;
6
+ command: string;
7
+ args?: string[] | null;
8
+ env?: Record<string, string> | null;
9
+ }
10
+ export interface CodexInput {
11
+ workingDirectory: string;
12
+ prompt: string;
13
+ model?: string;
14
+ effortLevel?: RuntimeEffortLevel;
15
+ /** Sandbox for normal non-bypass runs. Defaults to workspace-write. */
16
+ sandboxMode?: 'read-only' | 'workspace-write' | 'danger-full-access';
17
+ /** Skip Codex approvals and sandboxing. Codex's "yolo" equivalent. */
18
+ bypassPermissions?: boolean;
19
+ /** If false, codex requires the workingDirectory to be a git repo. */
20
+ skipGitRepoCheck?: boolean;
21
+ /** Existing codex thread id to resume via `codex exec resume <id>`. */
22
+ resumeThreadId?: string;
23
+ /** When set, codex runs against a sandboxed CODEX_HOME that contains
24
+ * ONLY these MCP servers (host's ~/.codex/config.toml is ignored).
25
+ * Mirrors the claude --strict-mcp-config behavior. */
26
+ mcpServers?: CodexMcpServer[];
27
+ }
28
+ export interface CodexHandle {
29
+ pid: number | undefined;
30
+ threadId: string | null;
31
+ kill(): void;
32
+ onEvent(cb: (e: RuntimeEvent) => void): void;
33
+ /** Fires on every raw stdout/stderr chunk. codex emits no per-tool progress
34
+ * events during a long `command_execution`, so raw bytes are the liveness
35
+ * signal the hang-watchdog uses to avoid killing a busy agent. */
36
+ onActivity(cb: () => void): void;
37
+ done: Promise<number>;
38
+ }
39
+ /** Clamp our effort ladder to a value codex's CLI accepts. codex only knows
40
+ * `minimal|low|medium|high` and FAIL-FASTS (exit 2, dead-letter) on anything
41
+ * else — but RuntimeEffortLevel also carries `xhigh`/`max`, and a ladder
42
+ * failover (e.g. claude(max) → codex) forwards req.effort across agents
43
+ * unchanged. Map the extra-high tiers down to `high`; drop anything
44
+ * unrecognised so we emit no `-c reasoning.effort` at all rather than a bad
45
+ * one. Mirrors grokEffort() in grok-spawn.ts. */
46
+ export declare function codexEffort(level?: RuntimeEffortLevel): string | null;
47
+ export declare function spawnCodex(input: CodexInput): CodexHandle;