@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.
- package/README.md +96 -0
- package/dist/agent-auth.d.ts +9 -0
- package/dist/agent-auth.js +96 -0
- package/dist/attachments.d.ts +20 -0
- package/dist/attachments.js +164 -0
- package/dist/capabilities.d.ts +64 -0
- package/dist/capabilities.js +406 -0
- package/dist/claude-binary.d.ts +16 -0
- package/dist/claude-binary.js +145 -0
- package/dist/claude-config.d.ts +11 -0
- package/dist/claude-config.js +144 -0
- package/dist/claude-print.d.ts +35 -0
- package/dist/claude-print.js +312 -0
- package/dist/claude-spawn.d.ts +40 -0
- package/dist/claude-spawn.js +220 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +137 -0
- package/dist/codex-binary.d.ts +4 -0
- package/dist/codex-binary.js +70 -0
- package/dist/codex-spawn.d.ts +47 -0
- package/dist/codex-spawn.js +320 -0
- package/dist/command-runner.d.ts +9 -0
- package/dist/command-runner.js +592 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +38 -0
- package/dist/control-server.d.ts +5 -0
- package/dist/control-server.js +292 -0
- package/dist/diskguard.d.ts +96 -0
- package/dist/diskguard.js +435 -0
- package/dist/error-classify.d.ts +20 -0
- package/dist/error-classify.js +159 -0
- package/dist/events.d.ts +26 -0
- package/dist/events.js +97 -0
- package/dist/gemini-binary.d.ts +4 -0
- package/dist/gemini-binary.js +64 -0
- package/dist/gemini-spawn.d.ts +52 -0
- package/dist/gemini-spawn.js +369 -0
- package/dist/git-identity.d.ts +18 -0
- package/dist/git-identity.js +104 -0
- package/dist/grok-binary.d.ts +4 -0
- package/dist/grok-binary.js +70 -0
- package/dist/grok-spawn.d.ts +41 -0
- package/dist/grok-spawn.js +495 -0
- package/dist/guardrails.d.ts +32 -0
- package/dist/guardrails.js +125 -0
- package/dist/hang-watchdog.d.ts +17 -0
- package/dist/hang-watchdog.js +48 -0
- package/dist/harness-registry.d.ts +55 -0
- package/dist/harness-registry.js +180 -0
- package/dist/heartbeat.d.ts +13 -0
- package/dist/heartbeat.js +95 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +440 -0
- package/dist/install.d.ts +63 -0
- package/dist/install.js +467 -0
- package/dist/jsonl-tail.d.ts +20 -0
- package/dist/jsonl-tail.js +192 -0
- package/dist/kimi-binary.d.ts +4 -0
- package/dist/kimi-binary.js +68 -0
- package/dist/kimi-spawn.d.ts +47 -0
- package/dist/kimi-spawn.js +319 -0
- package/dist/lockfile.d.ts +30 -0
- package/dist/lockfile.js +169 -0
- package/dist/mcp-config.d.ts +23 -0
- package/dist/mcp-config.js +133 -0
- package/dist/memory-capture.d.ts +20 -0
- package/dist/memory-capture.js +45 -0
- package/dist/memory-pack.d.ts +34 -0
- package/dist/memory-pack.js +171 -0
- package/dist/model-filter.d.ts +4 -0
- package/dist/model-filter.js +34 -0
- package/dist/node-health.d.ts +18 -0
- package/dist/node-health.js +74 -0
- package/dist/pairing.d.ts +20 -0
- package/dist/pairing.js +129 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.js +48 -0
- package/dist/precompact-hook.d.ts +27 -0
- package/dist/precompact-hook.js +136 -0
- package/dist/projects.d.ts +25 -0
- package/dist/projects.js +255 -0
- package/dist/prompt-keystrokes.d.ts +3 -0
- package/dist/prompt-keystrokes.js +56 -0
- package/dist/pty-helper.d.ts +26 -0
- package/dist/pty-helper.js +156 -0
- package/dist/request-pump.d.ts +12 -0
- package/dist/request-pump.js +249 -0
- package/dist/run-retry.d.ts +28 -0
- package/dist/run-retry.js +81 -0
- package/dist/self-update.d.ts +75 -0
- package/dist/self-update.js +192 -0
- package/dist/session-runner.d.ts +130 -0
- package/dist/session-runner.js +2271 -0
- package/dist/sleep-detector.d.ts +11 -0
- package/dist/sleep-detector.js +32 -0
- package/dist/state-migrate.d.ts +8 -0
- package/dist/state-migrate.js +91 -0
- package/dist/store.d.ts +5 -0
- package/dist/store.js +82 -0
- package/dist/supabase-client.d.ts +15 -0
- package/dist/supabase-client.js +119 -0
- package/dist/time-context.d.ts +10 -0
- package/dist/time-context.js +71 -0
- package/dist/tokens.d.ts +4 -0
- package/dist/tokens.js +33 -0
- package/dist/tui/app.d.ts +35 -0
- package/dist/tui/app.js +82 -0
- package/dist/tui/console-capture.d.ts +13 -0
- package/dist/tui/console-capture.js +62 -0
- package/dist/tui/dashboard.d.ts +42 -0
- package/dist/tui/dashboard.js +205 -0
- package/dist/tui/data.d.ts +57 -0
- package/dist/tui/data.js +62 -0
- package/dist/tui/harnesses.d.ts +16 -0
- package/dist/tui/harnesses.js +354 -0
- package/dist/tui/render.d.ts +21 -0
- package/dist/tui/render.js +116 -0
- package/dist/tui/requests.d.ts +18 -0
- package/dist/tui/requests.js +127 -0
- package/dist/tui/run.d.ts +13 -0
- package/dist/tui/run.js +178 -0
- package/dist/tui/transcript.d.ts +39 -0
- package/dist/tui/transcript.js +234 -0
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +314 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.js +3 -0
- package/dist/win.d.ts +67 -0
- package/dist/win.js +317 -0
- package/package.json +60 -0
- package/scripts/fix-pty-helper.js +28 -0
- package/scripts/precompact-capture.js +292 -0
- package/scripts/smoke-test.sh +74 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// codex exec --json adapter.
|
|
3
|
+
//
|
|
4
|
+
// Codex's CLI prints a JSONL stream similar in spirit to claude
|
|
5
|
+
// --output-format stream-json, with different shapes. We normalise what
|
|
6
|
+
// we can recognise (thread.started, turn.started/completed/failed,
|
|
7
|
+
// assistant text deltas, errors) and pass everything else through as
|
|
8
|
+
// claude:codex.<type> events.
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.codexEffort = codexEffort;
|
|
44
|
+
exports.spawnCodex = spawnCodex;
|
|
45
|
+
const child_process_1 = require("child_process");
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const codex_binary_1 = require("./codex-binary");
|
|
50
|
+
const diskguard_1 = require("./diskguard");
|
|
51
|
+
const win_1 = require("./win");
|
|
52
|
+
const events_1 = require("./events");
|
|
53
|
+
/** Quote a string as a TOML basic string (double-quoted, escape backslash + quote). */
|
|
54
|
+
function tomlString(s) {
|
|
55
|
+
return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
|
|
56
|
+
}
|
|
57
|
+
/** Build a config.toml that declares only the supplied MCP servers and
|
|
58
|
+
* return the directory path to use as CODEX_HOME. Caller is responsible
|
|
59
|
+
* for cleanup (we drop it under os.tmpdir() so it gets reaped). */
|
|
60
|
+
function writeCodexHome(servers, workingDirectory) {
|
|
61
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'entities-codex-'));
|
|
62
|
+
let toml = '# Generated by ainode — sandboxes codex against host config\n\n';
|
|
63
|
+
for (const s of servers) {
|
|
64
|
+
if (!s.slug || !s.command)
|
|
65
|
+
continue;
|
|
66
|
+
// On native Windows, registry commands like `npx` are .cmd shims that
|
|
67
|
+
// codex can't CreateProcess directly — wrap with `cmd /c` (no-op on POSIX).
|
|
68
|
+
const wrapped = (0, win_1.wrapMcpCommandForPlatform)(s.command, Array.isArray(s.args) ? s.args : []);
|
|
69
|
+
toml += `[mcp_servers.${tomlString(s.slug).slice(1, -1)}]\n`;
|
|
70
|
+
toml += `command = ${tomlString(wrapped.command)}\n`;
|
|
71
|
+
if (wrapped.args.length > 0) {
|
|
72
|
+
toml += `args = [${wrapped.args.map(tomlString).join(', ')}]\n`;
|
|
73
|
+
}
|
|
74
|
+
if (s.env && Object.keys(s.env).length > 0) {
|
|
75
|
+
toml += `[mcp_servers.${tomlString(s.slug).slice(1, -1)}.env]\n`;
|
|
76
|
+
for (const [k, v] of Object.entries(s.env)) {
|
|
77
|
+
toml += `${k} = ${tomlString(String(v))}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
toml += '\n';
|
|
81
|
+
}
|
|
82
|
+
fs.writeFileSync(path.join(dir, 'config.toml'), toml, { mode: 0o600 });
|
|
83
|
+
// Symlink the host's auth artifacts into the sandbox so codex can find
|
|
84
|
+
// the user's logged-in credentials. Without this codex sees CODEX_HOME
|
|
85
|
+
// pointing at an empty dir and 401s against the OpenAI API.
|
|
86
|
+
const hostHome = path.join(os.homedir(), '.codex');
|
|
87
|
+
if (fs.existsSync(hostHome)) {
|
|
88
|
+
for (const file of ['auth.json', 'credentials.json', 'config.json']) {
|
|
89
|
+
const real = path.join(hostHome, file);
|
|
90
|
+
if (fs.existsSync(real)) {
|
|
91
|
+
// symlink where allowed; copy on Windows (file symlinks need
|
|
92
|
+
// admin/Developer Mode there). A copied auth.json means a token
|
|
93
|
+
// refreshed mid-run doesn't propagate back to the host — acceptable:
|
|
94
|
+
// the next run re-copies the current host file.
|
|
95
|
+
(0, win_1.linkOrCopyFile)(real, path.join(dir, file));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Also link the sessions dir so codex can resume across runs.
|
|
99
|
+
// 'junction' works unprivileged on Windows; ignored on POSIX.
|
|
100
|
+
const sessionsDir = path.join(hostHome, 'sessions');
|
|
101
|
+
if (fs.existsSync(sessionsDir)) {
|
|
102
|
+
(0, win_1.linkDir)(sessionsDir, path.join(dir, 'sessions'));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// The registry installer writes selected skills to .claude/skills for
|
|
106
|
+
// Claude. Codex discovers skills from CODEX_HOME/skills, so bridge the
|
|
107
|
+
// session-local install into the isolated home.
|
|
108
|
+
const sessionSkillsDir = path.join(workingDirectory, '.claude', 'skills');
|
|
109
|
+
if (fs.existsSync(sessionSkillsDir)) {
|
|
110
|
+
(0, win_1.linkDir)(sessionSkillsDir, path.join(dir, 'skills'));
|
|
111
|
+
}
|
|
112
|
+
return dir;
|
|
113
|
+
}
|
|
114
|
+
function lineToEvents(line) {
|
|
115
|
+
const type = typeof line.type === 'string' ? line.type : '';
|
|
116
|
+
// Errors come as { type: 'error', message: string } — surface them.
|
|
117
|
+
if (type === 'error') {
|
|
118
|
+
return [{ type: 'error', code: 'codex_stream_error', message: String(line.message ?? 'unknown') }];
|
|
119
|
+
}
|
|
120
|
+
// Failure of the whole turn.
|
|
121
|
+
if (type === 'turn.failed') {
|
|
122
|
+
const err = line.error?.message ?? 'turn failed';
|
|
123
|
+
return [
|
|
124
|
+
{ type: 'error', code: 'codex_turn_failed', message: err },
|
|
125
|
+
{ type: 'turn_complete', stopReason: 'failed' },
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
// Successful completion of a turn.
|
|
129
|
+
if (type === 'turn.completed') {
|
|
130
|
+
return [{ type: 'turn_complete', usage: line.usage ?? undefined }];
|
|
131
|
+
}
|
|
132
|
+
// Agent text response (codex uses slightly different keys depending
|
|
133
|
+
// on version — try a few likely shapes).
|
|
134
|
+
if (type === 'agent_message' || type === 'agent.response.delta' || type === 'message') {
|
|
135
|
+
const text = (typeof line.text === 'string' && line.text) ||
|
|
136
|
+
(typeof line.message === 'string' && line.message) ||
|
|
137
|
+
(line.delta?.text) ||
|
|
138
|
+
undefined;
|
|
139
|
+
if (text)
|
|
140
|
+
return [{ type: 'assistant_text', delta: text }];
|
|
141
|
+
}
|
|
142
|
+
// Newer codex versions wrap assistant text in 'item.completed' events
|
|
143
|
+
// as { item: { type: 'agent_message', text } }. Keep this strict so
|
|
144
|
+
// command/reasoning/tool items with textual payloads don't become answers.
|
|
145
|
+
if (type === 'item.completed') {
|
|
146
|
+
const item = line.item;
|
|
147
|
+
if (item?.type === 'agent_message' && typeof item.text === 'string' && item.text) {
|
|
148
|
+
return [{ type: 'assistant_text', delta: item.text }];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Anything else: opaque passthrough. Reusing the claude:<type> namespace
|
|
152
|
+
// is intentional — vault renders it generically; we'd otherwise need a
|
|
153
|
+
// new "codex:" namespace which adds zero value.
|
|
154
|
+
return [{ type: `claude:codex.${type || 'unknown'}`, raw: line }];
|
|
155
|
+
}
|
|
156
|
+
/** Clamp our effort ladder to a value codex's CLI accepts. codex only knows
|
|
157
|
+
* `minimal|low|medium|high` and FAIL-FASTS (exit 2, dead-letter) on anything
|
|
158
|
+
* else — but RuntimeEffortLevel also carries `xhigh`/`max`, and a ladder
|
|
159
|
+
* failover (e.g. claude(max) → codex) forwards req.effort across agents
|
|
160
|
+
* unchanged. Map the extra-high tiers down to `high`; drop anything
|
|
161
|
+
* unrecognised so we emit no `-c reasoning.effort` at all rather than a bad
|
|
162
|
+
* one. Mirrors grokEffort() in grok-spawn.ts. */
|
|
163
|
+
function codexEffort(level) {
|
|
164
|
+
switch (level) {
|
|
165
|
+
case 'low': return 'low';
|
|
166
|
+
case 'medium': return 'medium';
|
|
167
|
+
case 'high':
|
|
168
|
+
case 'xhigh':
|
|
169
|
+
case 'max': return 'high';
|
|
170
|
+
default: return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function spawnCodex(input) {
|
|
174
|
+
// codex 0.132's `exec resume` subcommand has a restricted option set —
|
|
175
|
+
// --cd, --json, --skip-git-repo-check, --model all belong on the BASE
|
|
176
|
+
// `exec` command, before the `resume` subcommand. Putting them after
|
|
177
|
+
// resume returns "unexpected argument" and exits 2.
|
|
178
|
+
const baseFlags = ['--cd', input.workingDirectory, '--json'];
|
|
179
|
+
if (input.skipGitRepoCheck)
|
|
180
|
+
baseFlags.push('--skip-git-repo-check');
|
|
181
|
+
if (input.bypassPermissions)
|
|
182
|
+
baseFlags.push('--dangerously-bypass-approvals-and-sandbox');
|
|
183
|
+
else
|
|
184
|
+
baseFlags.push('--sandbox', input.sandboxMode ?? 'workspace-write');
|
|
185
|
+
if (input.model)
|
|
186
|
+
baseFlags.push('--model', input.model);
|
|
187
|
+
const effort = codexEffort(input.effortLevel);
|
|
188
|
+
if (effort) {
|
|
189
|
+
baseFlags.push('-c', `reasoning.effort=${JSON.stringify(effort)}`);
|
|
190
|
+
}
|
|
191
|
+
const args = input.resumeThreadId
|
|
192
|
+
? ['exec', ...baseFlags, 'resume', input.resumeThreadId, input.prompt]
|
|
193
|
+
: ['exec', ...baseFlags, input.prompt];
|
|
194
|
+
// Sandbox codex against an isolated CODEX_HOME so the host's
|
|
195
|
+
// ~/.codex/config.toml mcp_servers don't leak in. TERM=dumb keeps
|
|
196
|
+
// codex's TTY heuristics happy (no fancy terminal output).
|
|
197
|
+
const codexHome = writeCodexHome(input.mcpServers ?? [], input.workingDirectory);
|
|
198
|
+
// Register the scratch dir with DiskGuard: marks it active (so a
|
|
199
|
+
// concurrent sweep won't evict a live run) and writes a PID lock so a
|
|
200
|
+
// different runtime process can tell it's still owned. dispose(true)
|
|
201
|
+
// removes the dir — this is the fix for the historical leak where every
|
|
202
|
+
// run's CODEX_HOME (50-350 MB) was abandoned under os.tmpdir() forever.
|
|
203
|
+
const guardSession = (0, diskguard_1.getDiskGuard)().beginSession(codexHome);
|
|
204
|
+
const childEnv = {
|
|
205
|
+
...process.env,
|
|
206
|
+
TERM: 'dumb',
|
|
207
|
+
CODEX_HOME: codexHome,
|
|
208
|
+
};
|
|
209
|
+
// Resolve binary path before spawn. Previously called spawn('codex', ...)
|
|
210
|
+
// bare, which threw ENOENT under launchd / desktop-app PATH inheritance
|
|
211
|
+
// and rejected `done` before any session_end event fired → request
|
|
212
|
+
// stuck at 'running' on the DB.
|
|
213
|
+
const bin = (0, codex_binary_1.findCodexBinary)();
|
|
214
|
+
const listeners = [];
|
|
215
|
+
const emit = (e) => { for (const l of listeners)
|
|
216
|
+
try {
|
|
217
|
+
l(e);
|
|
218
|
+
}
|
|
219
|
+
catch { } };
|
|
220
|
+
const activityListeners = [];
|
|
221
|
+
const touchActivity = () => { for (const l of activityListeners)
|
|
222
|
+
try {
|
|
223
|
+
l();
|
|
224
|
+
}
|
|
225
|
+
catch { } };
|
|
226
|
+
let threadId = null;
|
|
227
|
+
let buffer = '';
|
|
228
|
+
if (!bin) {
|
|
229
|
+
// Synthesise a synchronous-looking failure: emit error + turn_complete
|
|
230
|
+
// so the caller's onExit logic gets the same shape as a normal failed
|
|
231
|
+
// run. bufferedEvents delivers even though the runner attaches its
|
|
232
|
+
// listener after an await (setImmediate used to fire first → dropped).
|
|
233
|
+
void guardSession.dispose(true);
|
|
234
|
+
const events = (0, events_1.bufferedEvents)([
|
|
235
|
+
{ type: 'error', code: 'codex_not_found', message: 'codex binary not on PATH. Install with `npm i -g @openai/codex-cli`.' },
|
|
236
|
+
{ type: 'turn_complete', stopReason: 'failed' },
|
|
237
|
+
]);
|
|
238
|
+
return {
|
|
239
|
+
pid: undefined,
|
|
240
|
+
get threadId() { return null; },
|
|
241
|
+
onEvent: events.onEvent,
|
|
242
|
+
onActivity: () => { },
|
|
243
|
+
done: Promise.resolve(-1),
|
|
244
|
+
kill() { },
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
let proc;
|
|
248
|
+
try {
|
|
249
|
+
// npm .cmd shim → node script (win32); identity on POSIX. Throws on an
|
|
250
|
+
// unresolvable shim — caught below alongside spawn's own sync errors.
|
|
251
|
+
const inv = (0, win_1.resolveCliInvocation)(bin, args);
|
|
252
|
+
proc = (0, child_process_1.spawn)(inv.file, inv.args, {
|
|
253
|
+
cwd: input.workingDirectory,
|
|
254
|
+
env: childEnv,
|
|
255
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
const msg = err.message || String(err);
|
|
260
|
+
void guardSession.dispose(true);
|
|
261
|
+
const events = (0, events_1.bufferedEvents)([
|
|
262
|
+
{ type: 'error', code: 'codex_spawn_failed', message: msg },
|
|
263
|
+
{ type: 'turn_complete', stopReason: 'failed' },
|
|
264
|
+
]);
|
|
265
|
+
return {
|
|
266
|
+
pid: undefined,
|
|
267
|
+
get threadId() { return null; },
|
|
268
|
+
onEvent: events.onEvent,
|
|
269
|
+
onActivity: () => { },
|
|
270
|
+
done: Promise.resolve(-1),
|
|
271
|
+
kill() { },
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
proc.stdout?.on('data', (chunk) => {
|
|
275
|
+
touchActivity();
|
|
276
|
+
buffer += chunk.toString('utf8');
|
|
277
|
+
let nl;
|
|
278
|
+
while ((nl = buffer.indexOf('\n')) !== -1) {
|
|
279
|
+
const raw = buffer.slice(0, nl).trim();
|
|
280
|
+
buffer = buffer.slice(nl + 1);
|
|
281
|
+
if (!raw)
|
|
282
|
+
continue;
|
|
283
|
+
let parsed;
|
|
284
|
+
try {
|
|
285
|
+
parsed = JSON.parse(raw);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (parsed.type === 'thread.started' && typeof parsed.thread_id === 'string') {
|
|
291
|
+
threadId = parsed.thread_id;
|
|
292
|
+
}
|
|
293
|
+
for (const ev of lineToEvents(parsed))
|
|
294
|
+
emit(ev);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
proc.stderr?.on('data', (chunk) => {
|
|
298
|
+
touchActivity();
|
|
299
|
+
const txt = chunk.toString('utf8').trim();
|
|
300
|
+
if (txt === 'Reading additional input from stdin...')
|
|
301
|
+
return;
|
|
302
|
+
if (txt)
|
|
303
|
+
emit({ type: 'error', code: 'codex_stderr', message: txt.slice(0, 4096) });
|
|
304
|
+
});
|
|
305
|
+
const done = new Promise((resolve, reject) => {
|
|
306
|
+
// Remove the scratch CODEX_HOME on any terminal outcome (clean exit,
|
|
307
|
+
// SIGINT from kill()/timeout, or async spawn error). DiskGuard is the
|
|
308
|
+
// backstop for the paths this can't cover — SIGKILL and crashes.
|
|
309
|
+
proc.once('error', (err) => { void guardSession.dispose(true); reject(err); });
|
|
310
|
+
proc.once('exit', (code) => { void guardSession.dispose(true); resolve(code ?? -1); });
|
|
311
|
+
});
|
|
312
|
+
return {
|
|
313
|
+
pid: proc.pid,
|
|
314
|
+
get threadId() { return threadId; },
|
|
315
|
+
kill: () => { (0, win_1.killProcessTree)(proc); },
|
|
316
|
+
onEvent: (cb) => { listeners.push(cb); },
|
|
317
|
+
onActivity: (cb) => { activityListeners.push(cb); },
|
|
318
|
+
done,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type RestartHook = (opts: {
|
|
2
|
+
commandId: string;
|
|
3
|
+
version: string;
|
|
4
|
+
}) => Promise<void>;
|
|
5
|
+
export declare function setRestartHook(fn: RestartHook): void;
|
|
6
|
+
/** Called from index.ts when a heartbeat reports pending_commands > 0.
|
|
7
|
+
* Drains the queue serially; re-entrant calls while draining are no-ops
|
|
8
|
+
* (the drain loop re-picks until empty). */
|
|
9
|
+
export declare function wake(): void;
|