hankweave 0.5.7 → 0.6.2
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 +12 -11
- package/dist/base-process-manager.d.ts +30 -0
- package/dist/budget.d.ts +315 -0
- package/dist/checkpoint-git.d.ts +98 -0
- package/dist/claude-agent-sdk-manager.d.ts +144 -0
- package/dist/claude-log-parser.d.ts +63 -0
- package/dist/claude-runtime-extractor.d.ts +73 -0
- package/dist/codex-runtime-extractor.d.ts +107 -0
- package/dist/codon-runner.d.ts +278 -0
- package/dist/config-validation/model-validator.d.ts +16 -0
- package/dist/config-validation/sentinel.schema.d.ts +6967 -0
- package/dist/config.d.ts +40815 -0
- package/dist/cost-tracker.d.ts +72 -0
- package/dist/execution-planner.d.ts +62 -0
- package/dist/execution-thread.d.ts +71 -0
- package/dist/exports/schemas.d.ts +9 -0
- package/dist/exports/schemas.js +1019 -0
- package/dist/exports/types.d.ts +15 -0
- package/dist/exports/types.js +60 -0
- package/dist/file-resolver.d.ts +33 -0
- package/dist/index.js +380 -293
- package/dist/index.js.map +33 -29
- package/dist/llm/llm-provider-registry.d.ts +207 -0
- package/dist/llm/models-dev-schema.d.ts +679 -0
- package/dist/llm/provider-config.d.ts +30 -0
- package/dist/prompt-builder.d.ts +75 -0
- package/dist/prompt-frontmatter.d.ts +61 -0
- package/dist/replay-process-manager.d.ts +82 -0
- package/dist/runtime-extractor-base.d.ts +120 -0
- package/dist/schemas/event-schemas.d.ts +8389 -0
- package/dist/schemas/websocket-log-schemas.d.ts +4502 -0
- package/dist/shim-process-manager.d.ts +98 -0
- package/dist/shim-runtime-extractor.d.ts +51 -0
- package/dist/shims/codex/README.md +129 -0
- package/dist/shims/codex/THIRDPARTY.md +18 -0
- package/dist/shims/codex/VERSION +1 -0
- package/dist/shims/codex/common/package.json +24 -0
- package/dist/shims/codex/index.js +1154 -970
- package/dist/shims/codex/package.json +46 -0
- package/dist/shims/codex/tsup.config.ts +16 -0
- package/dist/shims/gemini/README.md +59 -0
- package/dist/shims/gemini/THIRDPARTY.md +32 -0
- package/dist/shims/gemini/VERSION +1 -0
- package/dist/shims/gemini/common/package.json +24 -0
- package/dist/shims/gemini/index.js +1359 -30
- package/dist/shims/gemini/package.json +37 -0
- package/dist/shims/opencode/README.md +82 -0
- package/dist/shims/opencode/THIRDPARTY.md +32 -0
- package/dist/shims/opencode/VERSION +1 -0
- package/dist/shims/opencode/common/package.json +24 -0
- package/dist/shims/opencode/index.js +1476 -0
- package/dist/shims/opencode/package.json +38 -0
- package/dist/shims/pi/README.md +87 -0
- package/dist/shims/pi/THIRDPARTY.md +24 -0
- package/dist/shims/pi/VERSION +1 -0
- package/dist/shims/pi/common/package.json +24 -0
- package/dist/shims/pi/index.js +249832 -0
- package/dist/shims/pi/package.json +53 -0
- package/dist/state-manager.d.ts +161 -0
- package/dist/state-transition-guards.d.ts +37 -0
- package/dist/telemetry/telemetry-types.d.ts +206 -0
- package/dist/typed-event-emitter.d.ts +57 -0
- package/dist/types/branded-types.d.ts +15 -0
- package/dist/types/budget-types.d.ts +82 -0
- package/dist/types/claude-session-schema.d.ts +2430 -0
- package/dist/types/error-types.d.ts +44 -0
- package/dist/types/input-ai-types.d.ts +1070 -0
- package/dist/types/llm-call-types.d.ts +3829 -0
- package/dist/types/sentinel-types.d.ts +66 -0
- package/dist/types/state-types.d.ts +1099 -0
- package/dist/types/tool-types.d.ts +86 -0
- package/dist/types/types.d.ts +367 -0
- package/dist/types/websocket-log-types.d.ts +7 -0
- package/dist/utils.d.ts +452 -0
- package/package.json +15 -2
- package/schemas/hank.schema.json +158 -3
- package/schemas/hankweave.schema.json +17 -1
- package/shims/codex/index.js +0 -1583
- package/shims/gemini/index.js +0 -31
|
@@ -1,230 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../common/src/args.ts
|
|
4
|
-
var VALID_SANDBOX_LEVELS = ["none", "standard", "strict"];
|
|
5
|
-
function parseArgs(argv, aliases) {
|
|
6
|
-
const args = {
|
|
7
|
-
model: "",
|
|
8
|
-
verbose: false,
|
|
9
|
-
idleTimeout: 120,
|
|
10
|
-
sandbox: "none",
|
|
11
|
-
selfTest: false,
|
|
12
|
-
version: false,
|
|
13
|
-
help: false
|
|
14
|
-
};
|
|
15
|
-
for (let i = 0; i < argv.length; i++) {
|
|
16
|
-
let arg = argv[i];
|
|
17
|
-
if (arg.includes("=")) {
|
|
18
|
-
const [key, value] = arg.split("=", 2);
|
|
19
|
-
argv.splice(i, 1, key, value);
|
|
20
|
-
arg = key;
|
|
21
|
-
}
|
|
22
|
-
if (aliases && arg in aliases) {
|
|
23
|
-
arg = aliases[arg];
|
|
24
|
-
}
|
|
25
|
-
switch (arg) {
|
|
26
|
-
case "--model":
|
|
27
|
-
args.model = argv[++i];
|
|
28
|
-
break;
|
|
29
|
-
case "--resume":
|
|
30
|
-
args.resume = argv[++i];
|
|
31
|
-
break;
|
|
32
|
-
case "--verbose":
|
|
33
|
-
args.verbose = true;
|
|
34
|
-
break;
|
|
35
|
-
case "--append-system-prompt":
|
|
36
|
-
args.appendSystemPrompt = argv[++i];
|
|
37
|
-
break;
|
|
38
|
-
case "--debug-dir":
|
|
39
|
-
args.debugDir = argv[++i];
|
|
40
|
-
break;
|
|
41
|
-
case "--idle-timeout": {
|
|
42
|
-
const val = Number(argv[++i]);
|
|
43
|
-
if (!Number.isFinite(val) || val <= 0) {
|
|
44
|
-
console.error("Invalid --idle-timeout value: must be a positive number");
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
args.idleTimeout = val;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
case "--sandbox": {
|
|
51
|
-
const level = argv[++i];
|
|
52
|
-
if (!VALID_SANDBOX_LEVELS.includes(level)) {
|
|
53
|
-
console.error(
|
|
54
|
-
`Invalid --sandbox value: must be one of ${VALID_SANDBOX_LEVELS.join(", ")}`
|
|
55
|
-
);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
args.sandbox = level;
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
case "--self-test":
|
|
62
|
-
args.selfTest = true;
|
|
63
|
-
break;
|
|
64
|
-
case "--version":
|
|
65
|
-
args.version = true;
|
|
66
|
-
break;
|
|
67
|
-
case "--help":
|
|
68
|
-
args.help = true;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return args;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// src/selftest.ts
|
|
76
|
-
import { spawn } from "child_process";
|
|
77
|
-
import { existsSync as existsSync2 } from "fs";
|
|
78
|
-
import { isAbsolute } from "path";
|
|
79
|
-
|
|
80
|
-
// src/utils/auth.ts
|
|
81
|
-
import * as fs from "fs";
|
|
82
|
-
import * as os from "os";
|
|
83
|
-
import * as path from "path";
|
|
84
|
-
function getAuthFilePath() {
|
|
85
|
-
const homeDir = os.homedir();
|
|
86
|
-
return path.join(homeDir, ".codex", "auth.json");
|
|
87
|
-
}
|
|
88
|
-
function readAuthFile() {
|
|
89
|
-
try {
|
|
90
|
-
const authPath = getAuthFilePath();
|
|
91
|
-
if (!fs.existsSync(authPath)) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
return fs.readFileSync(authPath, "utf8");
|
|
95
|
-
} catch {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function getApiKey() {
|
|
100
|
-
const authFileKey = readAuthFile();
|
|
101
|
-
if (authFileKey) {
|
|
102
|
-
return { apiKey: authFileKey, source: "env" };
|
|
103
|
-
}
|
|
104
|
-
if (process.env.CODEX_API_KEY) {
|
|
105
|
-
return { apiKey: process.env.CODEX_API_KEY, source: "CODEX_API_KEY" };
|
|
106
|
-
}
|
|
107
|
-
if (process.env.OPENAI_API_KEY) {
|
|
108
|
-
return { apiKey: process.env.OPENAI_API_KEY, source: "OPENAI_API_KEY" };
|
|
109
|
-
}
|
|
110
|
-
return { apiKey: null, source: "none" };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// src/selftest.ts
|
|
114
|
-
async function isCodexInstalled() {
|
|
115
|
-
return new Promise((resolve) => {
|
|
116
|
-
const isWindows = process.platform === "win32";
|
|
117
|
-
const codexCommand = process.env.CODEX_PATH_OVERRIDE || "codex";
|
|
118
|
-
if (isAbsolute(codexCommand)) {
|
|
119
|
-
if (existsSync2(codexCommand)) {
|
|
120
|
-
const versionProc = spawn(codexCommand, ["--version"], { shell: isWindows });
|
|
121
|
-
let version = "unknown";
|
|
122
|
-
versionProc.stdout.on("data", (data) => {
|
|
123
|
-
version = data.toString().trim();
|
|
124
|
-
});
|
|
125
|
-
versionProc.on("close", () => {
|
|
126
|
-
resolve({ found: true, version });
|
|
127
|
-
});
|
|
128
|
-
} else {
|
|
129
|
-
resolve({ found: false, version: "N/A" });
|
|
130
|
-
}
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const whichCommand = isWindows ? "where" : "which";
|
|
134
|
-
const proc = spawn(whichCommand, [codexCommand], { shell: isWindows });
|
|
135
|
-
let found = false;
|
|
136
|
-
proc.on("close", (code) => {
|
|
137
|
-
if (code === 0) {
|
|
138
|
-
found = true;
|
|
139
|
-
}
|
|
140
|
-
if (found) {
|
|
141
|
-
const versionProc = spawn(codexCommand, ["--version"], { shell: isWindows });
|
|
142
|
-
let version = "unknown";
|
|
143
|
-
versionProc.stdout.on("data", (data) => {
|
|
144
|
-
version = data.toString().trim();
|
|
145
|
-
});
|
|
146
|
-
versionProc.on("close", () => {
|
|
147
|
-
resolve({ found: true, version });
|
|
148
|
-
});
|
|
149
|
-
} else {
|
|
150
|
-
resolve({ found: false, version: "N/A" });
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function checkApiKey() {
|
|
156
|
-
const authConfig = getApiKey();
|
|
157
|
-
if (authConfig.apiKey) {
|
|
158
|
-
const sourceMessages = {
|
|
159
|
-
env: "API key found in ~/.codex/auth.json",
|
|
160
|
-
CODEX_API_KEY: "CODEX_API_KEY found in environment",
|
|
161
|
-
OPENAI_API_KEY: "OPENAI_API_KEY found in environment",
|
|
162
|
-
none: "No API key found"
|
|
163
|
-
};
|
|
164
|
-
return {
|
|
165
|
-
passed: true,
|
|
166
|
-
message: sourceMessages[authConfig.source] || "API key found"
|
|
167
|
-
};
|
|
168
|
-
} else {
|
|
169
|
-
return {
|
|
170
|
-
passed: false,
|
|
171
|
-
message: "No API key found. Set CODEX_API_KEY or OPENAI_API_KEY environment variable, or create ~/.codex/auth.json with apiKey field."
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async function runSelfTest() {
|
|
176
|
-
const checks = [];
|
|
177
|
-
const codexStatus = await isCodexInstalled();
|
|
178
|
-
checks.push({
|
|
179
|
-
name: "codex_cli_found",
|
|
180
|
-
passed: codexStatus.found,
|
|
181
|
-
message: codexStatus.found ? `Codex CLI found (version: ${codexStatus.version})` : "Codex CLI not found in PATH. Install from https://developers.openai.com/codex/"
|
|
182
|
-
});
|
|
183
|
-
const apiKeyStatus = checkApiKey();
|
|
184
|
-
checks.push({
|
|
185
|
-
name: "api_key",
|
|
186
|
-
passed: apiKeyStatus.passed,
|
|
187
|
-
message: apiKeyStatus.message
|
|
188
|
-
});
|
|
189
|
-
const nodeVersion = process.version;
|
|
190
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
|
|
191
|
-
const nodeVersionOk = majorVersion >= 18;
|
|
192
|
-
checks.push({
|
|
193
|
-
name: "node_version",
|
|
194
|
-
passed: nodeVersionOk,
|
|
195
|
-
message: nodeVersionOk ? `Node.js version ${nodeVersion} is compatible` : `Node.js version ${nodeVersion} is too old. Requires Node.js 18+`
|
|
196
|
-
});
|
|
197
|
-
const allPassed = checks.every((c) => c.passed);
|
|
198
|
-
return {
|
|
199
|
-
shim: {
|
|
200
|
-
name: "codex-shim",
|
|
201
|
-
version: "1.0.0"
|
|
202
|
-
},
|
|
203
|
-
agent: {
|
|
204
|
-
name: "Codex",
|
|
205
|
-
version: codexStatus.version,
|
|
206
|
-
found: codexStatus.found
|
|
207
|
-
},
|
|
208
|
-
checks,
|
|
209
|
-
overall: {
|
|
210
|
-
passed: allPassed,
|
|
211
|
-
message: allPassed ? "All checks passed" : "Some checks failed"
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
3
|
// src/shim.ts
|
|
217
|
-
import
|
|
218
|
-
import
|
|
219
|
-
|
|
220
|
-
// ../../node_modules/.bun/@openai+codex-sdk@0.98.0/node_modules/@openai/codex-sdk/dist/index.js
|
|
221
|
-
import { promises as fs2 } from "fs";
|
|
4
|
+
import { spawn as spawn2 } from "child_process";
|
|
5
|
+
import fs4 from "fs";
|
|
222
6
|
import os2 from "os";
|
|
7
|
+
import path6 from "path";
|
|
8
|
+
|
|
9
|
+
// node_modules/@openai/codex-sdk/dist/index.js
|
|
10
|
+
import { promises as fs } from "fs";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { spawn } from "child_process";
|
|
223
14
|
import path2 from "path";
|
|
224
|
-
import { spawn as spawn2 } from "child_process";
|
|
225
|
-
import path22 from "path";
|
|
226
15
|
import readline from "readline";
|
|
227
|
-
import {
|
|
16
|
+
import { createRequire } from "module";
|
|
228
17
|
async function createOutputSchemaFile(schema) {
|
|
229
18
|
if (schema === void 0) {
|
|
230
19
|
return { cleanup: async () => {
|
|
@@ -233,16 +22,16 @@ async function createOutputSchemaFile(schema) {
|
|
|
233
22
|
if (!isJsonObject(schema)) {
|
|
234
23
|
throw new Error("outputSchema must be a plain JSON object");
|
|
235
24
|
}
|
|
236
|
-
const schemaDir = await
|
|
237
|
-
const schemaPath =
|
|
25
|
+
const schemaDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-output-schema-"));
|
|
26
|
+
const schemaPath = path.join(schemaDir, "schema.json");
|
|
238
27
|
const cleanup = async () => {
|
|
239
28
|
try {
|
|
240
|
-
await
|
|
29
|
+
await fs.rm(schemaDir, { recursive: true, force: true });
|
|
241
30
|
} catch {
|
|
242
31
|
}
|
|
243
32
|
};
|
|
244
33
|
try {
|
|
245
|
-
await
|
|
34
|
+
await fs.writeFile(schemaPath, JSON.stringify(schema), "utf8");
|
|
246
35
|
return { schemaPath, cleanup };
|
|
247
36
|
} catch (error) {
|
|
248
37
|
await cleanup();
|
|
@@ -355,6 +144,16 @@ function normalizeInput(input) {
|
|
|
355
144
|
}
|
|
356
145
|
var INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
|
357
146
|
var TYPESCRIPT_SDK_ORIGINATOR = "codex_sdk_ts";
|
|
147
|
+
var CODEX_NPM_NAME = "@openai/codex";
|
|
148
|
+
var PLATFORM_PACKAGE_BY_TARGET = {
|
|
149
|
+
"x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
|
|
150
|
+
"aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
|
|
151
|
+
"x86_64-apple-darwin": "@openai/codex-darwin-x64",
|
|
152
|
+
"aarch64-apple-darwin": "@openai/codex-darwin-arm64",
|
|
153
|
+
"x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
|
|
154
|
+
"aarch64-pc-windows-msvc": "@openai/codex-win32-arm64"
|
|
155
|
+
};
|
|
156
|
+
var moduleRequire = createRequire(import.meta.url);
|
|
358
157
|
var CodexExec = class {
|
|
359
158
|
executablePath;
|
|
360
159
|
envOverride;
|
|
@@ -437,7 +236,7 @@ var CodexExec = class {
|
|
|
437
236
|
if (args.apiKey) {
|
|
438
237
|
env.CODEX_API_KEY = args.apiKey;
|
|
439
238
|
}
|
|
440
|
-
const child =
|
|
239
|
+
const child = spawn(this.executablePath, commandArgs, {
|
|
441
240
|
env,
|
|
442
241
|
signal: args.signal
|
|
443
242
|
});
|
|
@@ -567,8 +366,6 @@ function formatTomlKey(key) {
|
|
|
567
366
|
function isPlainObject(value) {
|
|
568
367
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
569
368
|
}
|
|
570
|
-
var scriptFileName = fileURLToPath(import.meta.url);
|
|
571
|
-
var scriptDirName = path22.dirname(scriptFileName);
|
|
572
369
|
function findCodexPath() {
|
|
573
370
|
const { platform, arch } = process;
|
|
574
371
|
let targetTriple = null;
|
|
@@ -616,10 +413,24 @@ function findCodexPath() {
|
|
|
616
413
|
if (!targetTriple) {
|
|
617
414
|
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
618
415
|
}
|
|
619
|
-
const
|
|
620
|
-
|
|
416
|
+
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
|
|
417
|
+
if (!platformPackage) {
|
|
418
|
+
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
|
419
|
+
}
|
|
420
|
+
let vendorRoot;
|
|
421
|
+
try {
|
|
422
|
+
const codexPackageJsonPath = moduleRequire.resolve(`${CODEX_NPM_NAME}/package.json`);
|
|
423
|
+
const codexRequire = createRequire(codexPackageJsonPath);
|
|
424
|
+
const platformPackageJsonPath = codexRequire.resolve(`${platformPackage}/package.json`);
|
|
425
|
+
vendorRoot = path2.join(path2.dirname(platformPackageJsonPath), "vendor");
|
|
426
|
+
} catch {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`Unable to locate Codex CLI binaries. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const archRoot = path2.join(vendorRoot, targetTriple);
|
|
621
432
|
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
622
|
-
const binaryPath =
|
|
433
|
+
const binaryPath = path2.join(archRoot, "codex", codexBinaryName);
|
|
623
434
|
return binaryPath;
|
|
624
435
|
}
|
|
625
436
|
var Codex = class {
|
|
@@ -649,9 +460,81 @@ var Codex = class {
|
|
|
649
460
|
}
|
|
650
461
|
};
|
|
651
462
|
|
|
652
|
-
//
|
|
463
|
+
// node_modules/@shims/common/src/args.ts
|
|
464
|
+
var VALID_SANDBOX_LEVELS = ["none", "standard", "strict"];
|
|
465
|
+
function parseArgs(argv, aliases) {
|
|
466
|
+
const args = {
|
|
467
|
+
model: "",
|
|
468
|
+
verbose: false,
|
|
469
|
+
idleTimeout: 120,
|
|
470
|
+
sandbox: "none",
|
|
471
|
+
selfTest: false,
|
|
472
|
+
version: false,
|
|
473
|
+
help: false
|
|
474
|
+
};
|
|
475
|
+
for (let i = 0; i < argv.length; i++) {
|
|
476
|
+
let arg = argv[i];
|
|
477
|
+
if (arg.includes("=")) {
|
|
478
|
+
const [key, value] = arg.split("=", 2);
|
|
479
|
+
argv.splice(i, 1, key, value);
|
|
480
|
+
arg = key;
|
|
481
|
+
}
|
|
482
|
+
if (aliases && arg in aliases) {
|
|
483
|
+
arg = aliases[arg];
|
|
484
|
+
}
|
|
485
|
+
switch (arg) {
|
|
486
|
+
case "--model":
|
|
487
|
+
args.model = argv[++i];
|
|
488
|
+
break;
|
|
489
|
+
case "--resume":
|
|
490
|
+
args.resume = argv[++i];
|
|
491
|
+
break;
|
|
492
|
+
case "--verbose":
|
|
493
|
+
args.verbose = true;
|
|
494
|
+
break;
|
|
495
|
+
case "--append-system-prompt":
|
|
496
|
+
args.appendSystemPrompt = argv[++i];
|
|
497
|
+
break;
|
|
498
|
+
case "--debug-dir":
|
|
499
|
+
args.debugDir = argv[++i];
|
|
500
|
+
break;
|
|
501
|
+
case "--idle-timeout": {
|
|
502
|
+
const val = Number(argv[++i]);
|
|
503
|
+
if (!Number.isFinite(val) || val <= 0) {
|
|
504
|
+
console.error("Invalid --idle-timeout value: must be a positive number");
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
args.idleTimeout = val;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case "--sandbox": {
|
|
511
|
+
const level = argv[++i];
|
|
512
|
+
if (!VALID_SANDBOX_LEVELS.includes(level)) {
|
|
513
|
+
console.error(
|
|
514
|
+
`Invalid --sandbox value: must be one of ${VALID_SANDBOX_LEVELS.join(", ")}`
|
|
515
|
+
);
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
args.sandbox = level;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
case "--self-test":
|
|
522
|
+
args.selfTest = true;
|
|
523
|
+
break;
|
|
524
|
+
case "--version":
|
|
525
|
+
args.version = true;
|
|
526
|
+
break;
|
|
527
|
+
case "--help":
|
|
528
|
+
args.help = true;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return args;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// node_modules/@shims/common/src/sessions.ts
|
|
653
536
|
import { randomUUID } from "crypto";
|
|
654
|
-
import
|
|
537
|
+
import fs2 from "fs";
|
|
655
538
|
import path3 from "path";
|
|
656
539
|
var SessionManager = class {
|
|
657
540
|
sessionsDir;
|
|
@@ -665,7 +548,6 @@ var SessionManager = class {
|
|
|
665
548
|
}
|
|
666
549
|
this.sessionsDir = path3.join(home, ".shim", "sessions");
|
|
667
550
|
}
|
|
668
|
-
fs3.mkdirSync(this.sessionsDir, { recursive: true });
|
|
669
551
|
}
|
|
670
552
|
/**
|
|
671
553
|
* Generate a new UUID v4 session ID
|
|
@@ -677,8 +559,9 @@ var SessionManager = class {
|
|
|
677
559
|
* Save session data
|
|
678
560
|
*/
|
|
679
561
|
saveSession(data) {
|
|
562
|
+
this.ensureSessionsDir();
|
|
680
563
|
const sessionPath = path3.join(this.sessionsDir, `${data.sessionId}.json`);
|
|
681
|
-
|
|
564
|
+
fs2.writeFileSync(sessionPath, JSON.stringify(data, null, 2), "utf8");
|
|
682
565
|
}
|
|
683
566
|
/**
|
|
684
567
|
* Load session data by session ID
|
|
@@ -687,7 +570,7 @@ var SessionManager = class {
|
|
|
687
570
|
loadSession(sessionId) {
|
|
688
571
|
const sessionPath = path3.join(this.sessionsDir, `${sessionId}.json`);
|
|
689
572
|
try {
|
|
690
|
-
const content =
|
|
573
|
+
const content = fs2.readFileSync(sessionPath, "utf8");
|
|
691
574
|
return JSON.parse(content);
|
|
692
575
|
} catch (error) {
|
|
693
576
|
if (error.code === "ENOENT") {
|
|
@@ -701,7 +584,7 @@ var SessionManager = class {
|
|
|
701
584
|
*/
|
|
702
585
|
sessionExists(sessionId) {
|
|
703
586
|
const sessionPath = path3.join(this.sessionsDir, `${sessionId}.json`);
|
|
704
|
-
return
|
|
587
|
+
return fs2.existsSync(sessionPath);
|
|
705
588
|
}
|
|
706
589
|
/**
|
|
707
590
|
* Delete session data
|
|
@@ -709,7 +592,7 @@ var SessionManager = class {
|
|
|
709
592
|
deleteSession(sessionId) {
|
|
710
593
|
const sessionPath = path3.join(this.sessionsDir, `${sessionId}.json`);
|
|
711
594
|
try {
|
|
712
|
-
|
|
595
|
+
fs2.unlinkSync(sessionPath);
|
|
713
596
|
} catch (error) {
|
|
714
597
|
if (error.code !== "ENOENT") {
|
|
715
598
|
throw error;
|
|
@@ -721,7 +604,7 @@ var SessionManager = class {
|
|
|
721
604
|
*/
|
|
722
605
|
listSessions() {
|
|
723
606
|
try {
|
|
724
|
-
const files =
|
|
607
|
+
const files = fs2.readdirSync(this.sessionsDir);
|
|
725
608
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
726
609
|
} catch (error) {
|
|
727
610
|
if (error.code === "ENOENT") {
|
|
@@ -736,9 +619,12 @@ var SessionManager = class {
|
|
|
736
619
|
getSessionsDir() {
|
|
737
620
|
return this.sessionsDir;
|
|
738
621
|
}
|
|
622
|
+
ensureSessionsDir() {
|
|
623
|
+
fs2.mkdirSync(this.sessionsDir, { recursive: true });
|
|
624
|
+
}
|
|
739
625
|
};
|
|
740
626
|
|
|
741
|
-
//
|
|
627
|
+
// node_modules/@shims/common/src/timeout.ts
|
|
742
628
|
var IdleTimeoutError = class extends Error {
|
|
743
629
|
timeoutMs;
|
|
744
630
|
constructor(timeoutMs) {
|
|
@@ -747,21 +633,49 @@ var IdleTimeoutError = class extends Error {
|
|
|
747
633
|
this.timeoutMs = timeoutMs;
|
|
748
634
|
}
|
|
749
635
|
};
|
|
750
|
-
|
|
636
|
+
var BusyStepTimeoutError = class extends Error {
|
|
637
|
+
timeoutMs;
|
|
638
|
+
constructor(timeoutMs) {
|
|
639
|
+
super(`Busy-step stall timeout: no observed activity for ${timeoutMs}ms`);
|
|
640
|
+
this.name = "BusyStepTimeoutError";
|
|
641
|
+
this.timeoutMs = timeoutMs;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
async function* withAdaptiveTimeout(events, options) {
|
|
751
645
|
const iterator = events[Symbol.asyncIterator]();
|
|
646
|
+
let state = "idle";
|
|
647
|
+
const busyTimeoutMs = Math.max(
|
|
648
|
+
options.busyTimeoutMs ?? options.idleTimeoutMs,
|
|
649
|
+
options.idleTimeoutMs
|
|
650
|
+
);
|
|
651
|
+
const controller = {
|
|
652
|
+
markBusy() {
|
|
653
|
+
state = "busy";
|
|
654
|
+
},
|
|
655
|
+
markIdle() {
|
|
656
|
+
state = "idle";
|
|
657
|
+
},
|
|
658
|
+
get state() {
|
|
659
|
+
return state;
|
|
660
|
+
}
|
|
661
|
+
};
|
|
752
662
|
try {
|
|
753
663
|
while (true) {
|
|
664
|
+
const timeoutMs = state === "busy" ? busyTimeoutMs : options.idleTimeoutMs;
|
|
754
665
|
let timeoutId;
|
|
755
666
|
try {
|
|
756
667
|
const result = await Promise.race([
|
|
757
668
|
iterator.next(),
|
|
758
669
|
new Promise((_, reject) => {
|
|
759
670
|
timeoutId = setTimeout(() => {
|
|
760
|
-
reject(
|
|
671
|
+
reject(
|
|
672
|
+
state === "busy" ? new BusyStepTimeoutError(timeoutMs) : new IdleTimeoutError(timeoutMs)
|
|
673
|
+
);
|
|
761
674
|
}, timeoutMs);
|
|
762
675
|
})
|
|
763
676
|
]);
|
|
764
677
|
if (result.done) break;
|
|
678
|
+
options.onEvent?.(result.value, controller);
|
|
765
679
|
yield result.value;
|
|
766
680
|
} finally {
|
|
767
681
|
clearTimeout(timeoutId);
|
|
@@ -772,812 +686,1082 @@ async function* withIdleTimeout(events, timeoutMs) {
|
|
|
772
686
|
}
|
|
773
687
|
}
|
|
774
688
|
|
|
775
|
-
// src/
|
|
689
|
+
// src/debug.ts
|
|
690
|
+
import fs3 from "fs";
|
|
691
|
+
import path4 from "path";
|
|
692
|
+
var DebugRecorder = class {
|
|
693
|
+
debugDir;
|
|
694
|
+
rawJsonlPath;
|
|
695
|
+
rawLogPath;
|
|
696
|
+
constructor(debugDir) {
|
|
697
|
+
this.debugDir = debugDir;
|
|
698
|
+
}
|
|
699
|
+
setSession(sessionId, init) {
|
|
700
|
+
if (!this.debugDir) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
fs3.mkdirSync(this.debugDir, { recursive: true });
|
|
704
|
+
this.rawJsonlPath = path4.join(this.debugDir, `session-${sessionId}.raw.jsonl`);
|
|
705
|
+
this.rawLogPath = path4.join(this.debugDir, `session-${sessionId}.raw.log`);
|
|
706
|
+
this.touch(this.rawJsonlPath);
|
|
707
|
+
this.touch(this.rawLogPath);
|
|
708
|
+
this.logJson({
|
|
709
|
+
type: "init",
|
|
710
|
+
session_id: sessionId,
|
|
711
|
+
cwd: init.cwd,
|
|
712
|
+
model: init.model,
|
|
713
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
logSdkEvent(event) {
|
|
717
|
+
this.logJson(event);
|
|
718
|
+
}
|
|
719
|
+
logResult(status, extra = {}) {
|
|
720
|
+
this.logJson({
|
|
721
|
+
type: "result",
|
|
722
|
+
status,
|
|
723
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
724
|
+
...extra
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
logLine(line) {
|
|
728
|
+
if (!this.rawLogPath) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
fs3.appendFileSync(this.rawLogPath, line.endsWith("\n") ? line : `${line}
|
|
732
|
+
`, "utf8");
|
|
733
|
+
}
|
|
734
|
+
static logStartupError(debugDir, line) {
|
|
735
|
+
if (!debugDir) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
fs3.mkdirSync(debugDir, { recursive: true });
|
|
739
|
+
const rawLogPath = path4.join(debugDir, "session-unknown.raw.log");
|
|
740
|
+
fs3.appendFileSync(rawLogPath, line.endsWith("\n") ? line : `${line}
|
|
741
|
+
`, "utf8");
|
|
742
|
+
}
|
|
743
|
+
logJson(value) {
|
|
744
|
+
if (!this.rawJsonlPath) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
fs3.appendFileSync(this.rawJsonlPath, `${JSON.stringify(value)}
|
|
748
|
+
`, "utf8");
|
|
749
|
+
}
|
|
750
|
+
touch(filePath) {
|
|
751
|
+
fs3.closeSync(fs3.openSync(filePath, "a"));
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
// src/utils.ts
|
|
756
|
+
import { randomBytes, randomUUID as randomUUID2 } from "crypto";
|
|
757
|
+
import path5 from "path";
|
|
758
|
+
var NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
759
|
+
var SESSION_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
760
|
+
function generateSessionId() {
|
|
761
|
+
return randomUUID2();
|
|
762
|
+
}
|
|
776
763
|
function generateMessageId() {
|
|
777
|
-
return `msg_${Date.now().toString(36)}${
|
|
764
|
+
return `msg_${Date.now().toString(36)}${randomBytes(6).toString("hex")}`;
|
|
778
765
|
}
|
|
779
766
|
function generateToolUseId() {
|
|
780
|
-
return `toolu_${Date.now().toString(36)}${
|
|
767
|
+
return `toolu_${Date.now().toString(36)}${randomBytes(8).toString("hex")}`;
|
|
781
768
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
// src/utils/models.ts
|
|
785
|
-
var MODEL_SHORTNAMES = {
|
|
786
|
-
codex: "openai/gpt-5.1-codex-max",
|
|
787
|
-
"codex-max": "openai/gpt-5.1-codex-max",
|
|
788
|
-
"o4-mini": "openai/o4-mini"
|
|
789
|
-
};
|
|
790
|
-
var VALID_REASONING_EFFORTS = [
|
|
791
|
-
"minimal",
|
|
792
|
-
"low",
|
|
793
|
-
"medium",
|
|
794
|
-
"high",
|
|
795
|
-
"xhigh"
|
|
796
|
-
];
|
|
797
|
-
function isValidReasoningEffort(value) {
|
|
798
|
-
return VALID_REASONING_EFFORTS.includes(value);
|
|
769
|
+
function isValidSessionId(value) {
|
|
770
|
+
return typeof value === "string" && SESSION_ID_REGEX.test(value);
|
|
799
771
|
}
|
|
800
|
-
function resolveModel(
|
|
801
|
-
|
|
802
|
-
let
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
modelInput = baseModel;
|
|
809
|
-
reasoningEffort = potentialEffort;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
const fullModel = MODEL_SHORTNAMES[modelInput.toLowerCase()] || modelInput;
|
|
813
|
-
if (fullModel.includes("/")) {
|
|
814
|
-
const [providerID, modelID] = fullModel.split("/", 2);
|
|
815
|
-
return { providerID, modelID, reasoningEffort };
|
|
772
|
+
function resolveModel(modelFromArgs) {
|
|
773
|
+
const requestedModel = modelFromArgs?.trim() || process.env.MODEL?.trim() || "gpt-5.1-codex-max";
|
|
774
|
+
let sdkModel = requestedModel;
|
|
775
|
+
if (sdkModel.includes("/")) {
|
|
776
|
+
const [provider, model] = sdkModel.split("/", 2);
|
|
777
|
+
if (provider.toLowerCase() === "openai" && model) {
|
|
778
|
+
sdkModel = model;
|
|
779
|
+
}
|
|
816
780
|
}
|
|
781
|
+
let reasoningEffort = "high";
|
|
782
|
+
const effortMatch = sdkModel.match(/^(.*)-(minimal|low|medium|high|xhigh)$/);
|
|
783
|
+
if (effortMatch) {
|
|
784
|
+
sdkModel = effortMatch[1];
|
|
785
|
+
reasoningEffort = effortMatch[2];
|
|
786
|
+
}
|
|
787
|
+
const publicModel = `openai/${sdkModel}`;
|
|
817
788
|
return {
|
|
818
|
-
|
|
819
|
-
|
|
789
|
+
requestedModel,
|
|
790
|
+
publicModel,
|
|
791
|
+
sdkModel,
|
|
820
792
|
reasoningEffort
|
|
821
793
|
};
|
|
822
794
|
}
|
|
823
|
-
function
|
|
824
|
-
|
|
795
|
+
function mapSandbox(level) {
|
|
796
|
+
switch (level) {
|
|
797
|
+
case "strict":
|
|
798
|
+
return "read-only";
|
|
799
|
+
case "standard":
|
|
800
|
+
return "workspace-write";
|
|
801
|
+
case "none":
|
|
802
|
+
default:
|
|
803
|
+
return "danger-full-access";
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
function getCodexPathOverride() {
|
|
807
|
+
const value = process.env.CODEX_PATH_OVERRIDE?.trim();
|
|
808
|
+
return value ? value : void 0;
|
|
825
809
|
}
|
|
826
|
-
function
|
|
827
|
-
return
|
|
810
|
+
function toErrorMessage(error) {
|
|
811
|
+
return error instanceof Error ? error.message : String(error);
|
|
828
812
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
function emit(message) {
|
|
832
|
-
console.log(JSON.stringify(message));
|
|
813
|
+
function camelToSnakeKey(key) {
|
|
814
|
+
return key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
833
815
|
}
|
|
834
|
-
function
|
|
835
|
-
if (
|
|
836
|
-
|
|
816
|
+
function topLevelCamelToSnake(value) {
|
|
817
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
818
|
+
return void 0;
|
|
837
819
|
}
|
|
820
|
+
const result = {};
|
|
821
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
822
|
+
result[camelToSnakeKey(key)] = entry;
|
|
823
|
+
}
|
|
824
|
+
return result;
|
|
838
825
|
}
|
|
839
826
|
async function flushStdout() {
|
|
840
|
-
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
827
|
+
await new Promise((resolve, reject) => {
|
|
828
|
+
const done = (error) => {
|
|
829
|
+
if (error) {
|
|
830
|
+
reject(error);
|
|
831
|
+
} else {
|
|
832
|
+
resolve();
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
if (process.stdout.write("")) {
|
|
836
|
+
done();
|
|
837
|
+
return;
|
|
846
838
|
}
|
|
839
|
+
process.stdout.once("drain", () => done());
|
|
840
|
+
process.stdout.once("error", (error) => done(error));
|
|
847
841
|
});
|
|
848
842
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
var STANDARD_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "LS"];
|
|
852
|
-
function normalizeToolName(name) {
|
|
853
|
-
const lowerName = name.toLowerCase();
|
|
854
|
-
const toolMap = {
|
|
855
|
-
read: "Read",
|
|
856
|
-
file_read: "Read",
|
|
857
|
-
readfile: "Read",
|
|
858
|
-
read_text_file: "Read",
|
|
859
|
-
write: "Write",
|
|
860
|
-
file_write: "Write",
|
|
861
|
-
writefile: "Write",
|
|
862
|
-
write_text_file: "Write",
|
|
863
|
-
edit: "Edit",
|
|
864
|
-
str_replace_editor: "Edit",
|
|
865
|
-
edit_text_file: "Edit",
|
|
866
|
-
bash: "Bash",
|
|
867
|
-
shell: "Bash",
|
|
868
|
-
execute_bash: "Bash",
|
|
869
|
-
exec: "Bash",
|
|
870
|
-
glob: "Glob",
|
|
871
|
-
find_files: "Glob",
|
|
872
|
-
grep: "Grep",
|
|
873
|
-
search_files: "Grep",
|
|
874
|
-
ls: "LS",
|
|
875
|
-
list: "LS",
|
|
876
|
-
list_directory: "LS"
|
|
877
|
-
};
|
|
878
|
-
return toolMap[lowerName] || name;
|
|
843
|
+
function makeAbsoluteCwd(cwd) {
|
|
844
|
+
return path5.resolve(cwd);
|
|
879
845
|
}
|
|
880
|
-
function
|
|
881
|
-
|
|
846
|
+
function toRelativePath(filePath, cwd) {
|
|
847
|
+
const absoluteCwd = path5.resolve(cwd);
|
|
848
|
+
const absolutePath = path5.resolve(filePath);
|
|
849
|
+
const relative = path5.relative(absoluteCwd, absolutePath);
|
|
850
|
+
return relative && !relative.startsWith("..") ? relative : absolutePath;
|
|
882
851
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
852
|
+
|
|
853
|
+
// src/tools.ts
|
|
854
|
+
function normalizeToolUse(item, cwd) {
|
|
855
|
+
switch (item.type) {
|
|
856
|
+
case "command_execution":
|
|
857
|
+
return normalizeCommandExecution(item);
|
|
858
|
+
case "file_change":
|
|
859
|
+
return normalizeFileChange(item, cwd);
|
|
860
|
+
case "mcp_tool_call":
|
|
861
|
+
return normalizeMcpToolCall(item);
|
|
862
|
+
case "web_search":
|
|
863
|
+
return normalizeWebSearch(item);
|
|
864
|
+
default:
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function normalizeToolResult(item, cwd) {
|
|
869
|
+
switch (item.type) {
|
|
870
|
+
case "command_execution":
|
|
871
|
+
return normalizeCommandExecutionResult(item);
|
|
872
|
+
case "file_change":
|
|
873
|
+
return normalizeFileChangeResult(item, cwd);
|
|
874
|
+
case "mcp_tool_call":
|
|
875
|
+
return normalizeMcpToolCallResult(item);
|
|
876
|
+
case "web_search":
|
|
877
|
+
return {
|
|
878
|
+
isError: false,
|
|
879
|
+
content: `Web search completed for query: ${item.query}`
|
|
880
|
+
};
|
|
881
|
+
default:
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
function normalizeCommandExecution(item) {
|
|
886
|
+
const innerCommand = extractInnerCommand(item.command);
|
|
887
|
+
const simpleWrite = parseSimpleShellWrite(innerCommand);
|
|
888
|
+
if (simpleWrite) {
|
|
889
|
+
return {
|
|
890
|
+
name: "Write",
|
|
891
|
+
input: {
|
|
892
|
+
file_path: simpleWrite.filePath,
|
|
893
|
+
content: simpleWrite.content
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
const readMatch = innerCommand.match(/^cat\s+([^\s;&|]+)$/);
|
|
898
|
+
if (readMatch) {
|
|
899
|
+
return {
|
|
900
|
+
name: "Read",
|
|
901
|
+
input: { file_path: stripQuotes(readMatch[1]) }
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const lsMatch = innerCommand.match(/^ls(?:\s+(-[A-Za-z-]+))*?(?:\s+([^;&|]+))?$/);
|
|
905
|
+
if (lsMatch) {
|
|
906
|
+
const target = lsMatch[2]?.trim();
|
|
907
|
+
return {
|
|
908
|
+
name: "LS",
|
|
909
|
+
input: target ? { path: stripQuotes(target) } : void 0
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
const grepMatch = innerCommand.match(/^(?:rg|grep)\b(.*)$/);
|
|
913
|
+
if (grepMatch) {
|
|
914
|
+
return {
|
|
915
|
+
name: "Grep",
|
|
916
|
+
input: { command: innerCommand }
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
const globMatch = innerCommand.match(/^(?:find|fd)\b(.*)$/);
|
|
920
|
+
if (globMatch) {
|
|
921
|
+
return {
|
|
922
|
+
name: "Glob",
|
|
923
|
+
input: { command: innerCommand }
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
name: "Bash",
|
|
928
|
+
input: { command: innerCommand }
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function normalizeCommandExecutionResult(item) {
|
|
932
|
+
const output = item.aggregated_output;
|
|
933
|
+
const innerCommand = extractInnerCommand(item.command);
|
|
934
|
+
const simpleWrite = parseSimpleShellWrite(innerCommand);
|
|
935
|
+
if (item.status === "failed" || typeof item.exit_code === "number" && item.exit_code !== 0) {
|
|
936
|
+
return {
|
|
937
|
+
isError: true,
|
|
938
|
+
content: {
|
|
939
|
+
is_error: true,
|
|
940
|
+
error: output.trim() || `Command failed with exit code ${item.exit_code ?? "unknown"}`
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
if (output.trim().length > 0) {
|
|
945
|
+
return {
|
|
946
|
+
isError: false,
|
|
947
|
+
content: output
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
if (simpleWrite) {
|
|
951
|
+
return {
|
|
952
|
+
isError: false,
|
|
953
|
+
content: `Wrote ${simpleWrite.filePath}`
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
isError: false,
|
|
958
|
+
content: `Command completed with exit code ${item.exit_code ?? 0} (no output).`
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function normalizeFileChange(item, cwd) {
|
|
962
|
+
const paths = item.changes.map((change) => toRelativePath(change.path, cwd));
|
|
963
|
+
const allAdds = item.changes.every((change) => change.kind === "add");
|
|
964
|
+
return {
|
|
965
|
+
name: allAdds ? "Write" : "Edit",
|
|
966
|
+
input: paths.length === 1 ? { file_path: paths[0] } : { paths }
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function normalizeFileChangeResult(item, cwd) {
|
|
970
|
+
const summary = item.changes.map((change) => `${change.kind} ${toRelativePath(change.path, cwd)}`).join(", ");
|
|
971
|
+
if (item.status === "failed") {
|
|
972
|
+
return {
|
|
973
|
+
isError: true,
|
|
974
|
+
content: {
|
|
975
|
+
is_error: true,
|
|
976
|
+
error: summary || "File change failed"
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
return {
|
|
981
|
+
isError: false,
|
|
982
|
+
content: summary ? `Applied file change: ${summary}` : "Applied file change."
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function normalizeMcpToolCall(item) {
|
|
986
|
+
const lowerServer = item.server.toLowerCase();
|
|
987
|
+
const lowerTool = item.tool.toLowerCase();
|
|
988
|
+
if (lowerTool.includes("search") || lowerServer.includes("search") || lowerServer.includes("web")) {
|
|
989
|
+
return {
|
|
990
|
+
name: "WebSearch",
|
|
991
|
+
input: topLevelCamelToSnake(item.arguments) ?? { arguments: item.arguments }
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
if (lowerTool.includes("fetch") || lowerTool.includes("browse")) {
|
|
995
|
+
return {
|
|
996
|
+
name: "WebFetch",
|
|
997
|
+
input: topLevelCamelToSnake(item.arguments) ?? { arguments: item.arguments }
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
return {
|
|
1001
|
+
name: `${item.server}:${item.tool}`,
|
|
1002
|
+
input: topLevelCamelToSnake(item.arguments) ?? { arguments: item.arguments }
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function normalizeMcpToolCallResult(item) {
|
|
1006
|
+
if (item.status === "failed") {
|
|
1007
|
+
return {
|
|
1008
|
+
isError: true,
|
|
1009
|
+
content: {
|
|
1010
|
+
is_error: true,
|
|
1011
|
+
error: item.error?.message || "MCP tool call failed"
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
const textParts = item.result?.content?.map((block) => {
|
|
1016
|
+
if (block.type === "text") {
|
|
1017
|
+
return block.text;
|
|
1018
|
+
}
|
|
1019
|
+
return JSON.stringify(block);
|
|
1020
|
+
}).filter((value) => typeof value === "string" && value.length > 0);
|
|
1021
|
+
const combinedText = textParts?.join("\n").trim();
|
|
1022
|
+
return {
|
|
1023
|
+
isError: false,
|
|
1024
|
+
content: combinedText || (item.result?.structured_content !== void 0 ? JSON.stringify(item.result.structured_content) : `${item.server}:${item.tool} completed successfully.`)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function normalizeWebSearch(item) {
|
|
1028
|
+
return {
|
|
1029
|
+
name: "WebSearch",
|
|
1030
|
+
input: { query: item.query }
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function shouldTreatAsToolItem(item) {
|
|
1034
|
+
return item.type === "command_execution" || item.type === "file_change" || item.type === "mcp_tool_call" || item.type === "web_search";
|
|
1035
|
+
}
|
|
1036
|
+
function extractInnerCommand(command) {
|
|
1037
|
+
const trimmed = command.trim();
|
|
1038
|
+
const zshMatch = trimmed.match(/-lc\s+([\s\S]+)$/);
|
|
1039
|
+
const shellCommand = zshMatch ? stripQuotes(zshMatch[1].trim()) : trimmed;
|
|
1040
|
+
return stripLeadingCdPrefix(shellCommand);
|
|
1041
|
+
}
|
|
1042
|
+
function stripLeadingCdPrefix(command) {
|
|
1043
|
+
const match = command.match(/^cd\s+(.+?)\s*&&\s*([\s\S]+)$/);
|
|
1044
|
+
if (!match) {
|
|
1045
|
+
return command;
|
|
1046
|
+
}
|
|
1047
|
+
return match[2].trim();
|
|
1048
|
+
}
|
|
1049
|
+
function parseSimpleShellWrite(command) {
|
|
1050
|
+
return parseHereDocShellWrite(command) ?? parseRedirectShellWrite(command);
|
|
1051
|
+
}
|
|
1052
|
+
function parseHereDocShellWrite(command) {
|
|
1053
|
+
const redirectFirst = command.match(
|
|
1054
|
+
/^cat\s*>\s*("(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|[^\s;&|]+)\s*<<['"]?([A-Za-z0-9_-]+)['"]?\n([\s\S]*?)\n\2$/
|
|
1055
|
+
);
|
|
1056
|
+
if (redirectFirst) {
|
|
1057
|
+
const [, rawFilePath, , body] = redirectFirst;
|
|
1058
|
+
return {
|
|
1059
|
+
filePath: stripQuotes(rawFilePath.trim()),
|
|
1060
|
+
content: `${body}
|
|
1061
|
+
`
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
const heredocFirst = command.match(
|
|
1065
|
+
/^cat\s*<<['"]?([A-Za-z0-9_-]+)['"]?\s*>\s*("(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|[^\s;&|]+)\n([\s\S]*?)\n\1$/
|
|
1066
|
+
);
|
|
1067
|
+
if (heredocFirst) {
|
|
1068
|
+
const [, , rawFilePath, body] = heredocFirst;
|
|
1069
|
+
return {
|
|
1070
|
+
filePath: stripQuotes(rawFilePath.trim()),
|
|
1071
|
+
content: `${body}
|
|
1072
|
+
`
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
function parseRedirectShellWrite(command) {
|
|
1078
|
+
const match = command.match(
|
|
1079
|
+
/^(printf|echo)(?:\s+(-n))?\s+([\s\S]+?)\s*>\s*("(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|[^\s;&|]+)$/
|
|
1080
|
+
);
|
|
1081
|
+
if (!match) {
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
const [, commandName, noNewlineFlag, rawContent, rawFilePath] = match;
|
|
1085
|
+
const parsedContent = parseSimpleShellTextLiteral(rawContent.trim());
|
|
1086
|
+
if (parsedContent === void 0) {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
const content = commandName === "printf" ? decodePrintfEscapes(parsedContent) : noNewlineFlag === "-n" ? parsedContent : `${parsedContent}
|
|
1090
|
+
`;
|
|
1091
|
+
return {
|
|
1092
|
+
filePath: stripQuotes(rawFilePath.trim()),
|
|
1093
|
+
content
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function parseSimpleShellTextLiteral(value) {
|
|
1097
|
+
const trimmed = value.trim();
|
|
1098
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
1099
|
+
return stripQuotes(trimmed);
|
|
1100
|
+
}
|
|
1101
|
+
if (/^[^\s;&|<>]+$/.test(trimmed)) {
|
|
1102
|
+
return trimmed;
|
|
1103
|
+
}
|
|
1104
|
+
return void 0;
|
|
1105
|
+
}
|
|
1106
|
+
function decodePrintfEscapes(value) {
|
|
1107
|
+
return value.replace(/\\\\/g, "\\").replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ");
|
|
1108
|
+
}
|
|
1109
|
+
function stripQuotes(value) {
|
|
1110
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1111
|
+
return value.slice(1, -1);
|
|
1112
|
+
}
|
|
1113
|
+
return value;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/timeout-state.ts
|
|
1117
|
+
function shouldTreatTurnStartAsBusy(idleTimeoutMs) {
|
|
1118
|
+
return idleTimeoutMs > 1e3;
|
|
1119
|
+
}
|
|
1120
|
+
function updateCodexTimeoutState(event, controller, idleTimeoutMs) {
|
|
1121
|
+
const turnBusy = shouldTreatTurnStartAsBusy(idleTimeoutMs);
|
|
1122
|
+
switch (event.type) {
|
|
1123
|
+
case "turn.started": {
|
|
1124
|
+
if (turnBusy) {
|
|
1125
|
+
controller.markBusy();
|
|
1126
|
+
}
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
case "item.started":
|
|
1130
|
+
case "item.updated":
|
|
1131
|
+
case "item.completed": {
|
|
1132
|
+
if (turnBusy || shouldTreatAsToolItem(event.item)) {
|
|
1133
|
+
controller.markBusy();
|
|
1134
|
+
}
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
case "turn.completed":
|
|
1138
|
+
case "turn.failed": {
|
|
1139
|
+
controller.markIdle();
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
default:
|
|
1143
|
+
return;
|
|
887
1144
|
}
|
|
888
|
-
return normalized;
|
|
889
1145
|
}
|
|
890
1146
|
|
|
1147
|
+
// package.json
|
|
1148
|
+
var package_default = {
|
|
1149
|
+
name: "codex-shim",
|
|
1150
|
+
version: "0.1.0",
|
|
1151
|
+
private: true,
|
|
1152
|
+
description: "Self-contained Hankweave shim for OpenAI Codex via @openai/codex-sdk",
|
|
1153
|
+
type: "module",
|
|
1154
|
+
bin: {
|
|
1155
|
+
"codex-shim": "./index.js"
|
|
1156
|
+
},
|
|
1157
|
+
files: [
|
|
1158
|
+
"index.js",
|
|
1159
|
+
"dist",
|
|
1160
|
+
"src",
|
|
1161
|
+
"tests",
|
|
1162
|
+
"common",
|
|
1163
|
+
"docs",
|
|
1164
|
+
"README.md",
|
|
1165
|
+
"rebuild.sh",
|
|
1166
|
+
"VERSION",
|
|
1167
|
+
"THIRDPARTY.md",
|
|
1168
|
+
"tsconfig.json",
|
|
1169
|
+
"tsup.config.ts",
|
|
1170
|
+
"bun.lock"
|
|
1171
|
+
],
|
|
1172
|
+
engines: {
|
|
1173
|
+
node: ">=18",
|
|
1174
|
+
bun: ">=1.1.0"
|
|
1175
|
+
},
|
|
1176
|
+
scripts: {
|
|
1177
|
+
build: "tsup",
|
|
1178
|
+
rebuild: "./rebuild.sh",
|
|
1179
|
+
test: "bun test",
|
|
1180
|
+
typecheck: "tsc --noEmit -p tsconfig.json",
|
|
1181
|
+
clean: `node -e "const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('index.js',{force:true});"`
|
|
1182
|
+
},
|
|
1183
|
+
dependencies: {
|
|
1184
|
+
"@openai/codex-sdk": "^0.112.0",
|
|
1185
|
+
"@shims/common": "file:./common"
|
|
1186
|
+
},
|
|
1187
|
+
devDependencies: {
|
|
1188
|
+
"@types/bun": "latest",
|
|
1189
|
+
"@types/node": "^20.10.0",
|
|
1190
|
+
tsup: "^8.0.1",
|
|
1191
|
+
typescript: "^5.3.0"
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
|
|
891
1195
|
// src/shim.ts
|
|
1196
|
+
var DEFAULT_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "LS", "WebSearch"];
|
|
1197
|
+
var HELP_TEXT = `codex-shim
|
|
1198
|
+
|
|
1199
|
+
Usage:
|
|
1200
|
+
codex-shim --model <model> [options]
|
|
1201
|
+
|
|
1202
|
+
Options:
|
|
1203
|
+
-p
|
|
1204
|
+
--model <model>
|
|
1205
|
+
--resume <session_id>
|
|
1206
|
+
--verbose
|
|
1207
|
+
--append-system-prompt <text>
|
|
1208
|
+
--idle-timeout <seconds>
|
|
1209
|
+
--debug-dir <path>
|
|
1210
|
+
--sandbox <none|standard|strict>
|
|
1211
|
+
--self-test
|
|
1212
|
+
--version
|
|
1213
|
+
--help
|
|
1214
|
+
`;
|
|
1215
|
+
function emit(message) {
|
|
1216
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
1217
|
+
`);
|
|
1218
|
+
}
|
|
1219
|
+
function usageToTokenUsage(usage) {
|
|
1220
|
+
if (!usage) {
|
|
1221
|
+
return void 0;
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
input_tokens: usage.input_tokens,
|
|
1225
|
+
output_tokens: usage.output_tokens,
|
|
1226
|
+
cache_read_input_tokens: usage.cached_input_tokens
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function createSystemMessage(cwd, sessionId, model, apiKeySource) {
|
|
1230
|
+
return {
|
|
1231
|
+
type: "system",
|
|
1232
|
+
subtype: "init",
|
|
1233
|
+
cwd,
|
|
1234
|
+
session_id: sessionId,
|
|
1235
|
+
tools: DEFAULT_TOOLS,
|
|
1236
|
+
model,
|
|
1237
|
+
permissionMode: "bypassPermissions",
|
|
1238
|
+
apiKeySource,
|
|
1239
|
+
mcp_servers: []
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
function createAssistantMessage(model, content, stopReason = null, usage, id = generateMessageId()) {
|
|
1243
|
+
return {
|
|
1244
|
+
type: "assistant",
|
|
1245
|
+
message: {
|
|
1246
|
+
id,
|
|
1247
|
+
type: "message",
|
|
1248
|
+
role: "assistant",
|
|
1249
|
+
model,
|
|
1250
|
+
content,
|
|
1251
|
+
stop_reason: stopReason,
|
|
1252
|
+
...usage ? { usage } : {}
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
function createToolResultMessage(toolUseId, content) {
|
|
1257
|
+
return {
|
|
1258
|
+
type: "user",
|
|
1259
|
+
message: {
|
|
1260
|
+
role: "user",
|
|
1261
|
+
content: [
|
|
1262
|
+
{
|
|
1263
|
+
type: "tool_result",
|
|
1264
|
+
tool_use_id: toolUseId,
|
|
1265
|
+
content
|
|
1266
|
+
}
|
|
1267
|
+
]
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function createResultMessage(ok, durationMs, durationApiMs, numTurns, summary, sessionId, usage) {
|
|
1272
|
+
return {
|
|
1273
|
+
type: "result",
|
|
1274
|
+
subtype: ok ? "success" : "error",
|
|
1275
|
+
is_error: !ok,
|
|
1276
|
+
duration_ms: durationMs,
|
|
1277
|
+
duration_api_ms: durationApiMs,
|
|
1278
|
+
num_turns: numTurns,
|
|
1279
|
+
result: summary,
|
|
1280
|
+
session_id: sessionId,
|
|
1281
|
+
...usage ? { usage } : {}
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function composePrompt(prompt, appendSystemPrompt) {
|
|
1285
|
+
if (!appendSystemPrompt?.trim()) {
|
|
1286
|
+
return prompt;
|
|
1287
|
+
}
|
|
1288
|
+
return [
|
|
1289
|
+
"<SYSTEM_INSTRUCTIONS>",
|
|
1290
|
+
appendSystemPrompt.trim(),
|
|
1291
|
+
"</SYSTEM_INSTRUCTIONS>",
|
|
1292
|
+
"",
|
|
1293
|
+
"<USER_REQUEST>",
|
|
1294
|
+
prompt,
|
|
1295
|
+
"</USER_REQUEST>"
|
|
1296
|
+
].join("\n");
|
|
1297
|
+
}
|
|
1298
|
+
function classifyRuntimeError(error) {
|
|
1299
|
+
if (error instanceof IdleTimeoutError || error instanceof BusyStepTimeoutError) {
|
|
1300
|
+
return `Agent Error: ${error.message}`;
|
|
1301
|
+
}
|
|
1302
|
+
const message = toErrorMessage(error);
|
|
1303
|
+
if (/rate limit|authentication|not supported|invalid model|insufficient|quota|api|unsupported/i.test(
|
|
1304
|
+
message
|
|
1305
|
+
)) {
|
|
1306
|
+
return `API Error: ${message}`;
|
|
1307
|
+
}
|
|
1308
|
+
return `Agent Error: ${message}`;
|
|
1309
|
+
}
|
|
1310
|
+
async function readStdin() {
|
|
1311
|
+
const chunks = [];
|
|
1312
|
+
for await (const chunk of process.stdin) {
|
|
1313
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1314
|
+
}
|
|
1315
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
1316
|
+
}
|
|
1317
|
+
function writeStartupError(message, debugDir) {
|
|
1318
|
+
DebugRecorder.logStartupError(debugDir, message);
|
|
1319
|
+
process.stderr.write(`${message}
|
|
1320
|
+
`);
|
|
1321
|
+
process.exit(1);
|
|
1322
|
+
}
|
|
1323
|
+
function findVendoredCodexExe(npmPrefix) {
|
|
1324
|
+
const arch = process.arch === "arm64" ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc";
|
|
1325
|
+
const pkgName = process.arch === "arm64" ? "@openai/codex-win32-arm64" : "@openai/codex-win32-x64";
|
|
1326
|
+
const tail = path6.join(...pkgName.split("/"), "vendor", arch, "codex", "codex.exe");
|
|
1327
|
+
const candidates = [
|
|
1328
|
+
path6.join(npmPrefix, "node_modules", tail),
|
|
1329
|
+
path6.join(npmPrefix, "node_modules", "@openai", "codex", "node_modules", tail)
|
|
1330
|
+
];
|
|
1331
|
+
for (const candidate of candidates) {
|
|
1332
|
+
if (fs4.existsSync(candidate)) return candidate;
|
|
1333
|
+
}
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
async function resolveCodexPath(command) {
|
|
1337
|
+
const isWindows = process.platform === "win32";
|
|
1338
|
+
if (path6.isAbsolute(command) || command.includes(path6.sep)) {
|
|
1339
|
+
if (fs4.existsSync(command)) return command;
|
|
1340
|
+
if (isWindows) {
|
|
1341
|
+
for (const ext of [".cmd", ".exe"]) {
|
|
1342
|
+
if (fs4.existsSync(command + ext)) return command + ext;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
const whichCommand = isWindows ? "where" : "which";
|
|
1348
|
+
const resolved = await new Promise((resolve) => {
|
|
1349
|
+
const proc = spawn2(whichCommand, [command], {
|
|
1350
|
+
shell: isWindows,
|
|
1351
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1352
|
+
env: process.env
|
|
1353
|
+
});
|
|
1354
|
+
let stdout = "";
|
|
1355
|
+
proc.stdout?.on("data", (chunk) => {
|
|
1356
|
+
stdout += chunk.toString();
|
|
1357
|
+
});
|
|
1358
|
+
proc.on("error", () => resolve(null));
|
|
1359
|
+
proc.on("close", (code) => {
|
|
1360
|
+
if (code === 0 && stdout.trim()) {
|
|
1361
|
+
resolve(stdout.trim().split(/\r?\n/)[0]);
|
|
1362
|
+
} else {
|
|
1363
|
+
resolve(null);
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
});
|
|
1367
|
+
if (!resolved) return null;
|
|
1368
|
+
if (isWindows) {
|
|
1369
|
+
const npmPrefix = path6.dirname(resolved);
|
|
1370
|
+
const vendored = findVendoredCodexExe(npmPrefix);
|
|
1371
|
+
if (vendored) return vendored;
|
|
1372
|
+
}
|
|
1373
|
+
return resolved;
|
|
1374
|
+
}
|
|
1375
|
+
async function resolveAgentVersion(codexPath) {
|
|
1376
|
+
const isWindows = process.platform === "win32";
|
|
1377
|
+
return await new Promise((resolve) => {
|
|
1378
|
+
const proc = spawn2(codexPath, ["--version"], {
|
|
1379
|
+
shell: isWindows,
|
|
1380
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1381
|
+
env: process.env
|
|
1382
|
+
});
|
|
1383
|
+
let stdout = "";
|
|
1384
|
+
let stderr = "";
|
|
1385
|
+
proc.stdout?.on("data", (chunk) => {
|
|
1386
|
+
stdout += chunk.toString();
|
|
1387
|
+
});
|
|
1388
|
+
proc.stderr?.on("data", (chunk) => {
|
|
1389
|
+
stderr += chunk.toString();
|
|
1390
|
+
});
|
|
1391
|
+
proc.on("error", () => resolve("unknown"));
|
|
1392
|
+
proc.on("close", (code) => {
|
|
1393
|
+
if (code === 0) {
|
|
1394
|
+
resolve(stdout.trim() || stderr.trim() || package_default.dependencies["@openai/codex-sdk"] || "unknown");
|
|
1395
|
+
} else {
|
|
1396
|
+
resolve("unknown");
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
892
1401
|
var CodexShim = class {
|
|
893
1402
|
args;
|
|
894
|
-
|
|
1403
|
+
prompt;
|
|
895
1404
|
cwd;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
interrupted = false;
|
|
1405
|
+
debug;
|
|
1406
|
+
model;
|
|
899
1407
|
sessionManager;
|
|
900
|
-
|
|
901
|
-
|
|
1408
|
+
runtime = {
|
|
1409
|
+
numTurns: 0,
|
|
1410
|
+
finalText: ""
|
|
1411
|
+
};
|
|
1412
|
+
toolIdMap = /* @__PURE__ */ new Map();
|
|
1413
|
+
codex;
|
|
1414
|
+
sessionId;
|
|
1415
|
+
interrupted = false;
|
|
1416
|
+
currentAbortController;
|
|
1417
|
+
constructor(args, prompt) {
|
|
902
1418
|
this.args = args;
|
|
903
|
-
this.
|
|
904
|
-
this.
|
|
905
|
-
this.
|
|
1419
|
+
this.prompt = prompt;
|
|
1420
|
+
this.cwd = makeAbsoluteCwd(process.cwd());
|
|
1421
|
+
this.debug = new DebugRecorder(args.debugDir);
|
|
1422
|
+
this.model = resolveModel(this.args.model);
|
|
906
1423
|
this.sessionManager = new SessionManager({ debugDir: args.debugDir });
|
|
907
|
-
this.
|
|
1424
|
+
this.sessionId = args.resume || generateSessionId();
|
|
1425
|
+
}
|
|
1426
|
+
get resolvedApiKey() {
|
|
1427
|
+
return process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || void 0;
|
|
1428
|
+
}
|
|
1429
|
+
get apiKeySource() {
|
|
1430
|
+
if (process.env.OPENAI_API_KEY) return "OPENAI_API_KEY";
|
|
1431
|
+
if (process.env.CODEX_API_KEY) return "CODEX_API_KEY";
|
|
1432
|
+
if (fs4.existsSync(path6.join(os2.homedir(), ".codex", "auth.json"))) return "~/.codex/auth.json";
|
|
1433
|
+
return "none";
|
|
1434
|
+
}
|
|
1435
|
+
get isAuthConfigured() {
|
|
1436
|
+
return this.apiKeySource !== "none";
|
|
1437
|
+
}
|
|
1438
|
+
async runSelfTest() {
|
|
1439
|
+
const override = getCodexPathOverride() || "codex";
|
|
1440
|
+
const resolvedPath = await resolveCodexPath(override);
|
|
1441
|
+
const agentFound = resolvedPath !== null;
|
|
1442
|
+
const agentVersion = resolvedPath ? await resolveAgentVersion(resolvedPath) : "unknown";
|
|
1443
|
+
const checks = [
|
|
1444
|
+
{
|
|
1445
|
+
name: "agent_found",
|
|
1446
|
+
passed: agentFound,
|
|
1447
|
+
message: agentFound ? `Found codex at ${resolvedPath}` : `Could not find codex via ${override}`
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
name: "api_key",
|
|
1451
|
+
passed: this.isAuthConfigured,
|
|
1452
|
+
message: this.isAuthConfigured ? `Authentication source available: ${this.apiKeySource}` : "No OPENAI_API_KEY, CODEX_API_KEY, or ~/.codex/auth.json found"
|
|
1453
|
+
}
|
|
1454
|
+
];
|
|
1455
|
+
const overallPassed = checks.every((check) => check.passed);
|
|
1456
|
+
process.stdout.write(
|
|
1457
|
+
`${JSON.stringify(
|
|
1458
|
+
{
|
|
1459
|
+
shim: { name: "codex-shim", version: package_default.version },
|
|
1460
|
+
agent: { name: "codex", version: agentVersion, found: agentFound },
|
|
1461
|
+
checks,
|
|
1462
|
+
overall: {
|
|
1463
|
+
passed: overallPassed,
|
|
1464
|
+
message: overallPassed ? "All checks passed" : "One or more checks failed"
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1467
|
+
null,
|
|
1468
|
+
2
|
|
1469
|
+
)}
|
|
1470
|
+
`
|
|
1471
|
+
);
|
|
1472
|
+
return overallPassed ? 0 : 1;
|
|
1473
|
+
}
|
|
1474
|
+
async run() {
|
|
1475
|
+
const codexPath = await resolveCodexPath(getCodexPathOverride() || "codex");
|
|
1476
|
+
if (!codexPath) {
|
|
1477
|
+
writeStartupError("Agent not found: could not locate codex via CODEX_PATH_OVERRIDE or PATH", this.args.debugDir);
|
|
1478
|
+
}
|
|
1479
|
+
if (!this.isAuthConfigured) {
|
|
1480
|
+
writeStartupError("Missing API key: set OPENAI_API_KEY, CODEX_API_KEY, or ~/.codex/auth.json", this.args.debugDir);
|
|
1481
|
+
}
|
|
908
1482
|
this.codex = new Codex({
|
|
909
|
-
apiKey: this.
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1483
|
+
apiKey: this.resolvedApiKey,
|
|
1484
|
+
codexPathOverride: codexPath,
|
|
1485
|
+
env: Object.fromEntries(
|
|
1486
|
+
Object.entries(process.env).filter((entry) => entry[1] !== void 0)
|
|
1487
|
+
)
|
|
913
1488
|
});
|
|
914
|
-
this.setupSignalHandlers();
|
|
915
|
-
}
|
|
916
|
-
setupSignalHandlers() {
|
|
917
|
-
const handleSignal = () => {
|
|
918
|
-
if (!this.interrupted) {
|
|
919
|
-
this.interrupted = true;
|
|
920
|
-
verboseLog(this.args.verbose, "Received interrupt signal, shutting down gracefully");
|
|
921
|
-
process.exit(0);
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
process.on("SIGINT", handleSignal);
|
|
925
|
-
process.on("SIGTERM", handleSignal);
|
|
926
|
-
}
|
|
927
|
-
async run(prompt) {
|
|
928
|
-
const modelSpec = resolveModel(this.args.model);
|
|
929
|
-
const modelOutput = formatModelOutput(modelSpec);
|
|
930
|
-
const codexModelId = getCodexModelId(modelSpec);
|
|
931
|
-
verboseLog(this.args.verbose, `Resolved model: ${modelOutput}`);
|
|
932
|
-
verboseLog(this.args.verbose, `Codex model ID: ${codexModelId}`);
|
|
933
1489
|
let thread;
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
const resumeSessionId = this.args.resume;
|
|
938
|
-
verboseLog(this.args.verbose, `Resuming session: ${resumeSessionId}`);
|
|
939
|
-
const sessionData = this.sessionManager.loadSession(resumeSessionId);
|
|
940
|
-
const threadId = sessionData.agentSessionId;
|
|
941
|
-
verboseLog(
|
|
942
|
-
this.args.verbose,
|
|
943
|
-
`Found thread ID: ${threadId} for session: ${resumeSessionId}`
|
|
944
|
-
);
|
|
945
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
946
|
-
const codexSessionDir = path4.join(home, ".codex", "sessions");
|
|
947
|
-
let threadExists = false;
|
|
948
|
-
try {
|
|
949
|
-
const { spawnSync } = await import("child_process");
|
|
950
|
-
const result = spawnSync("find", [codexSessionDir, "-name", `*${threadId}*`], {
|
|
951
|
-
encoding: "utf8"
|
|
952
|
-
});
|
|
953
|
-
threadExists = result.stdout.trim().length > 0 && result.status === 0;
|
|
954
|
-
} catch (error) {
|
|
955
|
-
verboseLog(this.args.verbose, `Thread validation error: ${error}`);
|
|
956
|
-
}
|
|
957
|
-
if (!threadExists) {
|
|
958
|
-
throw new Error(
|
|
959
|
-
`Thread not found for session ${resumeSessionId}. Cannot resume non-existent conversation.`
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
thread = this.codex.resumeThread(threadId, this.getThreadOptions());
|
|
963
|
-
sessionId = resumeSessionId;
|
|
964
|
-
} else {
|
|
965
|
-
verboseLog(this.args.verbose, `Starting new session`);
|
|
966
|
-
sessionId = this.sessionManager.generateSessionId();
|
|
967
|
-
thread = this.codex.startThread(this.getThreadOptions());
|
|
1490
|
+
if (this.args.resume) {
|
|
1491
|
+
if (!isValidSessionId(this.args.resume)) {
|
|
1492
|
+
writeStartupError(`Invalid session ID: ${this.args.resume}`, this.args.debugDir);
|
|
968
1493
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1494
|
+
let sessionData;
|
|
1495
|
+
try {
|
|
1496
|
+
sessionData = this.sessionManager.loadSession(this.args.resume);
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
writeStartupError(toErrorMessage(error), this.args.debugDir);
|
|
973
1499
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
input_tokens: 0,
|
|
987
|
-
output_tokens: 0
|
|
988
|
-
},
|
|
989
|
-
workStarted: false,
|
|
990
|
-
hasReceivedDeltas: false,
|
|
991
|
-
lastEmittedAssistantText: "",
|
|
992
|
-
lastEmittedReasoningByItemId: /* @__PURE__ */ new Map(),
|
|
993
|
-
emittedToolIds: /* @__PURE__ */ new Set(),
|
|
994
|
-
toolIdMapping: /* @__PURE__ */ new Map()
|
|
995
|
-
};
|
|
1500
|
+
this.logVerbose(`Resuming session ${this.args.resume} -> ${sessionData.agentSessionId}`);
|
|
1501
|
+
thread = this.codex.resumeThread(sessionData.agentSessionId, this.getThreadOptions());
|
|
1502
|
+
this.sessionId = this.args.resume;
|
|
1503
|
+
} else {
|
|
1504
|
+
thread = this.codex.startThread(this.getThreadOptions());
|
|
1505
|
+
}
|
|
1506
|
+
const systemMessage = createSystemMessage(this.cwd, this.sessionId, this.model.publicModel, this.apiKeySource);
|
|
1507
|
+
emit(systemMessage);
|
|
1508
|
+
this.debug.setSession(this.sessionId, { cwd: this.cwd, model: this.model.publicModel });
|
|
1509
|
+
const startedAt = Date.now();
|
|
1510
|
+
const apiStartedAt = Date.now();
|
|
1511
|
+
this.installSignalHandlers();
|
|
996
1512
|
try {
|
|
997
|
-
|
|
998
|
-
|
|
1513
|
+
const prompt = composePrompt(this.prompt, this.args.appendSystemPrompt);
|
|
1514
|
+
const stream = await thread.runStreamed(prompt, { signal: this.getAbortController().signal });
|
|
1515
|
+
await this.consumeEvents(stream.events);
|
|
1516
|
+
const durationMs = Date.now() - startedAt;
|
|
1517
|
+
const durationApiMs = Date.now() - apiStartedAt;
|
|
1518
|
+
const resultMessage = createResultMessage(
|
|
1519
|
+
true,
|
|
1520
|
+
durationMs,
|
|
1521
|
+
durationApiMs,
|
|
1522
|
+
Math.max(this.runtime.numTurns, 1),
|
|
1523
|
+
this.runtime.finalText || "Completed successfully.",
|
|
1524
|
+
this.sessionId,
|
|
1525
|
+
this.runtime.lastUsage
|
|
1526
|
+
);
|
|
1527
|
+
emit(resultMessage);
|
|
1528
|
+
this.debug.logResult("success", {
|
|
1529
|
+
session_id: this.sessionId,
|
|
1530
|
+
usage: this.runtime.lastUsage
|
|
1531
|
+
});
|
|
1532
|
+
await flushStdout();
|
|
1533
|
+
return 0;
|
|
999
1534
|
} catch (error) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1535
|
+
const summary = classifyRuntimeError(error);
|
|
1536
|
+
const durationMs = Date.now() - startedAt;
|
|
1537
|
+
const durationApiMs = Date.now() - apiStartedAt;
|
|
1538
|
+
emit(
|
|
1539
|
+
createAssistantMessage(
|
|
1540
|
+
"<synthetic>",
|
|
1541
|
+
[{ type: "text", text: summary }],
|
|
1542
|
+
null,
|
|
1543
|
+
void 0,
|
|
1544
|
+
NIL_UUID
|
|
1545
|
+
)
|
|
1546
|
+
);
|
|
1547
|
+
const resultMessage = createResultMessage(
|
|
1548
|
+
false,
|
|
1549
|
+
durationMs,
|
|
1550
|
+
durationApiMs,
|
|
1551
|
+
Math.max(this.runtime.numTurns, 1),
|
|
1552
|
+
summary,
|
|
1553
|
+
this.sessionId,
|
|
1554
|
+
this.runtime.lastUsage
|
|
1555
|
+
);
|
|
1556
|
+
emit(resultMessage);
|
|
1557
|
+
this.debug.logLine(summary);
|
|
1558
|
+
this.debug.logResult("error", {
|
|
1559
|
+
session_id: this.sessionId,
|
|
1560
|
+
error: summary
|
|
1561
|
+
});
|
|
1006
1562
|
await flushStdout();
|
|
1007
|
-
|
|
1563
|
+
return 1;
|
|
1008
1564
|
}
|
|
1009
|
-
await flushStdout();
|
|
1010
|
-
process.exit(0);
|
|
1011
1565
|
}
|
|
1012
1566
|
getThreadOptions() {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
strict: "read-only"
|
|
1018
|
-
};
|
|
1019
|
-
const options = {
|
|
1567
|
+
return {
|
|
1568
|
+
model: this.model.sdkModel,
|
|
1569
|
+
modelReasoningEffort: this.model.reasoningEffort,
|
|
1570
|
+
sandboxMode: mapSandbox(this.args.sandbox),
|
|
1020
1571
|
workingDirectory: this.cwd,
|
|
1021
1572
|
skipGitRepoCheck: true,
|
|
1022
|
-
|
|
1023
|
-
// Auto-approve all operations (no user present)
|
|
1024
|
-
approvalPolicy: "never",
|
|
1025
|
-
sandboxMode: sandboxModeMap[this.args.sandbox],
|
|
1026
|
-
networkAccessEnabled: true,
|
|
1027
|
-
webSearchEnabled: true,
|
|
1028
|
-
modelReasoningEffort: modelSpec.reasoningEffort || "high"
|
|
1573
|
+
approvalPolicy: "never"
|
|
1029
1574
|
};
|
|
1030
|
-
return options;
|
|
1031
|
-
}
|
|
1032
|
-
emitSystemInit(sessionId, model) {
|
|
1033
|
-
let apiKeySource;
|
|
1034
|
-
switch (this.authConfig.source) {
|
|
1035
|
-
case "env":
|
|
1036
|
-
case "CODEX_API_KEY":
|
|
1037
|
-
case "OPENAI_API_KEY":
|
|
1038
|
-
apiKeySource = "env";
|
|
1039
|
-
break;
|
|
1040
|
-
default:
|
|
1041
|
-
apiKeySource = "none";
|
|
1042
|
-
}
|
|
1043
|
-
const message = {
|
|
1044
|
-
type: "system",
|
|
1045
|
-
subtype: "init",
|
|
1046
|
-
cwd: this.cwd,
|
|
1047
|
-
session_id: sessionId,
|
|
1048
|
-
tools: [...STANDARD_TOOLS],
|
|
1049
|
-
model,
|
|
1050
|
-
permissionMode: "bypassPermissions",
|
|
1051
|
-
apiKeySource,
|
|
1052
|
-
mcp_servers: []
|
|
1053
|
-
};
|
|
1054
|
-
emit(message);
|
|
1055
|
-
verboseLog(this.args.verbose, "Emitted system init");
|
|
1056
1575
|
}
|
|
1057
|
-
async
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
this.apiStartTime = state.apiStartTime;
|
|
1067
|
-
const { events } = await state.thread.runStreamed(finalPrompt);
|
|
1068
|
-
const timedEvents = withIdleTimeout(events, this.args.idleTimeout * 1e3);
|
|
1069
|
-
const currentMessageContent = [];
|
|
1070
|
-
let currentMessageId = generateMessageId();
|
|
1071
|
-
const pendingToolResults = /* @__PURE__ */ new Map();
|
|
1072
|
-
let systemInitEmitted = false;
|
|
1576
|
+
async consumeEvents(events) {
|
|
1577
|
+
const idleTimeoutMs = this.args.idleTimeout * 1e3;
|
|
1578
|
+
const timedEvents = withAdaptiveTimeout(events, {
|
|
1579
|
+
idleTimeoutMs,
|
|
1580
|
+
busyTimeoutMs: Math.max(idleTimeoutMs, 3e5),
|
|
1581
|
+
onEvent: (event, controller) => {
|
|
1582
|
+
updateCodexTimeoutState(event, controller, idleTimeoutMs);
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1073
1585
|
for await (const event of timedEvents) {
|
|
1074
|
-
|
|
1075
|
-
|
|
1586
|
+
this.debug.logSdkEvent(event);
|
|
1587
|
+
this.logVerbose(`SDK event: ${event.type}`);
|
|
1588
|
+
await this.handleEvent(event);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
async handleEvent(event) {
|
|
1592
|
+
switch (event.type) {
|
|
1593
|
+
case "thread.started": {
|
|
1594
|
+
this.sessionManager.saveSession({
|
|
1595
|
+
sessionId: this.sessionId,
|
|
1596
|
+
agentSessionId: event.thread_id,
|
|
1597
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1598
|
+
metadata: {
|
|
1599
|
+
model: this.model.publicModel,
|
|
1600
|
+
cwd: this.cwd
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1076
1603
|
break;
|
|
1077
1604
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
this.saveRawEvent(state.sessionId, event);
|
|
1081
|
-
if (event.type === "thread.started") {
|
|
1082
|
-
this.createRawLogFile(state.sessionId);
|
|
1083
|
-
}
|
|
1605
|
+
case "turn.started": {
|
|
1606
|
+
break;
|
|
1084
1607
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1608
|
+
case "turn.completed": {
|
|
1609
|
+
this.runtime.numTurns += 1;
|
|
1610
|
+
this.runtime.lastUsage = usageToTokenUsage(event.usage);
|
|
1611
|
+
break;
|
|
1089
1612
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
verboseLog(this.args.verbose, `Thread started with Codex ID: ${event.thread_id}`);
|
|
1094
|
-
verboseLog(this.args.verbose, `Using session ID: ${state.sessionId}`);
|
|
1095
|
-
if (!systemInitEmitted) {
|
|
1096
|
-
this.emitSystemInit(state.sessionId, model);
|
|
1097
|
-
systemInitEmitted = true;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
break;
|
|
1101
|
-
case "turn.started":
|
|
1102
|
-
state.numTurns++;
|
|
1103
|
-
verboseLog(this.args.verbose, `Turn ${state.numTurns} started`);
|
|
1104
|
-
break;
|
|
1105
|
-
case "item.started":
|
|
1106
|
-
case "item.updated":
|
|
1107
|
-
case "item.completed":
|
|
1108
|
-
this.handleItemEvent(
|
|
1109
|
-
event.item,
|
|
1110
|
-
state,
|
|
1111
|
-
currentMessageContent,
|
|
1112
|
-
pendingToolResults,
|
|
1113
|
-
model,
|
|
1114
|
-
currentMessageId,
|
|
1115
|
-
event.type === "item.completed"
|
|
1116
|
-
);
|
|
1117
|
-
break;
|
|
1118
|
-
case "turn.completed": {
|
|
1119
|
-
if (event.usage) {
|
|
1120
|
-
state.totalUsage.input_tokens += event.usage.input_tokens;
|
|
1121
|
-
state.totalUsage.output_tokens += event.usage.output_tokens;
|
|
1122
|
-
if (event.usage.cached_input_tokens) {
|
|
1123
|
-
state.totalUsage.cache_read_input_tokens = (state.totalUsage.cache_read_input_tokens || 0) + event.usage.cached_input_tokens;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
verboseLog(this.args.verbose, "Turn completed", event.usage);
|
|
1127
|
-
const completionContent = state.hasReceivedDeltas ? [] : currentMessageContent;
|
|
1128
|
-
if (completionContent.length > 0 || event.usage) {
|
|
1129
|
-
this.emitAssistantMessage(
|
|
1130
|
-
currentMessageId,
|
|
1131
|
-
model,
|
|
1132
|
-
completionContent,
|
|
1133
|
-
event.usage,
|
|
1134
|
-
"end_turn"
|
|
1135
|
-
);
|
|
1136
|
-
}
|
|
1137
|
-
for (const result of pendingToolResults.values()) {
|
|
1138
|
-
this.emitToolResult(result);
|
|
1139
|
-
}
|
|
1140
|
-
pendingToolResults.clear();
|
|
1141
|
-
currentMessageContent.length = 0;
|
|
1142
|
-
state.hasReceivedDeltas = false;
|
|
1143
|
-
state.lastEmittedAssistantText = "";
|
|
1144
|
-
state.lastEmittedReasoningByItemId.clear();
|
|
1145
|
-
currentMessageId = generateMessageId();
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
|
-
case "turn.failed":
|
|
1149
|
-
verboseLog(this.args.verbose, "Turn failed:", event.error);
|
|
1150
|
-
if (this.args.debugDir) {
|
|
1151
|
-
this.saveRuntimeError(
|
|
1152
|
-
state.sessionId,
|
|
1153
|
-
new Error(`Turn failed: ${event.error.message}`)
|
|
1154
|
-
);
|
|
1155
|
-
}
|
|
1156
|
-
throw new Error(`Turn failed: ${event.error.message}`);
|
|
1157
|
-
case "error":
|
|
1158
|
-
verboseLog(this.args.verbose, `Thread error: ${event.message}`);
|
|
1159
|
-
if (this.args.debugDir) {
|
|
1160
|
-
this.saveRuntimeError(state.sessionId, new Error(`Thread error: ${event.message}`));
|
|
1161
|
-
}
|
|
1162
|
-
break;
|
|
1613
|
+
case "turn.failed": {
|
|
1614
|
+
this.runtime.numTurns += 1;
|
|
1615
|
+
throw new Error(event.error.message);
|
|
1163
1616
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
handleItemEvent(item, state, currentMessageContent, pendingToolResults, model, currentMessageId, _isCompleted = false) {
|
|
1173
|
-
switch (item.type) {
|
|
1174
|
-
case "agent_message":
|
|
1175
|
-
if (item.text) {
|
|
1176
|
-
const previousText = state.lastEmittedAssistantText || "";
|
|
1177
|
-
const deltaText = item.text.startsWith(previousText) ? item.text.slice(previousText.length) : item.text;
|
|
1178
|
-
if (deltaText) {
|
|
1179
|
-
this.emitAssistantMessage(
|
|
1180
|
-
currentMessageId,
|
|
1181
|
-
model,
|
|
1182
|
-
[{ type: "text", text: deltaText }],
|
|
1183
|
-
void 0,
|
|
1184
|
-
null
|
|
1185
|
-
);
|
|
1186
|
-
state.hasReceivedDeltas = true;
|
|
1187
|
-
}
|
|
1188
|
-
state.lastEmittedAssistantText = item.text;
|
|
1189
|
-
const existing = currentMessageContent.find((c) => c.type === "text");
|
|
1190
|
-
if (existing && existing.type === "text") {
|
|
1191
|
-
existing.text = item.text;
|
|
1192
|
-
} else {
|
|
1193
|
-
currentMessageContent.push({ type: "text", text: item.text });
|
|
1194
|
-
}
|
|
1617
|
+
case "error": {
|
|
1618
|
+
throw new Error(event.message);
|
|
1619
|
+
}
|
|
1620
|
+
case "item.started": {
|
|
1621
|
+
if (event.item.type !== "web_search") {
|
|
1622
|
+
await this.emitToolUseIfNeeded(event.item);
|
|
1195
1623
|
}
|
|
1196
1624
|
break;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
const deltaThinking = item.text.startsWith(previousReasoning) ? item.text.slice(previousReasoning.length) : item.text;
|
|
1202
|
-
if (deltaThinking) {
|
|
1203
|
-
this.emitAssistantMessage(
|
|
1204
|
-
currentMessageId,
|
|
1205
|
-
model,
|
|
1206
|
-
[{ type: "thinking", thinking: deltaThinking }],
|
|
1207
|
-
void 0,
|
|
1208
|
-
null
|
|
1209
|
-
);
|
|
1210
|
-
state.hasReceivedDeltas = true;
|
|
1211
|
-
}
|
|
1212
|
-
state.lastEmittedReasoningByItemId.set(reasoningItemId, item.text);
|
|
1213
|
-
currentMessageContent.push({ type: "thinking", thinking: item.text });
|
|
1625
|
+
}
|
|
1626
|
+
case "item.updated": {
|
|
1627
|
+
if (event.item.type !== "web_search") {
|
|
1628
|
+
await this.emitToolUseIfNeeded(event.item);
|
|
1214
1629
|
}
|
|
1215
1630
|
break;
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
state,
|
|
1220
|
-
currentMessageContent,
|
|
1221
|
-
pendingToolResults,
|
|
1222
|
-
model,
|
|
1223
|
-
currentMessageId
|
|
1224
|
-
);
|
|
1225
|
-
break;
|
|
1226
|
-
case "mcp_tool_call":
|
|
1227
|
-
this.handleMcpToolCall(
|
|
1228
|
-
item,
|
|
1229
|
-
state,
|
|
1230
|
-
currentMessageContent,
|
|
1231
|
-
pendingToolResults,
|
|
1232
|
-
model,
|
|
1233
|
-
currentMessageId
|
|
1234
|
-
);
|
|
1631
|
+
}
|
|
1632
|
+
case "item.completed": {
|
|
1633
|
+
await this.handleCompletedItem(event.item);
|
|
1235
1634
|
break;
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
item,
|
|
1239
|
-
state,
|
|
1240
|
-
currentMessageContent,
|
|
1241
|
-
pendingToolResults,
|
|
1242
|
-
model,
|
|
1243
|
-
currentMessageId
|
|
1244
|
-
);
|
|
1635
|
+
}
|
|
1636
|
+
default:
|
|
1245
1637
|
break;
|
|
1246
1638
|
}
|
|
1247
1639
|
}
|
|
1248
|
-
|
|
1249
|
-
if (
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
if (!toolUseId) return;
|
|
1265
|
-
let content = item.aggregated_output || "";
|
|
1266
|
-
if (!content && item.status === "completed" && item.exit_code === 0) {
|
|
1267
|
-
content = `Command executed successfully (exit code: 0)`;
|
|
1268
|
-
} else if (!content && item.status === "failed") {
|
|
1269
|
-
content = `Command failed (exit code: ${item.exit_code || "unknown"})`;
|
|
1270
|
-
}
|
|
1271
|
-
pendingToolResults.set(item.id, {
|
|
1272
|
-
type: "tool_result",
|
|
1273
|
-
tool_use_id: toolUseId,
|
|
1274
|
-
content: item.status === "failed" ? { is_error: true, error: content } : content
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
handleMcpToolCall(item, state, currentMessageContent, pendingToolResults, model, currentMessageId) {
|
|
1278
|
-
if (!state.emittedToolIds.has(item.id)) {
|
|
1279
|
-
const toolUseId2 = generateToolUseId();
|
|
1280
|
-
state.emittedToolIds.add(item.id);
|
|
1281
|
-
state.toolIdMapping.set(item.id, toolUseId2);
|
|
1282
|
-
const toolName = normalizeToolName(item.tool);
|
|
1283
|
-
const toolUse = {
|
|
1284
|
-
type: "tool_use",
|
|
1285
|
-
id: toolUseId2,
|
|
1286
|
-
name: toolName,
|
|
1287
|
-
input: normalizeToolInput(item.arguments || {})
|
|
1288
|
-
};
|
|
1289
|
-
currentMessageContent.push(toolUse);
|
|
1290
|
-
this.emitAssistantMessage(currentMessageId, model, [toolUse], void 0, null);
|
|
1291
|
-
state.hasReceivedDeltas = true;
|
|
1292
|
-
}
|
|
1293
|
-
const toolUseId = state.toolIdMapping.get(item.id);
|
|
1294
|
-
if (!toolUseId) return;
|
|
1295
|
-
let content = "";
|
|
1296
|
-
if (item.result?.content) {
|
|
1297
|
-
content = item.result.content.map((block) => {
|
|
1298
|
-
if (block.type === "text") return block.text;
|
|
1299
|
-
return JSON.stringify(block);
|
|
1300
|
-
}).join("\n");
|
|
1301
|
-
}
|
|
1302
|
-
pendingToolResults.set(item.id, {
|
|
1303
|
-
type: "tool_result",
|
|
1304
|
-
tool_use_id: toolUseId,
|
|
1305
|
-
content: item.status === "failed" ? { is_error: true, error: item.error?.message || "Failed" } : content
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
handleFileChange(item, state, currentMessageContent, pendingToolResults, model, currentMessageId) {
|
|
1309
|
-
if (!state.emittedToolIds.has(item.id)) {
|
|
1310
|
-
const toolUseId2 = generateToolUseId();
|
|
1311
|
-
state.emittedToolIds.add(item.id);
|
|
1312
|
-
state.toolIdMapping.set(item.id, toolUseId2);
|
|
1313
|
-
const change2 = item.changes[0];
|
|
1314
|
-
const toolName = change2.kind === "add" ? "Write" : "Edit";
|
|
1315
|
-
const toolUse = {
|
|
1316
|
-
type: "tool_use",
|
|
1317
|
-
id: toolUseId2,
|
|
1318
|
-
name: toolName,
|
|
1319
|
-
input: { file_path: change2.path }
|
|
1320
|
-
};
|
|
1321
|
-
currentMessageContent.push(toolUse);
|
|
1322
|
-
this.emitAssistantMessage(currentMessageId, model, [toolUse], void 0, null);
|
|
1323
|
-
state.hasReceivedDeltas = true;
|
|
1324
|
-
}
|
|
1325
|
-
const toolUseId = state.toolIdMapping.get(item.id);
|
|
1326
|
-
if (!toolUseId) return;
|
|
1327
|
-
const change = item.changes[0];
|
|
1328
|
-
const content = `${change.kind}: ${change.path}`;
|
|
1329
|
-
pendingToolResults.set(item.id, {
|
|
1330
|
-
type: "tool_result",
|
|
1331
|
-
tool_use_id: toolUseId,
|
|
1332
|
-
content: item.status === "failed" ? { is_error: true, error: "File change failed" } : content
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
emitAssistantMessage(messageId, model, content, usage, stopReason) {
|
|
1336
|
-
const message = {
|
|
1337
|
-
type: "assistant",
|
|
1338
|
-
message: {
|
|
1339
|
-
id: messageId,
|
|
1340
|
-
type: "message",
|
|
1341
|
-
role: "assistant",
|
|
1342
|
-
model,
|
|
1343
|
-
content,
|
|
1344
|
-
usage: usage ? {
|
|
1345
|
-
input_tokens: usage.input_tokens,
|
|
1346
|
-
output_tokens: usage.output_tokens,
|
|
1347
|
-
cache_read_input_tokens: usage.cached_input_tokens
|
|
1348
|
-
} : void 0,
|
|
1349
|
-
stop_reason: stopReason || null
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
emit(message);
|
|
1353
|
-
verboseLog(this.args.verbose, "Emitted assistant message");
|
|
1354
|
-
}
|
|
1355
|
-
emitToolResult(result) {
|
|
1356
|
-
const message = {
|
|
1357
|
-
type: "user",
|
|
1358
|
-
message: {
|
|
1359
|
-
role: "user",
|
|
1360
|
-
content: [result]
|
|
1361
|
-
}
|
|
1362
|
-
};
|
|
1363
|
-
emit(message);
|
|
1364
|
-
verboseLog(this.args.verbose, "Emitted tool result");
|
|
1365
|
-
}
|
|
1366
|
-
emitSyntheticError(error, _model) {
|
|
1367
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1368
|
-
const message = {
|
|
1369
|
-
type: "assistant",
|
|
1370
|
-
message: {
|
|
1371
|
-
id: NIL_UUID,
|
|
1372
|
-
type: "message",
|
|
1373
|
-
role: "assistant",
|
|
1374
|
-
model: "<synthetic>",
|
|
1375
|
-
content: [
|
|
1640
|
+
async handleCompletedItem(item) {
|
|
1641
|
+
if (item.type === "reasoning") {
|
|
1642
|
+
emit(
|
|
1643
|
+
createAssistantMessage(this.model.publicModel, [
|
|
1644
|
+
{
|
|
1645
|
+
type: "thinking",
|
|
1646
|
+
thinking: item.text
|
|
1647
|
+
}
|
|
1648
|
+
])
|
|
1649
|
+
);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
if (item.type === "agent_message") {
|
|
1653
|
+
this.runtime.finalText = item.text;
|
|
1654
|
+
emit(
|
|
1655
|
+
createAssistantMessage(this.model.publicModel, [
|
|
1376
1656
|
{
|
|
1377
1657
|
type: "text",
|
|
1378
|
-
text:
|
|
1658
|
+
text: item.text
|
|
1379
1659
|
}
|
|
1380
|
-
],
|
|
1381
|
-
|
|
1660
|
+
], "end_turn")
|
|
1661
|
+
);
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
if (item.type === "error") {
|
|
1665
|
+
this.logVerbose(`Codex non-fatal error item: ${item.message}`);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
if (shouldTreatAsToolItem(item)) {
|
|
1669
|
+
const toolId = await this.emitToolUseIfNeeded(item);
|
|
1670
|
+
const normalizedResult = normalizeToolResult(item, this.cwd);
|
|
1671
|
+
if (!toolId || !normalizedResult) {
|
|
1672
|
+
return;
|
|
1382
1673
|
}
|
|
1383
|
-
|
|
1384
|
-
emit(message);
|
|
1385
|
-
verboseLog(this.args.verbose, "Emitted synthetic error");
|
|
1386
|
-
}
|
|
1387
|
-
emitResult(state, isError, result) {
|
|
1388
|
-
const duration = Date.now() - state.startTime;
|
|
1389
|
-
const apiDuration = state.apiStartTime > 0 ? Date.now() - state.apiStartTime : 0;
|
|
1390
|
-
const message = {
|
|
1391
|
-
type: "result",
|
|
1392
|
-
subtype: isError ? "error" : "success",
|
|
1393
|
-
is_error: isError,
|
|
1394
|
-
duration_ms: duration,
|
|
1395
|
-
duration_api_ms: apiDuration,
|
|
1396
|
-
num_turns: state.numTurns,
|
|
1397
|
-
result,
|
|
1398
|
-
session_id: state.sessionId,
|
|
1399
|
-
usage: state.totalUsage
|
|
1400
|
-
};
|
|
1401
|
-
emit(message);
|
|
1402
|
-
verboseLog(this.args.verbose, "Emitted result");
|
|
1403
|
-
}
|
|
1404
|
-
saveSession(threadId, state) {
|
|
1405
|
-
try {
|
|
1406
|
-
const sessionData = {
|
|
1407
|
-
sessionId: state.sessionId,
|
|
1408
|
-
agentSessionId: threadId,
|
|
1409
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1410
|
-
metadata: {
|
|
1411
|
-
totalUsage: state.totalUsage,
|
|
1412
|
-
numTurns: state.numTurns
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
this.sessionManager.saveSession(sessionData);
|
|
1416
|
-
verboseLog(this.args.verbose, `Saved session to ${this.sessionManager.getSessionsDir()}`);
|
|
1417
|
-
} catch (error) {
|
|
1418
|
-
verboseLog(this.args.verbose, `Failed to save session: ${error}`);
|
|
1674
|
+
emit(createToolResultMessage(toolId, normalizedResult.content));
|
|
1419
1675
|
}
|
|
1420
1676
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1677
|
+
async emitToolUseIfNeeded(item) {
|
|
1678
|
+
if (!shouldTreatAsToolItem(item)) {
|
|
1679
|
+
return void 0;
|
|
1680
|
+
}
|
|
1681
|
+
const existing = this.toolIdMap.get(item.id);
|
|
1682
|
+
if (existing) {
|
|
1683
|
+
return existing;
|
|
1684
|
+
}
|
|
1685
|
+
const normalizedTool = normalizeToolUse(item, this.cwd);
|
|
1686
|
+
if (!normalizedTool) {
|
|
1687
|
+
return void 0;
|
|
1688
|
+
}
|
|
1689
|
+
const publicId = generateToolUseId();
|
|
1690
|
+
this.toolIdMap.set(item.id, publicId);
|
|
1691
|
+
emit(
|
|
1692
|
+
createAssistantMessage(this.model.publicModel, [
|
|
1693
|
+
{
|
|
1694
|
+
type: "tool_use",
|
|
1695
|
+
id: publicId,
|
|
1696
|
+
name: normalizedTool.name,
|
|
1697
|
+
...normalizedTool.input ? { input: normalizedTool.input } : {}
|
|
1698
|
+
}
|
|
1699
|
+
], "tool_use")
|
|
1700
|
+
);
|
|
1701
|
+
return publicId;
|
|
1432
1702
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
fs4.mkdirSync(this.args.debugDir, { recursive: true });
|
|
1437
|
-
const errorLogPath = path4.join(this.args.debugDir, "session-unknown.raw.log");
|
|
1438
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1439
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1440
|
-
fs4.appendFileSync(errorLogPath, `[${timestamp}] ${errorMessage}
|
|
1441
|
-
`);
|
|
1442
|
-
} catch (err) {
|
|
1443
|
-
verboseLog(this.args.verbose, `Failed to save pre-init error: ${err}`);
|
|
1703
|
+
getAbortController() {
|
|
1704
|
+
if (!this.currentAbortController) {
|
|
1705
|
+
this.currentAbortController = new AbortController();
|
|
1444
1706
|
}
|
|
1707
|
+
return this.currentAbortController;
|
|
1445
1708
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
if (
|
|
1449
|
-
|
|
1450
|
-
const errorLogPath = path4.join(this.args.debugDir, `session-${sessionId}.raw.log`);
|
|
1451
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1452
|
-
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
1453
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1454
|
-
let logEntry = `[${timestamp}] RUNTIME ERROR: ${errorMessage}
|
|
1455
|
-
`;
|
|
1456
|
-
if (errorStack) {
|
|
1457
|
-
logEntry += `Stack trace:
|
|
1458
|
-
${errorStack}
|
|
1459
|
-
`;
|
|
1709
|
+
installSignalHandlers() {
|
|
1710
|
+
const handle = (signal) => {
|
|
1711
|
+
if (this.interrupted) {
|
|
1712
|
+
return;
|
|
1460
1713
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1714
|
+
this.interrupted = true;
|
|
1715
|
+
this.logVerbose(`Received ${signal}, aborting current turn`);
|
|
1716
|
+
this.debug.logLine(`Received ${signal}, aborting current turn`);
|
|
1717
|
+
this.currentAbortController?.abort();
|
|
1718
|
+
};
|
|
1719
|
+
process.once("SIGINT", () => handle("SIGINT"));
|
|
1720
|
+
process.once("SIGTERM", () => handle("SIGTERM"));
|
|
1465
1721
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
const rawLogPath = path4.join(this.args.debugDir, `session-${sessionId}.raw.log`);
|
|
1470
|
-
if (!fs4.existsSync(rawLogPath)) {
|
|
1471
|
-
fs4.writeFileSync(
|
|
1472
|
-
rawLogPath,
|
|
1473
|
-
`# Codex SDK raw log (session ${sessionId})
|
|
1474
|
-
# Note: SDK doesn't expose agent stderr
|
|
1475
|
-
`
|
|
1476
|
-
);
|
|
1477
|
-
}
|
|
1478
|
-
} catch (error) {
|
|
1479
|
-
verboseLog(this.args.verbose, `Failed to create raw log file: ${error}`);
|
|
1722
|
+
logVerbose(message) {
|
|
1723
|
+
if (!this.args.verbose) {
|
|
1724
|
+
return;
|
|
1480
1725
|
}
|
|
1726
|
+
process.stderr.write(`${message}
|
|
1727
|
+
`);
|
|
1728
|
+
this.debug.logLine(message);
|
|
1481
1729
|
}
|
|
1482
1730
|
};
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
});
|
|
1492
|
-
process.stdin.on("end", () => {
|
|
1493
|
-
resolve(data.trim());
|
|
1494
|
-
});
|
|
1495
|
-
process.stdin.on("error", (err) => {
|
|
1496
|
-
reject(err);
|
|
1497
|
-
});
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
function printVersion() {
|
|
1501
|
-
console.log("codex-shim 1.0.0");
|
|
1502
|
-
}
|
|
1503
|
-
function printHelp() {
|
|
1504
|
-
console.log(`
|
|
1505
|
-
codex-shim - OpenAI Codex shim for standardized agent interface
|
|
1506
|
-
|
|
1507
|
-
USAGE:
|
|
1508
|
-
echo "prompt" | codex-shim --model <model>[-<reasoning>] [options]
|
|
1509
|
-
|
|
1510
|
-
REQUIRED:
|
|
1511
|
-
--model <model> OpenAI model identifier (shortname, openai/model, or full ID)
|
|
1512
|
-
Optionally append reasoning effort:
|
|
1513
|
-
-minimal, -low, -medium, -high, or -xhigh
|
|
1514
|
-
|
|
1515
|
-
OPTIONS:
|
|
1516
|
-
-p Indicates prompt via stdin (optional, stdin always read)
|
|
1517
|
-
--resume <session_id> Continue existing session
|
|
1518
|
-
--verbose Enable verbose logging to stderr
|
|
1519
|
-
--append-system-prompt Additional system prompt to append
|
|
1520
|
-
--idle-timeout <seconds> Max seconds between agent events before aborting (default: 120)
|
|
1521
|
-
--debug-dir <path> Directory for debug logs and session data
|
|
1522
|
-
--sandbox <level> Sandbox level: none (default), standard, or strict
|
|
1523
|
-
--self-test Run environment verification
|
|
1524
|
-
--version Print version and exit
|
|
1525
|
-
--help Print this help and exit
|
|
1526
|
-
|
|
1527
|
-
ENVIRONMENT:
|
|
1528
|
-
MODEL Default model if --model not provided
|
|
1529
|
-
OPENAI_API_KEY OpenAI API key for authentication
|
|
1530
|
-
CODEX_API_KEY Codex-specific API key (alternative)
|
|
1531
|
-
|
|
1532
|
-
EXAMPLES:
|
|
1533
|
-
echo "Hello" | codex-shim --model codex
|
|
1534
|
-
echo "Complex task" | codex-shim --model codex-high --verbose
|
|
1535
|
-
echo "Fix bug" | codex-shim --model gpt-5.2-xhigh
|
|
1536
|
-
echo "Continue" | codex-shim --model codex --resume <session-id>
|
|
1731
|
+
async function main(argv = process.argv.slice(2)) {
|
|
1732
|
+
const args = parseArgs(argv, { "-h": "--help" });
|
|
1733
|
+
if (args.help) {
|
|
1734
|
+
process.stdout.write(HELP_TEXT);
|
|
1735
|
+
return 0;
|
|
1736
|
+
}
|
|
1737
|
+
if (args.version) {
|
|
1738
|
+
process.stdout.write(`${package_default.version}
|
|
1537
1739
|
`);
|
|
1740
|
+
return 0;
|
|
1741
|
+
}
|
|
1742
|
+
if (args.selfTest) {
|
|
1743
|
+
return await new CodexShim(args, "").runSelfTest();
|
|
1744
|
+
}
|
|
1745
|
+
const prompt = await readStdin();
|
|
1746
|
+
if (!prompt) {
|
|
1747
|
+
return 0;
|
|
1748
|
+
}
|
|
1749
|
+
const shim = new CodexShim(args, prompt);
|
|
1750
|
+
return await shim.run();
|
|
1538
1751
|
}
|
|
1539
|
-
|
|
1540
|
-
// src/index.ts
|
|
1541
|
-
async function main() {
|
|
1752
|
+
async function runCli() {
|
|
1542
1753
|
try {
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
}
|
|
1547
|
-
if (args.version) {
|
|
1548
|
-
printVersion();
|
|
1549
|
-
process.exit(0);
|
|
1550
|
-
}
|
|
1551
|
-
if (args.help) {
|
|
1552
|
-
printHelp();
|
|
1553
|
-
process.exit(0);
|
|
1554
|
-
}
|
|
1555
|
-
if (args.selfTest) {
|
|
1556
|
-
const result = await runSelfTest();
|
|
1557
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1558
|
-
process.exit(result.overall.passed ? 0 : 1);
|
|
1559
|
-
}
|
|
1560
|
-
if (!args.model) {
|
|
1561
|
-
console.error("Error: --model argument is required");
|
|
1562
|
-
process.exit(1);
|
|
1563
|
-
}
|
|
1564
|
-
const prompt = await readStdin();
|
|
1565
|
-
if (!prompt) {
|
|
1566
|
-
verboseLog(args.verbose, "Empty prompt, exiting silently");
|
|
1567
|
-
process.exit(0);
|
|
1568
|
-
}
|
|
1569
|
-
verboseLog(args.verbose, "Starting Codex shim");
|
|
1570
|
-
verboseLog(args.verbose, `Model: ${args.model}`);
|
|
1571
|
-
verboseLog(args.verbose, `Prompt length: ${prompt.length} characters`);
|
|
1572
|
-
if (args.resume) {
|
|
1573
|
-
verboseLog(args.verbose, `Resuming session: ${args.resume}`);
|
|
1574
|
-
}
|
|
1575
|
-
const shim = new CodexShim(args);
|
|
1576
|
-
await shim.run(prompt);
|
|
1754
|
+
const exitCode = await main();
|
|
1755
|
+
await flushStdout();
|
|
1756
|
+
process.exit(exitCode);
|
|
1577
1757
|
} catch (error) {
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1758
|
+
const message = toErrorMessage(error);
|
|
1759
|
+
process.stderr.write(`${message}
|
|
1760
|
+
`);
|
|
1761
|
+
await flushStdout();
|
|
1580
1762
|
process.exit(1);
|
|
1581
1763
|
}
|
|
1582
1764
|
}
|
|
1583
|
-
|
|
1765
|
+
|
|
1766
|
+
// src/index.ts
|
|
1767
|
+
await runCli();
|