@titan-design/agent 0.3.0 → 0.4.1
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 +56 -3
- package/dist/index.d.ts +139 -122
- package/dist/index.js +301 -98
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/run.ts
|
|
2
2
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
-
import { toJSONSchema } from "zod";
|
|
3
|
+
import { toJSONSchema as toJSONSchema2 } from "zod";
|
|
4
4
|
|
|
5
5
|
// src/env.ts
|
|
6
6
|
var ANTI_NESTING_VARS = ["CLAUDECODE", "CLAUDE_CODE_SSE_PORT", "CLAUDE_CODE_ENTRYPOINT"];
|
|
@@ -64,7 +64,7 @@ function assertAuthEnvOk(env, options = {}) {
|
|
|
64
64
|
"pass the env through prepareEnv, or set allowApiKeyBilling to bill the API account on purpose"
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
|
-
if (!env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
67
|
+
if (options.requireOAuthToken !== false && !env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
68
68
|
throw new AuthMisconfiguredError(
|
|
69
69
|
"CLAUDE_CODE_OAUTH_TOKEN is not set; subscription auth will fail",
|
|
70
70
|
"run `claude setup-token` once and export the printed token"
|
|
@@ -84,6 +84,86 @@ function assertApiKeySourceAllowed(observed) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// src/claude-print.ts
|
|
88
|
+
import { spawn as spawn2 } from "child_process";
|
|
89
|
+
import { accessSync, constants, statSync } from "fs";
|
|
90
|
+
import { delimiter, join as join2 } from "path";
|
|
91
|
+
import { toJSONSchema } from "zod";
|
|
92
|
+
|
|
93
|
+
// src/codex-process.ts
|
|
94
|
+
import { execFile, spawn } from "child_process";
|
|
95
|
+
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
96
|
+
import { tmpdir } from "os";
|
|
97
|
+
import { join } from "path";
|
|
98
|
+
import { createInterface } from "readline";
|
|
99
|
+
async function inspectCodexVersion(executablePath, env, options) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
execFile(
|
|
102
|
+
executablePath,
|
|
103
|
+
["--version"],
|
|
104
|
+
{
|
|
105
|
+
env,
|
|
106
|
+
encoding: "utf8",
|
|
107
|
+
signal: options.signal,
|
|
108
|
+
timeout: Math.max(1, Math.ceil(options.timeoutMs)),
|
|
109
|
+
killSignal: "SIGKILL"
|
|
110
|
+
},
|
|
111
|
+
(error, stdout) => {
|
|
112
|
+
if (error) reject(error);
|
|
113
|
+
else resolve(stdout);
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function startCodexProcess(input) {
|
|
119
|
+
const child = spawn(input.executablePath, input.args, {
|
|
120
|
+
cwd: input.cwd,
|
|
121
|
+
env: input.env,
|
|
122
|
+
detached: process.platform !== "win32",
|
|
123
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
stdout: lines(child.stdout),
|
|
127
|
+
stderr: lines(child.stderr),
|
|
128
|
+
completed: completionOf(child),
|
|
129
|
+
terminate: (signal) => terminateProcessGroup(child, signal)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function createCodexSchemaFile(schema) {
|
|
133
|
+
const directory = await mkdtemp(join(tmpdir(), "titan-codex-schema-"));
|
|
134
|
+
const path = join(directory, "output-schema.json");
|
|
135
|
+
try {
|
|
136
|
+
await writeFile(path, JSON.stringify(schema), { encoding: "utf8", mode: 384 });
|
|
137
|
+
return { path, dispose: () => rm(directory, { recursive: true, force: true }) };
|
|
138
|
+
} catch (error) {
|
|
139
|
+
await rm(directory, { recursive: true, force: true });
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function* lines(stream) {
|
|
144
|
+
const reader = createInterface({ input: stream });
|
|
145
|
+
for await (const line of reader) yield line;
|
|
146
|
+
}
|
|
147
|
+
function completionOf(child) {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
child.once("error", reject);
|
|
150
|
+
child.once("close", (exitCode, signal) => resolve({ exitCode, signal }));
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function terminateProcessGroup(child, signal) {
|
|
154
|
+
if (child.pid && process.platform !== "win32") {
|
|
155
|
+
try {
|
|
156
|
+
process.kill(-child.pid, signal);
|
|
157
|
+
return;
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
child.kill(signal);
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
87
167
|
// src/failures.ts
|
|
88
168
|
var RATE_LIMIT_PATTERNS = [
|
|
89
169
|
/you'?ve hit your (session|weekly|opus|sonnet) limit/i,
|
|
@@ -146,18 +226,228 @@ function sumModelCost(modelUsage) {
|
|
|
146
226
|
return Object.values(modelUsage).reduce((total, m) => total + (m.costUSD ?? 0), 0);
|
|
147
227
|
}
|
|
148
228
|
|
|
229
|
+
// src/harness-contracts.ts
|
|
230
|
+
var CLAUDE_PRINT_UNSUPPORTED_OPTIONS = [
|
|
231
|
+
"allowedTools",
|
|
232
|
+
"disallowedTools",
|
|
233
|
+
"permissionMode",
|
|
234
|
+
"resumeSessionId",
|
|
235
|
+
"agents",
|
|
236
|
+
"mcpServers",
|
|
237
|
+
"hooks"
|
|
238
|
+
];
|
|
239
|
+
var CODEX_SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
240
|
+
var CODEX_APPROVAL_POLICIES = ["untrusted", "on-failure", "on-request", "never"];
|
|
241
|
+
var EXECUTION_CAPABILITIES = [
|
|
242
|
+
"fresh_run",
|
|
243
|
+
"resume",
|
|
244
|
+
"fork",
|
|
245
|
+
"structured_output",
|
|
246
|
+
"interactive_approvals",
|
|
247
|
+
"external_cancellation",
|
|
248
|
+
"semantic_interrupt",
|
|
249
|
+
"active_turn_steering",
|
|
250
|
+
"idle_turn_submission",
|
|
251
|
+
"persisted_transcript",
|
|
252
|
+
"process_reattachment",
|
|
253
|
+
"token_reporting"
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
// src/claude-print.ts
|
|
257
|
+
var CLAUDE_PRINT_KILL_GRACE_MS = 5e3;
|
|
258
|
+
var DEFAULT_TIMEOUT_MS = 6e5;
|
|
259
|
+
var EMPTY_MCP_CONFIG = '{"mcpServers":{}}';
|
|
260
|
+
var LOGIN_FAILURE = /not logged in|please run \/login|invalid api key/i;
|
|
261
|
+
function claudePrintCapabilities() {
|
|
262
|
+
const capabilities = Object.fromEntries(EXECUTION_CAPABILITIES.map((capability) => [capability, {
|
|
263
|
+
status: "unsupported",
|
|
264
|
+
reason: "claude -p runs with no tools, hooks, MCP or resume"
|
|
265
|
+
}]));
|
|
266
|
+
capabilities.fresh_run = { status: "supported", evidence: "spawns one claude -p per call" };
|
|
267
|
+
capabilities.structured_output = { status: "supported", evidence: "--json-schema, then a local zod parse" };
|
|
268
|
+
capabilities.external_cancellation = { status: "supported", evidence: "abort kills the child process group" };
|
|
269
|
+
capabilities.token_reporting = { status: "supported", evidence: "usage and modelUsage from the JSON result" };
|
|
270
|
+
return {
|
|
271
|
+
harness: "claude-print",
|
|
272
|
+
adapter: { name: "claude-print", version: "1" },
|
|
273
|
+
capabilities,
|
|
274
|
+
limits: [{ unit: "milliseconds", scope: "execution", enforcement: "hard", assessment: {
|
|
275
|
+
status: "supported",
|
|
276
|
+
evidence: "inactivityTimeoutMs is a wall deadline that kills the child process group"
|
|
277
|
+
} }]
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function assertClaudePrintConfig(config) {
|
|
281
|
+
const field = CLAUDE_PRINT_UNSUPPORTED_OPTIONS.find((name) => config[name] !== void 0) ?? (config.tools?.length ? "tools" : void 0) ?? (config.settingSources?.length ? "settingSources" : void 0);
|
|
282
|
+
if (field) throw new TypeError(`${field} is not supported by the claude-print harness (no tools, hooks or MCP)`);
|
|
283
|
+
}
|
|
284
|
+
function resolveClaudeBin(env) {
|
|
285
|
+
if (env.CLAUDE_BIN) return env.CLAUDE_BIN;
|
|
286
|
+
for (const dir of (env.PATH ?? "").split(delimiter)) {
|
|
287
|
+
if (dir && isExecutableFile(join2(dir, "claude"))) return join2(dir, "claude");
|
|
288
|
+
}
|
|
289
|
+
return void 0;
|
|
290
|
+
}
|
|
291
|
+
function isExecutableFile(path) {
|
|
292
|
+
try {
|
|
293
|
+
accessSync(path, constants.X_OK);
|
|
294
|
+
return statSync(path).isFile();
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function buildClaudePrintArgs(config) {
|
|
300
|
+
const args = [
|
|
301
|
+
"-p",
|
|
302
|
+
"--output-format",
|
|
303
|
+
"json",
|
|
304
|
+
"--tools",
|
|
305
|
+
"",
|
|
306
|
+
"--strict-mcp-config",
|
|
307
|
+
"--mcp-config",
|
|
308
|
+
EMPTY_MCP_CONFIG,
|
|
309
|
+
"--setting-sources",
|
|
310
|
+
"",
|
|
311
|
+
"--max-turns",
|
|
312
|
+
String(claudePrintMaxTurns(config)),
|
|
313
|
+
"--max-budget-usd",
|
|
314
|
+
String(config.maxBudgetUsd)
|
|
315
|
+
];
|
|
316
|
+
if (config.model) args.push("--model", config.model);
|
|
317
|
+
if (config.systemPrompt !== void 0) args.push("--system-prompt", config.systemPrompt);
|
|
318
|
+
if (config.outputSchema) args.push("--json-schema", cliJsonSchema(config.outputSchema));
|
|
319
|
+
return args;
|
|
320
|
+
}
|
|
321
|
+
function claudePrintMaxTurns(config) {
|
|
322
|
+
return config.outputSchema ? Math.max(config.maxTurns, 2) : config.maxTurns;
|
|
323
|
+
}
|
|
324
|
+
function cliJsonSchema(schema) {
|
|
325
|
+
const body = { ...toJSONSchema(schema) };
|
|
326
|
+
delete body.$schema;
|
|
327
|
+
return JSON.stringify(body);
|
|
328
|
+
}
|
|
329
|
+
async function runClaudePrint(config, env, clock) {
|
|
330
|
+
if (config.signal?.aborted) return { ok: false, failure: aborted(config.signal) };
|
|
331
|
+
const bin = resolveClaudeBin(env);
|
|
332
|
+
if (!bin) return { ok: false, failure: { kind: "runtime_error", reason: "claude binary not found on PATH; set CLAUDE_BIN" } };
|
|
333
|
+
const child = spawn2(bin, buildClaudePrintArgs(config), {
|
|
334
|
+
cwd: config.cwd,
|
|
335
|
+
env,
|
|
336
|
+
detached: process.platform !== "win32",
|
|
337
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
338
|
+
});
|
|
339
|
+
const outcome = await supervise(child, config);
|
|
340
|
+
return settle(outcome, config, clock.now() - clock.startedAt);
|
|
341
|
+
}
|
|
342
|
+
function supervise(child, config) {
|
|
343
|
+
const timeoutMs = config.inactivityTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
344
|
+
const output = collectOutput(child);
|
|
345
|
+
let ended;
|
|
346
|
+
const stop = (failure) => {
|
|
347
|
+
ended ??= failure;
|
|
348
|
+
terminateProcessGroup(child, "SIGTERM");
|
|
349
|
+
setTimeout(() => terminateProcessGroup(child, "SIGKILL"), CLAUDE_PRINT_KILL_GRACE_MS).unref();
|
|
350
|
+
};
|
|
351
|
+
const timer = setTimeout(() => stop({ kind: "inactivity_timeout", reason: `claude -p ran past ${timeoutMs}ms`, timeoutMs }), timeoutMs);
|
|
352
|
+
const onAbort = () => stop(aborted(config.signal));
|
|
353
|
+
config.signal?.addEventListener("abort", onAbort, { once: true });
|
|
354
|
+
child.stdin?.on("error", () => {
|
|
355
|
+
});
|
|
356
|
+
child.stdin?.end(config.prompt);
|
|
357
|
+
return new Promise((resolve) => {
|
|
358
|
+
const finish = (code, signal, spawnError) => {
|
|
359
|
+
clearTimeout(timer);
|
|
360
|
+
config.signal?.removeEventListener("abort", onAbort);
|
|
361
|
+
resolve({ code, signal, ...output(), ...spawnError ? { spawnError } : {}, ...ended ? { ended } : {} });
|
|
362
|
+
};
|
|
363
|
+
child.once("error", (error) => finish(null, null, error));
|
|
364
|
+
child.once("close", (code, signal) => finish(code, signal));
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function collectOutput(child) {
|
|
368
|
+
const stdout = [];
|
|
369
|
+
const stderr = [];
|
|
370
|
+
child.stdout?.on("data", (chunk) => stdout.push(chunk));
|
|
371
|
+
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
372
|
+
return () => ({ stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8") });
|
|
373
|
+
}
|
|
374
|
+
function settle(outcome, config, durationMs) {
|
|
375
|
+
if (outcome.ended) return { ok: false, failure: outcome.ended };
|
|
376
|
+
if (outcome.spawnError) return { ok: false, failure: { kind: "runtime_error", reason: `claude -p failed to start: ${outcome.spawnError.message}`, raw: outcome.spawnError } };
|
|
377
|
+
const result = parseResult(outcome.stdout);
|
|
378
|
+
if (!result) return { ok: false, failure: exitFailure(outcome, "printed no JSON result") };
|
|
379
|
+
config.onMessage?.(result);
|
|
380
|
+
const usage = usageFromResult(result, durationMs);
|
|
381
|
+
const failure = loginFailure(result) ?? maxTurnsFailure(result, config) ?? classifyResult(result) ?? (outcome.code === 0 ? void 0 : exitFailure(outcome, "reported success"));
|
|
382
|
+
const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
|
|
383
|
+
if (failure) return { ok: false, failure, ...sessionId ? { sessionId } : {}, usage };
|
|
384
|
+
if (!sessionId) return { ok: false, failure: { kind: "runtime_error", reason: "the JSON result carried no session_id", raw: result }, usage };
|
|
385
|
+
const output = readOutput(result, config);
|
|
386
|
+
if ("failure" in output) return { ok: false, failure: output.failure, sessionId, usage };
|
|
387
|
+
return { ok: true, output: output.value, sessionId, usage, init: initFrom(result) };
|
|
388
|
+
}
|
|
389
|
+
function parseResult(stdout) {
|
|
390
|
+
try {
|
|
391
|
+
const parsed = JSON.parse(stdout);
|
|
392
|
+
const isResult = typeof parsed === "object" && parsed !== null && parsed.type === "result";
|
|
393
|
+
return isResult ? parsed : void 0;
|
|
394
|
+
} catch {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function exitFailure(outcome, what) {
|
|
399
|
+
const exit = outcome.signal ? `signal ${outcome.signal}` : `code ${String(outcome.code)}`;
|
|
400
|
+
const stderr = outcome.stderr.trim();
|
|
401
|
+
return { kind: "runtime_error", reason: `claude -p exited with ${exit} and ${what}${stderr ? `: ${stderr}` : ""}`, raw: outcome };
|
|
402
|
+
}
|
|
403
|
+
function loginFailure(result) {
|
|
404
|
+
const text = result.subtype === "success" ? result.result : (result.errors ?? []).join("\n");
|
|
405
|
+
if (!result.is_error || !LOGIN_FAILURE.test(text)) return void 0;
|
|
406
|
+
return { kind: "auth_misconfigured", reason: text, hint: "run `claude` once interactively and /login", raw: result };
|
|
407
|
+
}
|
|
408
|
+
function maxTurnsFailure(result, config) {
|
|
409
|
+
if (result.subtype !== "error_max_turns") return void 0;
|
|
410
|
+
const text = (result.errors ?? []).join("\n") || "max turns reached";
|
|
411
|
+
return { kind: "runtime_error", reason: `claude -p reached --max-turns ${claudePrintMaxTurns(config)}: ${text}`, raw: result };
|
|
412
|
+
}
|
|
413
|
+
function readOutput(result, config) {
|
|
414
|
+
const text = result.subtype === "success" ? result.result : "";
|
|
415
|
+
if (!config.outputSchema) return { value: text };
|
|
416
|
+
const raw = (result.subtype === "success" ? result.structured_output : void 0) ?? parseJson(text);
|
|
417
|
+
const parsed = config.outputSchema.safeParse(raw);
|
|
418
|
+
if (parsed.success) return { value: parsed.data };
|
|
419
|
+
return { failure: { kind: "schema_invalid", reason: "structured output did not match the supplied schema", error: parsed.error, raw } };
|
|
420
|
+
}
|
|
421
|
+
function parseJson(text) {
|
|
422
|
+
try {
|
|
423
|
+
return JSON.parse(text);
|
|
424
|
+
} catch {
|
|
425
|
+
return void 0;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function initFrom(result) {
|
|
429
|
+
const models = Object.keys(result.modelUsage ?? {});
|
|
430
|
+
return { apiKeySource: void 0, model: models.length === 1 ? models[0] : void 0, tools: [], permissionMode: void 0, claudeCodeVersion: void 0 };
|
|
431
|
+
}
|
|
432
|
+
function aborted(signal) {
|
|
433
|
+
return { kind: "aborted", reason: String(signal?.reason ?? "aborted by caller") };
|
|
434
|
+
}
|
|
435
|
+
|
|
149
436
|
// src/run.ts
|
|
150
437
|
var DEFAULT_INACTIVITY_MS = 6e5;
|
|
151
438
|
async function runAgent(config, deps = {}) {
|
|
152
439
|
assertBudgets(config);
|
|
440
|
+
if (config.harness === "claude-print") assertClaudePrintConfig(config);
|
|
153
441
|
const now = deps.now ?? Date.now;
|
|
154
442
|
const startedAt = now();
|
|
443
|
+
const print = config.harness === "claude-print";
|
|
155
444
|
const env = prepareEnv(deps.env ?? process.env, { allowApiKeyBilling: config.allowApiKeyBilling });
|
|
156
445
|
try {
|
|
157
|
-
assertAuthEnvOk(env, { allowApiKeyBilling: config.allowApiKeyBilling });
|
|
446
|
+
assertAuthEnvOk(env, { allowApiKeyBilling: config.allowApiKeyBilling, requireOAuthToken: !print });
|
|
158
447
|
} catch (error) {
|
|
159
448
|
return { ok: false, failure: authFailure(error) };
|
|
160
449
|
}
|
|
450
|
+
if (print) return runClaudePrint(config, env, { now, startedAt });
|
|
161
451
|
const session = new Session(config, env, deps);
|
|
162
452
|
try {
|
|
163
453
|
return await session.run(startedAt);
|
|
@@ -251,7 +541,7 @@ var Session = class {
|
|
|
251
541
|
if (c.mcpServers) options.mcpServers = c.mcpServers;
|
|
252
542
|
if (c.hooks) options.hooks = c.hooks;
|
|
253
543
|
if (c.outputSchema) {
|
|
254
|
-
options.outputFormat = { type: "json_schema", schema:
|
|
544
|
+
options.outputFormat = { type: "json_schema", schema: toJSONSchema2(c.outputSchema) };
|
|
255
545
|
}
|
|
256
546
|
return options;
|
|
257
547
|
}
|
|
@@ -348,24 +638,6 @@ function describeEnd(reason) {
|
|
|
348
638
|
return reason.kind === "aborted" ? reason.reason : `no message for ${reason.timeoutMs}ms`;
|
|
349
639
|
}
|
|
350
640
|
|
|
351
|
-
// src/harness-contracts.ts
|
|
352
|
-
var CODEX_SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
353
|
-
var CODEX_APPROVAL_POLICIES = ["untrusted", "on-failure", "on-request", "never"];
|
|
354
|
-
var EXECUTION_CAPABILITIES = [
|
|
355
|
-
"fresh_run",
|
|
356
|
-
"resume",
|
|
357
|
-
"fork",
|
|
358
|
-
"structured_output",
|
|
359
|
-
"interactive_approvals",
|
|
360
|
-
"external_cancellation",
|
|
361
|
-
"semantic_interrupt",
|
|
362
|
-
"active_turn_steering",
|
|
363
|
-
"idle_turn_submission",
|
|
364
|
-
"persisted_transcript",
|
|
365
|
-
"process_reattachment",
|
|
366
|
-
"token_reporting"
|
|
367
|
-
];
|
|
368
|
-
|
|
369
641
|
// src/preflight.ts
|
|
370
642
|
import { conversationRef } from "@titan-design/agent-protocol";
|
|
371
643
|
function preflightHarnessRun(request, descriptor) {
|
|
@@ -660,7 +932,7 @@ var CodexExecSession = class {
|
|
|
660
932
|
if (this.stopReason === "timeout") return this.failure(timeoutFailure(this.request.wallTimeMs));
|
|
661
933
|
if (this.stopReason === "abort") return this.failure(abortFailure(this.request.signal));
|
|
662
934
|
if (this.streamError) return this.failure(this.streamError);
|
|
663
|
-
if (exit.exitCode !== 0) return this.failure(
|
|
935
|
+
if (exit.exitCode !== 0) return this.failure(exitFailure2(exit, this.stderr));
|
|
664
936
|
return this.success();
|
|
665
937
|
}
|
|
666
938
|
success() {
|
|
@@ -811,7 +1083,7 @@ function cancelledUnknown(cause, request, exit) {
|
|
|
811
1083
|
native: { cause, requested, processExit: exit }
|
|
812
1084
|
};
|
|
813
1085
|
}
|
|
814
|
-
function
|
|
1086
|
+
function exitFailure2(exit, stderr) {
|
|
815
1087
|
return {
|
|
816
1088
|
kind: "runtime_error",
|
|
817
1089
|
reason: stderr.trim() || `Codex exited with status ${String(exit.exitCode)}`,
|
|
@@ -830,80 +1102,6 @@ function messageOf3(error) {
|
|
|
830
1102
|
return error instanceof Error ? error.message : String(error);
|
|
831
1103
|
}
|
|
832
1104
|
|
|
833
|
-
// src/codex-process.ts
|
|
834
|
-
import { execFile, spawn } from "child_process";
|
|
835
|
-
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
836
|
-
import { tmpdir } from "os";
|
|
837
|
-
import { join } from "path";
|
|
838
|
-
import { createInterface } from "readline";
|
|
839
|
-
async function inspectCodexVersion(executablePath, env, options) {
|
|
840
|
-
return new Promise((resolve, reject) => {
|
|
841
|
-
execFile(
|
|
842
|
-
executablePath,
|
|
843
|
-
["--version"],
|
|
844
|
-
{
|
|
845
|
-
env,
|
|
846
|
-
encoding: "utf8",
|
|
847
|
-
signal: options.signal,
|
|
848
|
-
timeout: Math.max(1, Math.ceil(options.timeoutMs)),
|
|
849
|
-
killSignal: "SIGKILL"
|
|
850
|
-
},
|
|
851
|
-
(error, stdout) => {
|
|
852
|
-
if (error) reject(error);
|
|
853
|
-
else resolve(stdout);
|
|
854
|
-
}
|
|
855
|
-
);
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
function startCodexProcess(input) {
|
|
859
|
-
const child = spawn(input.executablePath, input.args, {
|
|
860
|
-
cwd: input.cwd,
|
|
861
|
-
env: input.env,
|
|
862
|
-
detached: process.platform !== "win32",
|
|
863
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
864
|
-
});
|
|
865
|
-
return {
|
|
866
|
-
stdout: lines(child.stdout),
|
|
867
|
-
stderr: lines(child.stderr),
|
|
868
|
-
completed: completionOf(child),
|
|
869
|
-
terminate: (signal) => terminateProcessGroup(child, signal)
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
async function createCodexSchemaFile(schema) {
|
|
873
|
-
const directory = await mkdtemp(join(tmpdir(), "titan-codex-schema-"));
|
|
874
|
-
const path = join(directory, "output-schema.json");
|
|
875
|
-
try {
|
|
876
|
-
await writeFile(path, JSON.stringify(schema), { encoding: "utf8", mode: 384 });
|
|
877
|
-
return { path, dispose: () => rm(directory, { recursive: true, force: true }) };
|
|
878
|
-
} catch (error) {
|
|
879
|
-
await rm(directory, { recursive: true, force: true });
|
|
880
|
-
throw error;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
async function* lines(stream) {
|
|
884
|
-
const reader = createInterface({ input: stream });
|
|
885
|
-
for await (const line of reader) yield line;
|
|
886
|
-
}
|
|
887
|
-
function completionOf(child) {
|
|
888
|
-
return new Promise((resolve, reject) => {
|
|
889
|
-
child.once("error", reject);
|
|
890
|
-
child.once("close", (exitCode, signal) => resolve({ exitCode, signal }));
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
function terminateProcessGroup(child, signal) {
|
|
894
|
-
if (child.pid && process.platform !== "win32") {
|
|
895
|
-
try {
|
|
896
|
-
process.kill(-child.pid, signal);
|
|
897
|
-
return;
|
|
898
|
-
} catch {
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
try {
|
|
902
|
-
child.kill(signal);
|
|
903
|
-
} catch {
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
1105
|
// src/codex-exec.ts
|
|
908
1106
|
var DEFAULT_CODEX_EXECUTABLE = "/Applications/ChatGPT.app/Contents/Resources/codex";
|
|
909
1107
|
var SUPPORTED_CODEX_EXEC_VERSION = "0.154.0-alpha.6.1";
|
|
@@ -1792,6 +1990,8 @@ export {
|
|
|
1792
1990
|
ANTI_NESTING_VARS,
|
|
1793
1991
|
AuthMisconfiguredError,
|
|
1794
1992
|
CLAUDE_CODE_PASSTHROUGH_VARS,
|
|
1993
|
+
CLAUDE_PRINT_KILL_GRACE_MS,
|
|
1994
|
+
CLAUDE_PRINT_UNSUPPORTED_OPTIONS,
|
|
1795
1995
|
CODEX_APPROVAL_POLICIES,
|
|
1796
1996
|
CODEX_SANDBOXES,
|
|
1797
1997
|
DEFAULT_CODEX_EXECUTABLE,
|
|
@@ -1806,9 +2006,11 @@ export {
|
|
|
1806
2006
|
SUPPORTED_CODEX_EXEC_VERSION,
|
|
1807
2007
|
assertApiKeySourceAllowed,
|
|
1808
2008
|
assertAuthEnvOk,
|
|
2009
|
+
buildClaudePrintArgs,
|
|
1809
2010
|
buildCodexExecArgs,
|
|
1810
2011
|
classifyResult,
|
|
1811
2012
|
claudeCodeCapabilities,
|
|
2013
|
+
claudePrintCapabilities,
|
|
1812
2014
|
codexExecCapabilities,
|
|
1813
2015
|
createClaudeCodeAdapter,
|
|
1814
2016
|
createCodexExecAdapter,
|
|
@@ -1817,6 +2019,7 @@ export {
|
|
|
1817
2019
|
preflightHarnessRun,
|
|
1818
2020
|
prepareCodexEnv,
|
|
1819
2021
|
prepareEnv,
|
|
2022
|
+
resolveClaudeBin,
|
|
1820
2023
|
runAgent,
|
|
1821
2024
|
sumModelCost,
|
|
1822
2025
|
usageFromResult
|