@titan-design/agent 0.2.0 → 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 +53 -5
- package/dist/index.d.ts +139 -118
- package/dist/index.js +295 -98
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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,220 @@ 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 one turn with no tools, hooks, MCP or resume"
|
|
265
|
+
}]));
|
|
266
|
+
capabilities.fresh_run = { status: "supported", evidence: "spawns claude -p --max-turns 1 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 (one turn, 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
|
+
"1",
|
|
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 cliJsonSchema(schema) {
|
|
322
|
+
const body = { ...toJSONSchema(schema) };
|
|
323
|
+
delete body.$schema;
|
|
324
|
+
return JSON.stringify(body);
|
|
325
|
+
}
|
|
326
|
+
async function runClaudePrint(config, env, clock) {
|
|
327
|
+
if (config.signal?.aborted) return { ok: false, failure: aborted(config.signal) };
|
|
328
|
+
const bin = resolveClaudeBin(env);
|
|
329
|
+
if (!bin) return { ok: false, failure: { kind: "runtime_error", reason: "claude binary not found on PATH; set CLAUDE_BIN" } };
|
|
330
|
+
const child = spawn2(bin, buildClaudePrintArgs(config), {
|
|
331
|
+
cwd: config.cwd,
|
|
332
|
+
env,
|
|
333
|
+
detached: process.platform !== "win32",
|
|
334
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
335
|
+
});
|
|
336
|
+
const outcome = await supervise(child, config);
|
|
337
|
+
return settle(outcome, config, clock.now() - clock.startedAt);
|
|
338
|
+
}
|
|
339
|
+
function supervise(child, config) {
|
|
340
|
+
const timeoutMs = config.inactivityTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
341
|
+
const output = collectOutput(child);
|
|
342
|
+
let ended;
|
|
343
|
+
const stop = (failure) => {
|
|
344
|
+
ended ??= failure;
|
|
345
|
+
terminateProcessGroup(child, "SIGTERM");
|
|
346
|
+
setTimeout(() => terminateProcessGroup(child, "SIGKILL"), CLAUDE_PRINT_KILL_GRACE_MS).unref();
|
|
347
|
+
};
|
|
348
|
+
const timer = setTimeout(() => stop({ kind: "inactivity_timeout", reason: `claude -p ran past ${timeoutMs}ms`, timeoutMs }), timeoutMs);
|
|
349
|
+
const onAbort = () => stop(aborted(config.signal));
|
|
350
|
+
config.signal?.addEventListener("abort", onAbort, { once: true });
|
|
351
|
+
child.stdin?.on("error", () => {
|
|
352
|
+
});
|
|
353
|
+
child.stdin?.end(config.prompt);
|
|
354
|
+
return new Promise((resolve) => {
|
|
355
|
+
const finish = (code, signal, spawnError) => {
|
|
356
|
+
clearTimeout(timer);
|
|
357
|
+
config.signal?.removeEventListener("abort", onAbort);
|
|
358
|
+
resolve({ code, signal, ...output(), ...spawnError ? { spawnError } : {}, ...ended ? { ended } : {} });
|
|
359
|
+
};
|
|
360
|
+
child.once("error", (error) => finish(null, null, error));
|
|
361
|
+
child.once("close", (code, signal) => finish(code, signal));
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function collectOutput(child) {
|
|
365
|
+
const stdout = [];
|
|
366
|
+
const stderr = [];
|
|
367
|
+
child.stdout?.on("data", (chunk) => stdout.push(chunk));
|
|
368
|
+
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
369
|
+
return () => ({ stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8") });
|
|
370
|
+
}
|
|
371
|
+
function settle(outcome, config, durationMs) {
|
|
372
|
+
if (outcome.ended) return { ok: false, failure: outcome.ended };
|
|
373
|
+
if (outcome.spawnError) return { ok: false, failure: { kind: "runtime_error", reason: `claude -p failed to start: ${outcome.spawnError.message}`, raw: outcome.spawnError } };
|
|
374
|
+
const result = parseResult(outcome.stdout);
|
|
375
|
+
if (!result) return { ok: false, failure: exitFailure(outcome, "printed no JSON result") };
|
|
376
|
+
config.onMessage?.(result);
|
|
377
|
+
const usage = usageFromResult(result, durationMs);
|
|
378
|
+
const failure = loginFailure(result) ?? classifyResult(result) ?? (outcome.code === 0 ? void 0 : exitFailure(outcome, "reported success"));
|
|
379
|
+
const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
|
|
380
|
+
if (failure) return { ok: false, failure, ...sessionId ? { sessionId } : {}, usage };
|
|
381
|
+
if (!sessionId) return { ok: false, failure: { kind: "runtime_error", reason: "the JSON result carried no session_id", raw: result }, usage };
|
|
382
|
+
const output = readOutput(result, config);
|
|
383
|
+
if ("failure" in output) return { ok: false, failure: output.failure, sessionId, usage };
|
|
384
|
+
return { ok: true, output: output.value, sessionId, usage, init: initFrom(result) };
|
|
385
|
+
}
|
|
386
|
+
function parseResult(stdout) {
|
|
387
|
+
try {
|
|
388
|
+
const parsed = JSON.parse(stdout);
|
|
389
|
+
const isResult = typeof parsed === "object" && parsed !== null && parsed.type === "result";
|
|
390
|
+
return isResult ? parsed : void 0;
|
|
391
|
+
} catch {
|
|
392
|
+
return void 0;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function exitFailure(outcome, what) {
|
|
396
|
+
const exit = outcome.signal ? `signal ${outcome.signal}` : `code ${String(outcome.code)}`;
|
|
397
|
+
const stderr = outcome.stderr.trim();
|
|
398
|
+
return { kind: "runtime_error", reason: `claude -p exited with ${exit} and ${what}${stderr ? `: ${stderr}` : ""}`, raw: outcome };
|
|
399
|
+
}
|
|
400
|
+
function loginFailure(result) {
|
|
401
|
+
const text = result.subtype === "success" ? result.result : (result.errors ?? []).join("\n");
|
|
402
|
+
if (!result.is_error || !LOGIN_FAILURE.test(text)) return void 0;
|
|
403
|
+
return { kind: "auth_misconfigured", reason: text, hint: "run `claude` once interactively and /login", raw: result };
|
|
404
|
+
}
|
|
405
|
+
function readOutput(result, config) {
|
|
406
|
+
const text = result.subtype === "success" ? result.result : "";
|
|
407
|
+
if (!config.outputSchema) return { value: text };
|
|
408
|
+
const raw = (result.subtype === "success" ? result.structured_output : void 0) ?? parseJson(text);
|
|
409
|
+
const parsed = config.outputSchema.safeParse(raw);
|
|
410
|
+
if (parsed.success) return { value: parsed.data };
|
|
411
|
+
return { failure: { kind: "schema_invalid", reason: "structured output did not match the supplied schema", error: parsed.error, raw } };
|
|
412
|
+
}
|
|
413
|
+
function parseJson(text) {
|
|
414
|
+
try {
|
|
415
|
+
return JSON.parse(text);
|
|
416
|
+
} catch {
|
|
417
|
+
return void 0;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function initFrom(result) {
|
|
421
|
+
const models = Object.keys(result.modelUsage ?? {});
|
|
422
|
+
return { apiKeySource: void 0, model: models.length === 1 ? models[0] : void 0, tools: [], permissionMode: void 0, claudeCodeVersion: void 0 };
|
|
423
|
+
}
|
|
424
|
+
function aborted(signal) {
|
|
425
|
+
return { kind: "aborted", reason: String(signal?.reason ?? "aborted by caller") };
|
|
426
|
+
}
|
|
427
|
+
|
|
149
428
|
// src/run.ts
|
|
150
429
|
var DEFAULT_INACTIVITY_MS = 6e5;
|
|
151
430
|
async function runAgent(config, deps = {}) {
|
|
152
431
|
assertBudgets(config);
|
|
432
|
+
if (config.harness === "claude-print") assertClaudePrintConfig(config);
|
|
153
433
|
const now = deps.now ?? Date.now;
|
|
154
434
|
const startedAt = now();
|
|
435
|
+
const print = config.harness === "claude-print";
|
|
155
436
|
const env = prepareEnv(deps.env ?? process.env, { allowApiKeyBilling: config.allowApiKeyBilling });
|
|
156
437
|
try {
|
|
157
|
-
assertAuthEnvOk(env, { allowApiKeyBilling: config.allowApiKeyBilling });
|
|
438
|
+
assertAuthEnvOk(env, { allowApiKeyBilling: config.allowApiKeyBilling, requireOAuthToken: !print });
|
|
158
439
|
} catch (error) {
|
|
159
440
|
return { ok: false, failure: authFailure(error) };
|
|
160
441
|
}
|
|
442
|
+
if (print) return runClaudePrint(config, env, { now, startedAt });
|
|
161
443
|
const session = new Session(config, env, deps);
|
|
162
444
|
try {
|
|
163
445
|
return await session.run(startedAt);
|
|
@@ -242,6 +524,8 @@ var Session = class {
|
|
|
242
524
|
settingSources: c.settingSources ?? []
|
|
243
525
|
};
|
|
244
526
|
if (c.model) options.model = c.model;
|
|
527
|
+
if (c.tools) options.tools = c.tools;
|
|
528
|
+
if (c.systemPrompt !== void 0) options.systemPrompt = c.systemPrompt;
|
|
245
529
|
if (c.allowedTools) options.allowedTools = c.allowedTools;
|
|
246
530
|
if (c.disallowedTools) options.disallowedTools = c.disallowedTools;
|
|
247
531
|
if (c.resumeSessionId) options.resume = c.resumeSessionId;
|
|
@@ -249,7 +533,7 @@ var Session = class {
|
|
|
249
533
|
if (c.mcpServers) options.mcpServers = c.mcpServers;
|
|
250
534
|
if (c.hooks) options.hooks = c.hooks;
|
|
251
535
|
if (c.outputSchema) {
|
|
252
|
-
options.outputFormat = { type: "json_schema", schema:
|
|
536
|
+
options.outputFormat = { type: "json_schema", schema: toJSONSchema2(c.outputSchema) };
|
|
253
537
|
}
|
|
254
538
|
return options;
|
|
255
539
|
}
|
|
@@ -346,24 +630,6 @@ function describeEnd(reason) {
|
|
|
346
630
|
return reason.kind === "aborted" ? reason.reason : `no message for ${reason.timeoutMs}ms`;
|
|
347
631
|
}
|
|
348
632
|
|
|
349
|
-
// src/harness-contracts.ts
|
|
350
|
-
var CODEX_SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
351
|
-
var CODEX_APPROVAL_POLICIES = ["untrusted", "on-failure", "on-request", "never"];
|
|
352
|
-
var EXECUTION_CAPABILITIES = [
|
|
353
|
-
"fresh_run",
|
|
354
|
-
"resume",
|
|
355
|
-
"fork",
|
|
356
|
-
"structured_output",
|
|
357
|
-
"interactive_approvals",
|
|
358
|
-
"external_cancellation",
|
|
359
|
-
"semantic_interrupt",
|
|
360
|
-
"active_turn_steering",
|
|
361
|
-
"idle_turn_submission",
|
|
362
|
-
"persisted_transcript",
|
|
363
|
-
"process_reattachment",
|
|
364
|
-
"token_reporting"
|
|
365
|
-
];
|
|
366
|
-
|
|
367
633
|
// src/preflight.ts
|
|
368
634
|
import { conversationRef } from "@titan-design/agent-protocol";
|
|
369
635
|
function preflightHarnessRun(request, descriptor) {
|
|
@@ -658,7 +924,7 @@ var CodexExecSession = class {
|
|
|
658
924
|
if (this.stopReason === "timeout") return this.failure(timeoutFailure(this.request.wallTimeMs));
|
|
659
925
|
if (this.stopReason === "abort") return this.failure(abortFailure(this.request.signal));
|
|
660
926
|
if (this.streamError) return this.failure(this.streamError);
|
|
661
|
-
if (exit.exitCode !== 0) return this.failure(
|
|
927
|
+
if (exit.exitCode !== 0) return this.failure(exitFailure2(exit, this.stderr));
|
|
662
928
|
return this.success();
|
|
663
929
|
}
|
|
664
930
|
success() {
|
|
@@ -809,7 +1075,7 @@ function cancelledUnknown(cause, request, exit) {
|
|
|
809
1075
|
native: { cause, requested, processExit: exit }
|
|
810
1076
|
};
|
|
811
1077
|
}
|
|
812
|
-
function
|
|
1078
|
+
function exitFailure2(exit, stderr) {
|
|
813
1079
|
return {
|
|
814
1080
|
kind: "runtime_error",
|
|
815
1081
|
reason: stderr.trim() || `Codex exited with status ${String(exit.exitCode)}`,
|
|
@@ -828,80 +1094,6 @@ function messageOf3(error) {
|
|
|
828
1094
|
return error instanceof Error ? error.message : String(error);
|
|
829
1095
|
}
|
|
830
1096
|
|
|
831
|
-
// src/codex-process.ts
|
|
832
|
-
import { execFile, spawn } from "child_process";
|
|
833
|
-
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
834
|
-
import { tmpdir } from "os";
|
|
835
|
-
import { join } from "path";
|
|
836
|
-
import { createInterface } from "readline";
|
|
837
|
-
async function inspectCodexVersion(executablePath, env, options) {
|
|
838
|
-
return new Promise((resolve, reject) => {
|
|
839
|
-
execFile(
|
|
840
|
-
executablePath,
|
|
841
|
-
["--version"],
|
|
842
|
-
{
|
|
843
|
-
env,
|
|
844
|
-
encoding: "utf8",
|
|
845
|
-
signal: options.signal,
|
|
846
|
-
timeout: Math.max(1, Math.ceil(options.timeoutMs)),
|
|
847
|
-
killSignal: "SIGKILL"
|
|
848
|
-
},
|
|
849
|
-
(error, stdout) => {
|
|
850
|
-
if (error) reject(error);
|
|
851
|
-
else resolve(stdout);
|
|
852
|
-
}
|
|
853
|
-
);
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
function startCodexProcess(input) {
|
|
857
|
-
const child = spawn(input.executablePath, input.args, {
|
|
858
|
-
cwd: input.cwd,
|
|
859
|
-
env: input.env,
|
|
860
|
-
detached: process.platform !== "win32",
|
|
861
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
862
|
-
});
|
|
863
|
-
return {
|
|
864
|
-
stdout: lines(child.stdout),
|
|
865
|
-
stderr: lines(child.stderr),
|
|
866
|
-
completed: completionOf(child),
|
|
867
|
-
terminate: (signal) => terminateProcessGroup(child, signal)
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
async function createCodexSchemaFile(schema) {
|
|
871
|
-
const directory = await mkdtemp(join(tmpdir(), "titan-codex-schema-"));
|
|
872
|
-
const path = join(directory, "output-schema.json");
|
|
873
|
-
try {
|
|
874
|
-
await writeFile(path, JSON.stringify(schema), { encoding: "utf8", mode: 384 });
|
|
875
|
-
return { path, dispose: () => rm(directory, { recursive: true, force: true }) };
|
|
876
|
-
} catch (error) {
|
|
877
|
-
await rm(directory, { recursive: true, force: true });
|
|
878
|
-
throw error;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
async function* lines(stream) {
|
|
882
|
-
const reader = createInterface({ input: stream });
|
|
883
|
-
for await (const line of reader) yield line;
|
|
884
|
-
}
|
|
885
|
-
function completionOf(child) {
|
|
886
|
-
return new Promise((resolve, reject) => {
|
|
887
|
-
child.once("error", reject);
|
|
888
|
-
child.once("close", (exitCode, signal) => resolve({ exitCode, signal }));
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
function terminateProcessGroup(child, signal) {
|
|
892
|
-
if (child.pid && process.platform !== "win32") {
|
|
893
|
-
try {
|
|
894
|
-
process.kill(-child.pid, signal);
|
|
895
|
-
return;
|
|
896
|
-
} catch {
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
try {
|
|
900
|
-
child.kill(signal);
|
|
901
|
-
} catch {
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
1097
|
// src/codex-exec.ts
|
|
906
1098
|
var DEFAULT_CODEX_EXECUTABLE = "/Applications/ChatGPT.app/Contents/Resources/codex";
|
|
907
1099
|
var SUPPORTED_CODEX_EXEC_VERSION = "0.154.0-alpha.6.1";
|
|
@@ -1790,6 +1982,8 @@ export {
|
|
|
1790
1982
|
ANTI_NESTING_VARS,
|
|
1791
1983
|
AuthMisconfiguredError,
|
|
1792
1984
|
CLAUDE_CODE_PASSTHROUGH_VARS,
|
|
1985
|
+
CLAUDE_PRINT_KILL_GRACE_MS,
|
|
1986
|
+
CLAUDE_PRINT_UNSUPPORTED_OPTIONS,
|
|
1793
1987
|
CODEX_APPROVAL_POLICIES,
|
|
1794
1988
|
CODEX_SANDBOXES,
|
|
1795
1989
|
DEFAULT_CODEX_EXECUTABLE,
|
|
@@ -1804,9 +1998,11 @@ export {
|
|
|
1804
1998
|
SUPPORTED_CODEX_EXEC_VERSION,
|
|
1805
1999
|
assertApiKeySourceAllowed,
|
|
1806
2000
|
assertAuthEnvOk,
|
|
2001
|
+
buildClaudePrintArgs,
|
|
1807
2002
|
buildCodexExecArgs,
|
|
1808
2003
|
classifyResult,
|
|
1809
2004
|
claudeCodeCapabilities,
|
|
2005
|
+
claudePrintCapabilities,
|
|
1810
2006
|
codexExecCapabilities,
|
|
1811
2007
|
createClaudeCodeAdapter,
|
|
1812
2008
|
createCodexExecAdapter,
|
|
@@ -1815,6 +2011,7 @@ export {
|
|
|
1815
2011
|
preflightHarnessRun,
|
|
1816
2012
|
prepareCodexEnv,
|
|
1817
2013
|
prepareEnv,
|
|
2014
|
+
resolveClaudeBin,
|
|
1818
2015
|
runAgent,
|
|
1819
2016
|
sumModelCost,
|
|
1820
2017
|
usageFromResult
|