@codex-native/sdk 0.0.1 → 0.0.3

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/dist/cli.cjs ADDED
@@ -0,0 +1,3510 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
30
+ // src/cli/index.ts
31
+ var import_node_process3 = __toESM(require("process"));
32
+ var import_node_util2 = require("util");
33
+ var import_node_url4 = require("url");
34
+
35
+ // package.json
36
+ var package_default = {
37
+ name: "@codex-native/sdk",
38
+ version: "0.0.3",
39
+ description: "Native NAPI-based Codex SDK - complete standalone implementation.",
40
+ main: "dist/index.cjs",
41
+ module: "dist/index.mjs",
42
+ types: "dist/index.d.ts",
43
+ exports: {
44
+ ".": {
45
+ types: "./dist/index.d.ts",
46
+ import: "./dist/index.mjs",
47
+ require: "./dist/index.cjs"
48
+ }
49
+ },
50
+ bin: {
51
+ "codex-native": "dist/cli.cjs"
52
+ },
53
+ files: [
54
+ "dist",
55
+ "README.md",
56
+ "npm",
57
+ "*.node",
58
+ "index.js",
59
+ "index.d.ts"
60
+ ],
61
+ dependencies: {
62
+ "@modelcontextprotocol/sdk": "^1.22.0",
63
+ "@opencode-ai/sdk": "^1.0.68",
64
+ "@toon-format/toon": "^1.0.0",
65
+ pyright: "^1.1.386",
66
+ "typescript-language-server": "^4.3.3",
67
+ "vscode-jsonrpc": "^8.2.1",
68
+ "vscode-languageserver-types": "^3.17.5"
69
+ },
70
+ scripts: {
71
+ clean: "rm -rf dist *.node npm target/release/*.node && cargo clean --release",
72
+ "clean:all": "rm -rf dist *.node npm target node_modules && cargo clean",
73
+ build: "npm run build:rust && npm run build:napi && npm run build:ts && npm run verify:build",
74
+ "build:ci": "npm run build:napi && npm run build:ts && npm run verify:build",
75
+ "build:rust": "cargo fmt && cargo clippy --release --features napi-bindings -- -D warnings",
76
+ "build:napi": "napi build --platform --release --features napi-bindings",
77
+ "build:napi:debug": "napi build --platform --features napi-bindings",
78
+ "build:ts": "tsup",
79
+ "verify:build": `node -e "const fs=require('fs');const hasNode=fs.readdirSync('.').some(f=>f.endsWith('.node'));const hasDist=fs.existsSync('dist/index.mjs')&&fs.existsSync('dist/index.d.ts');const hasLoader=fs.existsSync('index.js');if(!hasNode||!hasDist||!hasLoader){console.error('Build incomplete: missing .node, dist, or loader index.js');process.exit(1)}require('./index.js');console.log('\u2713 Build verification passed')"`,
80
+ "verify:pack": "node ./scripts/verify-pack.mjs",
81
+ "create-npm-dirs": "napi create-npm-dirs",
82
+ artifacts: "napi artifacts",
83
+ prepublishOnly: "napi prepublish -t npm",
84
+ pretest: `node -e "const fs=require('fs');const hasNode=fs.readdirSync('.').some(f=>f.endsWith('.node'));if(!hasNode){process.exitCode=1;console.error('missing .node binary; run npm run build:ci');} else {console.log('\u2713 native binary present; skipping rebuild');}"`,
85
+ test: "node ./scripts/run-jest.mjs --runInBand",
86
+ "test:watch": "node ./scripts/run-jest.mjs --watch",
87
+ coverage: "node ./scripts/run-jest.mjs --coverage",
88
+ typecheck: "tsc --noEmit",
89
+ lint: "cargo fmt --check && cargo clippy --release --features napi-bindings -- -D warnings && npm run typecheck",
90
+ version: "napi version",
91
+ "release:patch": "npm version patch && npm run release",
92
+ "release:minor": "npm version minor && npm run release",
93
+ "release:major": "npm version major && npm run release",
94
+ "publish:platforms": "npm run create-npm-dirs && node scripts/publish-platform-packages.mjs",
95
+ release: "npm run build && npm run test && npm run verify:pack && npm run publish:platforms && node scripts/publish-sdk.mjs",
96
+ "release:dry": "npm run build && npm run test && npm run verify:pack && npm publish --dry-run"
97
+ },
98
+ napi: {
99
+ binaryName: "codex_native",
100
+ targets: [
101
+ "aarch64-apple-darwin",
102
+ "x86_64-apple-darwin",
103
+ "aarch64-unknown-linux-gnu",
104
+ "x86_64-unknown-linux-gnu",
105
+ "aarch64-unknown-linux-musl",
106
+ "x86_64-unknown-linux-musl",
107
+ "aarch64-pc-windows-msvc",
108
+ "x86_64-pc-windows-msvc"
109
+ ]
110
+ },
111
+ optionalDependencies: {
112
+ "@codex-native/sdk-darwin-arm64": "0.0.3",
113
+ "@codex-native/sdk-darwin-x64": "0.0.3",
114
+ "@codex-native/sdk-linux-arm64-gnu": "0.0.3",
115
+ "@codex-native/sdk-linux-x64-gnu": "0.0.3",
116
+ "@codex-native/sdk-linux-arm64-musl": "0.0.3",
117
+ "@codex-native/sdk-linux-x64-musl": "0.0.3",
118
+ "@codex-native/sdk-win32-arm64-msvc": "0.0.3",
119
+ "@codex-native/sdk-win32-x64-msvc": "0.0.3"
120
+ },
121
+ engines: {
122
+ node: ">=18"
123
+ },
124
+ publishConfig: {
125
+ access: "public"
126
+ },
127
+ devDependencies: {
128
+ "@babel/core": "7.25.2",
129
+ "@babel/preset-env": "7.25.4",
130
+ "@babel/preset-typescript": "7.28.5",
131
+ "@jest/globals": "^29.7.0",
132
+ "@napi-rs/cli": "^3.4.1",
133
+ "@openai/agents": "^0.3.0",
134
+ "@types/jest": "29.5.11",
135
+ "@types/node": "^22.0.0",
136
+ "babel-jest": "29.7.0",
137
+ jest: "^29.7.0",
138
+ "ts-jest": "^29.1.1",
139
+ tsup: "^8.5.0",
140
+ tsx: "^4.20.6",
141
+ typescript: "^5.7.2",
142
+ zod: "^3.25.76"
143
+ }
144
+ };
145
+
146
+ // src/cli/config.ts
147
+ var import_node_fs = __toESM(require("fs"));
148
+ var import_promises = __toESM(require("fs/promises"));
149
+ var import_node_path = __toESM(require("path"));
150
+ var import_node_module = require("module");
151
+ var import_node_url = require("url");
152
+ var requireFromThisModule = (0, import_node_module.createRequire)(importMetaUrl);
153
+ var CONFIG_CANDIDATES = [
154
+ "codex.config.js",
155
+ "codex.config.cjs",
156
+ "codex.config.mjs",
157
+ "codex.config.ts",
158
+ "codex.js",
159
+ ".codexrc.js",
160
+ ".codexrc.cjs",
161
+ ".codexrc.mjs",
162
+ ".codexrc.json"
163
+ ];
164
+ async function loadCliConfig(options) {
165
+ const warnings = [];
166
+ const discovery = await resolveConfigPath(options);
167
+ const configPath = discovery?.path ?? null;
168
+ let config = null;
169
+ if (discovery) {
170
+ const loadResult = await loadConfig(discovery, warnings);
171
+ config = loadResult ?? null;
172
+ if (config && typeof config !== "object") {
173
+ warnings.push(
174
+ `Config at ${discovery.path} must export an object. Received ${typeof config}.`
175
+ );
176
+ config = null;
177
+ }
178
+ }
179
+ const plugins = await resolvePlugins({
180
+ config,
181
+ configPath,
182
+ cliPluginPaths: options.pluginPaths ?? [],
183
+ cwd: options.cwd,
184
+ warnings
185
+ });
186
+ return {
187
+ configPath,
188
+ config,
189
+ plugins,
190
+ warnings
191
+ };
192
+ }
193
+ async function resolveConfigPath(options) {
194
+ if (options.explicitConfigPath) {
195
+ const explicitPath = import_node_path.default.resolve(options.cwd, options.explicitConfigPath);
196
+ if (!import_node_fs.default.existsSync(explicitPath)) {
197
+ throw new Error(`Config file not found at ${explicitPath}`);
198
+ }
199
+ return classifyPath(explicitPath);
200
+ }
201
+ if (options.noConfig) {
202
+ return null;
203
+ }
204
+ let currentDir = import_node_path.default.resolve(options.cwd);
205
+ const visited = /* @__PURE__ */ new Set();
206
+ while (!visited.has(currentDir)) {
207
+ visited.add(currentDir);
208
+ for (const candidate of CONFIG_CANDIDATES) {
209
+ const candidatePath = import_node_path.default.join(currentDir, candidate);
210
+ if (import_node_fs.default.existsSync(candidatePath) && import_node_fs.default.statSync(candidatePath).isFile()) {
211
+ return classifyPath(candidatePath);
212
+ }
213
+ }
214
+ const packageJsonPath = import_node_path.default.join(currentDir, "package.json");
215
+ if (import_node_fs.default.existsSync(packageJsonPath) && import_node_fs.default.statSync(packageJsonPath).isFile()) {
216
+ const manifest = await readJson(packageJsonPath);
217
+ if (manifest && manifest.codexNative != null) {
218
+ return { path: packageJsonPath, type: "package-json", field: "codexNative" };
219
+ }
220
+ }
221
+ const parentDir = import_node_path.default.dirname(currentDir);
222
+ if (parentDir === currentDir) {
223
+ break;
224
+ }
225
+ currentDir = parentDir;
226
+ }
227
+ return null;
228
+ }
229
+ async function loadConfig(discovery, warnings) {
230
+ if (discovery.type === "package-json") {
231
+ const manifest = await readJson(discovery.path);
232
+ if (!manifest) {
233
+ warnings.push(`Failed to parse ${discovery.path}; ignoring config.`);
234
+ return null;
235
+ }
236
+ const raw = manifest[discovery.field];
237
+ if (typeof raw === "string") {
238
+ const baseDir = import_node_path.default.dirname(discovery.path);
239
+ const nestedPath = import_node_path.default.resolve(baseDir, raw);
240
+ if (!import_node_fs.default.existsSync(nestedPath)) {
241
+ throw new Error(
242
+ `Config path "${raw}" referenced by ${discovery.field} in ${discovery.path} was not found.`
243
+ );
244
+ }
245
+ return loadConfig({ path: nestedPath, type: "file" }, warnings);
246
+ }
247
+ if (typeof raw === "object" && raw !== null) {
248
+ return raw;
249
+ }
250
+ warnings.push(
251
+ `The ${discovery.field} field in ${discovery.path} must be an object or path string.`
252
+ );
253
+ return null;
254
+ }
255
+ const ext = import_node_path.default.extname(discovery.path).toLowerCase();
256
+ if (ext === ".json") {
257
+ const json = await readJson(discovery.path);
258
+ if (json === null) {
259
+ warnings.push(`Failed to parse JSON config at ${discovery.path}`);
260
+ }
261
+ return json;
262
+ }
263
+ if (ext === ".js" || ext === ".cjs") {
264
+ return extractModuleDefault(await loadCommonJsModule(discovery.path));
265
+ }
266
+ if (ext === ".mjs") {
267
+ return extractModuleDefault(await importModule(discovery.path));
268
+ }
269
+ if (ext === ".ts") {
270
+ return extractModuleDefault(await loadTypeScriptModule(discovery.path, warnings));
271
+ }
272
+ throw new Error(`Unsupported config extension "${ext}" at ${discovery.path}`);
273
+ }
274
+ async function resolvePlugins(params) {
275
+ const plugins = [];
276
+ const { config, configPath, cliPluginPaths, cwd, warnings } = params;
277
+ const configDir = configPath ? import_node_path.default.dirname(configPath) : cwd;
278
+ const rawConfigPlugins = config?.plugins;
279
+ const configPlugins = Array.isArray(rawConfigPlugins) ? rawConfigPlugins : [];
280
+ for (const spec of configPlugins) {
281
+ if (typeof spec === "string") {
282
+ const loaded = await loadPlugin(spec, configDir, "config", warnings);
283
+ if (loaded) {
284
+ plugins.push(loaded);
285
+ }
286
+ } else if (spec != null) {
287
+ plugins.push({
288
+ source: "config",
289
+ spec: "<inline>",
290
+ plugin: spec
291
+ });
292
+ }
293
+ }
294
+ for (const spec of cliPluginPaths) {
295
+ const loaded = await loadPlugin(spec, cwd, "cli", warnings);
296
+ if (loaded) {
297
+ plugins.push(loaded);
298
+ }
299
+ }
300
+ return plugins;
301
+ }
302
+ async function loadPlugin(spec, baseDir, source, warnings) {
303
+ try {
304
+ const resolved = resolveModule(spec, baseDir);
305
+ const moduleExports = await loadModuleForPath(resolved);
306
+ return {
307
+ source,
308
+ spec,
309
+ resolvedPath: resolved,
310
+ plugin: extractModuleDefault(moduleExports)
311
+ };
312
+ } catch (err) {
313
+ warnings.push(`Failed to load plugin "${spec}": ${err.message}`);
314
+ return null;
315
+ }
316
+ }
317
+ async function loadModuleForPath(modulePath) {
318
+ const ext = import_node_path.default.extname(modulePath).toLowerCase();
319
+ if (ext === ".cjs") {
320
+ return loadCommonJsModule(modulePath);
321
+ }
322
+ if (ext === ".mjs") {
323
+ return importModule(modulePath);
324
+ }
325
+ if (ext === ".ts") {
326
+ return loadTypeScriptModule(modulePath);
327
+ }
328
+ if (ext === ".json") {
329
+ return readJson(modulePath);
330
+ }
331
+ if (ext === ".js") {
332
+ try {
333
+ return loadCommonJsModule(modulePath);
334
+ } catch (err) {
335
+ if (err instanceof Error && err.message.includes("ERR_REQUIRE_ESM")) {
336
+ return importModule(modulePath);
337
+ }
338
+ throw err;
339
+ }
340
+ }
341
+ return loadCommonJsModule(modulePath);
342
+ }
343
+ async function loadCommonJsModule(modulePath) {
344
+ return requireFromThisModule(modulePath);
345
+ }
346
+ async function importModule(modulePath) {
347
+ const href = (0, import_node_url.pathToFileURL)(modulePath).href;
348
+ return import(href);
349
+ }
350
+ async function loadTypeScriptModule(modulePath, warnings) {
351
+ try {
352
+ const { register } = requireFromThisModule("tsx/cjs/api");
353
+ const unregister = register({ transpileOnly: true });
354
+ try {
355
+ return requireFromThisModule(modulePath);
356
+ } finally {
357
+ await maybeCall(unregister);
358
+ }
359
+ } catch (cjsError) {
360
+ try {
361
+ const tsxEsmSpecifier = "tsx/esm/api";
362
+ const apiModule = await import(tsxEsmSpecifier);
363
+ const unregister = typeof apiModule.register === "function" ? apiModule.register({ transpileOnly: true }) : apiModule.default({ transpileOnly: true });
364
+ try {
365
+ return importModule(modulePath);
366
+ } finally {
367
+ await maybeCall(unregister);
368
+ }
369
+ } catch (esmError) {
370
+ const message = [
371
+ `Failed to load TypeScript module ${modulePath}.`,
372
+ 'Install the "tsx" package or convert the config to JavaScript.'
373
+ ].join(" ");
374
+ if (warnings) {
375
+ warnings.push(message);
376
+ return null;
377
+ }
378
+ throw new Error(message);
379
+ }
380
+ }
381
+ }
382
+ async function readJson(filePath) {
383
+ try {
384
+ const raw = await import_promises.default.readFile(filePath, "utf8");
385
+ return JSON.parse(raw);
386
+ } catch {
387
+ return null;
388
+ }
389
+ }
390
+ function extractModuleDefault(module2) {
391
+ if (module2 && typeof module2 === "object" && "default" in module2) {
392
+ const value = module2.default;
393
+ if (value !== void 0) {
394
+ return value;
395
+ }
396
+ }
397
+ return module2;
398
+ }
399
+ function resolveModule(specifier, baseDir) {
400
+ if (import_node_path.default.isAbsolute(specifier)) {
401
+ return specifier;
402
+ }
403
+ return requireFromThisModule.resolve(specifier, { paths: [baseDir] });
404
+ }
405
+ function classifyPath(filePath) {
406
+ if (import_node_path.default.basename(filePath) === "package.json") {
407
+ return { path: filePath, type: "package-json", field: "codexNative" };
408
+ }
409
+ return { path: filePath, type: "file" };
410
+ }
411
+ async function maybeCall(candidate) {
412
+ if (typeof candidate === "function") {
413
+ await Promise.resolve(candidate());
414
+ }
415
+ }
416
+
417
+ // src/cli/run.ts
418
+ var import_node_fs3 = __toESM(require("fs"));
419
+ var import_promises2 = __toESM(require("fs/promises"));
420
+ var import_node_path4 = __toESM(require("path"));
421
+ var import_node_process = __toESM(require("process"));
422
+
423
+ // src/nativeBinding.ts
424
+ var import_node_fs2 = __toESM(require("fs"));
425
+ var import_node_module2 = require("module");
426
+ var import_node_path2 = __toESM(require("path"));
427
+ var import_node_url2 = require("url");
428
+ var CLI_ENTRYPOINT_ENV = "CODEX_NODE_CLI_ENTRYPOINT";
429
+ function ensureCliEntrypointEnv() {
430
+ if (process.env[CLI_ENTRYPOINT_ENV]) {
431
+ return;
432
+ }
433
+ const filename = (0, import_node_url2.fileURLToPath)(importMetaUrl);
434
+ const dirname3 = import_node_path2.default.dirname(filename);
435
+ const candidates = [
436
+ import_node_path2.default.resolve(dirname3, "cli.cjs"),
437
+ import_node_path2.default.resolve(dirname3, "../cli.cjs"),
438
+ import_node_path2.default.resolve(dirname3, "../dist/cli.cjs")
439
+ ];
440
+ for (const candidate of candidates) {
441
+ if (import_node_fs2.default.existsSync(candidate)) {
442
+ process.env[CLI_ENTRYPOINT_ENV] = candidate;
443
+ break;
444
+ }
445
+ }
446
+ }
447
+ ensureCliEntrypointEnv();
448
+ var cachedBinding;
449
+ function getImportMetaUrl2() {
450
+ try {
451
+ return Function(
452
+ "return typeof import.meta !== 'undefined' && import.meta.url ? import.meta.url : undefined;"
453
+ )();
454
+ } catch {
455
+ return void 0;
456
+ }
457
+ }
458
+ function resolveBindingEntryPath() {
459
+ if (typeof __dirname === "string") {
460
+ return import_node_path2.default.resolve(__dirname, "..", "index.js");
461
+ }
462
+ const importMetaUrl2 = getImportMetaUrl2();
463
+ if (importMetaUrl2) {
464
+ try {
465
+ const filePath = (0, import_node_url2.fileURLToPath)(importMetaUrl2);
466
+ return import_node_path2.default.resolve(import_node_path2.default.dirname(filePath), "..", "index.js");
467
+ } catch {
468
+ }
469
+ }
470
+ return import_node_path2.default.resolve(process.cwd(), "index.js");
471
+ }
472
+ function resolveRequire() {
473
+ const globalRequire = globalThis.require;
474
+ if (typeof globalRequire === "function") {
475
+ return globalRequire;
476
+ }
477
+ if (typeof __filename === "string") {
478
+ try {
479
+ return (0, import_node_module2.createRequire)(__filename);
480
+ } catch {
481
+ }
482
+ }
483
+ const importMetaUrl2 = getImportMetaUrl2();
484
+ if (importMetaUrl2) {
485
+ try {
486
+ return (0, import_node_module2.createRequire)(importMetaUrl2);
487
+ } catch {
488
+ }
489
+ }
490
+ const fallbackBase = typeof __dirname === "string" ? __dirname : process.cwd();
491
+ const fallbackPath = import_node_path2.default.join(fallbackBase, "noop.js");
492
+ return (0, import_node_module2.createRequire)(fallbackPath);
493
+ }
494
+ function getNativeBinding() {
495
+ if (cachedBinding !== void 0) {
496
+ return cachedBinding;
497
+ }
498
+ const requireFn = resolveRequire();
499
+ const envPath = process.env.CODEX_NATIVE_BINDING;
500
+ if (envPath && envPath.length > 0) {
501
+ process.env.NAPI_RS_NATIVE_LIBRARY_PATH = envPath;
502
+ }
503
+ const bindingEntryPath = resolveBindingEntryPath();
504
+ try {
505
+ const binding = requireFn(bindingEntryPath);
506
+ binding.ensureTokioRuntime?.();
507
+ cachedBinding = binding;
508
+ return cachedBinding;
509
+ } catch (error) {
510
+ console.warn("Failed to load native NAPI binding:", error);
511
+ cachedBinding = null;
512
+ return cachedBinding;
513
+ }
514
+ }
515
+ function runApplyPatch(patch) {
516
+ if (!patch) {
517
+ throw new Error("apply_patch requires patch contents");
518
+ }
519
+ const binding = getNativeBinding();
520
+ if (!binding) throw new Error("Native binding not available");
521
+ binding.runApplyPatch(patch);
522
+ }
523
+ async function reverieIndexSemantic(codexHomePath, options) {
524
+ const binding = getNativeBinding();
525
+ if (!binding?.reverieIndexSemantic) throw new Error("Native binding not available or reverie functions not supported");
526
+ return binding.reverieIndexSemantic(codexHomePath, options);
527
+ }
528
+ async function fastEmbedInit(options) {
529
+ const binding = getNativeBinding();
530
+ if (!binding?.fastEmbedInit) throw new Error("Native binding not available or FastEmbed functions not supported");
531
+ await binding.fastEmbedInit(options);
532
+ }
533
+
534
+ // src/events/convert.ts
535
+ function convertRustEventToThreadEvent(rustEvent) {
536
+ if (rustEvent?.ThreadStarted) {
537
+ return {
538
+ type: "thread.started",
539
+ thread_id: rustEvent.ThreadStarted.thread_id
540
+ };
541
+ }
542
+ if (rustEvent?.TurnStarted) {
543
+ return { type: "turn.started" };
544
+ }
545
+ if (rustEvent?.TurnCompleted) {
546
+ return {
547
+ type: "turn.completed",
548
+ usage: rustEvent.TurnCompleted.usage
549
+ };
550
+ }
551
+ if (rustEvent?.TurnFailed) {
552
+ return {
553
+ type: "turn.failed",
554
+ error: rustEvent.TurnFailed.error
555
+ };
556
+ }
557
+ if (rustEvent?.ItemStarted) {
558
+ return {
559
+ type: "item.started",
560
+ item: rustEvent.ItemStarted.item
561
+ };
562
+ }
563
+ if (rustEvent?.ItemUpdated) {
564
+ return {
565
+ type: "item.updated",
566
+ item: rustEvent.ItemUpdated.item
567
+ };
568
+ }
569
+ if (rustEvent?.ItemCompleted) {
570
+ return {
571
+ type: "item.completed",
572
+ item: rustEvent.ItemCompleted.item
573
+ };
574
+ }
575
+ if (rustEvent?.Error) {
576
+ return {
577
+ type: "error",
578
+ message: rustEvent.Error.message
579
+ };
580
+ }
581
+ if (rustEvent?.BackgroundEvent) {
582
+ return {
583
+ type: "background_event",
584
+ message: rustEvent.BackgroundEvent.message
585
+ };
586
+ }
587
+ if (rustEvent?.type === "background_event" && typeof rustEvent.message === "string") {
588
+ return {
589
+ type: "background_event",
590
+ message: rustEvent.message
591
+ };
592
+ }
593
+ if (rustEvent?.type === "plan_update_scheduled" && rustEvent.plan) {
594
+ const planData = rustEvent.plan;
595
+ const planItems = planData.plan || [];
596
+ return {
597
+ type: "item.completed",
598
+ item: {
599
+ id: `plan-${Date.now()}`,
600
+ type: "todo_list",
601
+ items: planItems.map((item) => ({
602
+ text: item.step,
603
+ completed: item.status === "completed"
604
+ }))
605
+ }
606
+ };
607
+ }
608
+ if (rustEvent?.type) {
609
+ return rustEvent;
610
+ }
611
+ return rustEvent;
612
+ }
613
+
614
+ // src/cli/optionParsers.ts
615
+ var SANDBOX_MODE_VALUES = ["read-only", "workspace-write", "danger-full-access"];
616
+ var APPROVAL_MODE_VALUES = ["never", "on-request", "on-failure", "untrusted"];
617
+ function isSandboxMode(value) {
618
+ return SANDBOX_MODE_VALUES.includes(value);
619
+ }
620
+ function isApprovalMode(value) {
621
+ return APPROVAL_MODE_VALUES.includes(value);
622
+ }
623
+ function parseSandboxModeFlag(value, origin) {
624
+ if (value === void 0) {
625
+ return void 0;
626
+ }
627
+ if (isSandboxMode(value)) {
628
+ return value;
629
+ }
630
+ throw new Error(
631
+ `Invalid sandbox mode "${value}" from ${origin}. Valid values: ${SANDBOX_MODE_VALUES.join(
632
+ ", "
633
+ )}.`
634
+ );
635
+ }
636
+ function parseApprovalModeFlag(value, origin) {
637
+ if (value === void 0) {
638
+ return void 0;
639
+ }
640
+ if (isApprovalMode(value)) {
641
+ return value;
642
+ }
643
+ throw new Error(
644
+ `Invalid approval mode "${value}" from ${origin}. Valid values: ${APPROVAL_MODE_VALUES.join(
645
+ ", "
646
+ )}.`
647
+ );
648
+ }
649
+
650
+ // src/cli/hooks.ts
651
+ function emitWarnings(warnings, fromIndex = 0) {
652
+ for (let i = fromIndex; i < warnings.length; i += 1) {
653
+ const message = warnings[i];
654
+ process.stderr.write(`[codex-native] Warning: ${message}
655
+ `);
656
+ }
657
+ }
658
+ async function runBeforeStartHooks(hooks, context, warnings) {
659
+ for (const hook of hooks) {
660
+ try {
661
+ await hook.callback(context);
662
+ } catch (error) {
663
+ warnings.push(
664
+ `beforeStart hook "${hook.source}" threw: ${error.message ?? String(error)}`
665
+ );
666
+ }
667
+ }
668
+ }
669
+ async function runEventHooks(hooks, event, context, warnings) {
670
+ for (const hook of hooks) {
671
+ try {
672
+ await hook.callback(event, context);
673
+ } catch (error) {
674
+ warnings.push(`onEvent hook "${hook.source}" threw: ${error.message ?? String(error)}`);
675
+ }
676
+ }
677
+ }
678
+
679
+ // src/cli/elevatedDefaults.ts
680
+ var import_node_path3 = __toESM(require("path"));
681
+ var FULL_ACCESS_SANDBOX = "workspace-write";
682
+ var FULL_ACCESS_APPROVAL = "never";
683
+ function applyElevatedRunDefaults(request, cwd) {
684
+ const workingDirectory = resolveWorkingDirectory(request.workingDirectory, cwd);
685
+ request.workingDirectory = workingDirectory;
686
+ ensureSandboxModes(request);
687
+ request.workspaceWriteOptions = ensureWorkspaceWriteOptions(
688
+ request.workspaceWriteOptions,
689
+ workingDirectory
690
+ );
691
+ }
692
+ function applyElevatedTuiDefaults(params) {
693
+ const { request, thread, cwd } = params;
694
+ const workingDirectory = resolveWorkingDirectory(
695
+ request.workingDirectory ?? thread.workingDirectory,
696
+ cwd
697
+ );
698
+ request.workingDirectory = workingDirectory;
699
+ thread.workingDirectory = workingDirectory;
700
+ ensureSandboxModes(request);
701
+ thread.sandboxMode = request.sandboxMode ?? thread.sandboxMode ?? FULL_ACCESS_SANDBOX;
702
+ thread.approvalMode = request.approvalMode ?? thread.approvalMode ?? FULL_ACCESS_APPROVAL;
703
+ thread.workspaceWriteOptions = ensureWorkspaceWriteOptions(
704
+ thread.workspaceWriteOptions,
705
+ workingDirectory
706
+ );
707
+ }
708
+ function ensureSandboxModes(target) {
709
+ if (!target.sandboxMode) {
710
+ target.sandboxMode = FULL_ACCESS_SANDBOX;
711
+ }
712
+ if (!target.approvalMode) {
713
+ target.approvalMode = FULL_ACCESS_APPROVAL;
714
+ }
715
+ }
716
+ function ensureWorkspaceWriteOptions(options, workingDirectory) {
717
+ const resolved = import_node_path3.default.resolve(workingDirectory);
718
+ const writableRoots = new Set(options?.writableRoots ?? []);
719
+ writableRoots.add(resolved);
720
+ return {
721
+ ...options,
722
+ networkAccess: options?.networkAccess ?? true,
723
+ writableRoots: Array.from(writableRoots)
724
+ };
725
+ }
726
+ function resolveWorkingDirectory(candidate, cwd) {
727
+ if (!candidate || candidate.trim().length === 0) {
728
+ return import_node_path3.default.resolve(cwd);
729
+ }
730
+ return import_node_path3.default.isAbsolute(candidate) ? candidate : import_node_path3.default.resolve(cwd, candidate);
731
+ }
732
+
733
+ // src/lsp/bridge.ts
734
+ var path8 = __toESM(require("path"));
735
+
736
+ // src/lsp/manager.ts
737
+ var path6 = __toESM(require("path"));
738
+
739
+ // src/lsp/servers.ts
740
+ var fs3 = __toESM(require("fs"));
741
+ var path4 = __toESM(require("path"));
742
+ var MARKERS_NODE = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"];
743
+ var MARKERS_PY = ["pyproject.toml", "requirements.txt", "Pipfile", "setup.py", "setup.cfg", "poetry.lock"];
744
+ var MARKERS_RUST = ["Cargo.toml"];
745
+ var DEFAULT_SERVERS = [
746
+ {
747
+ id: "typescript",
748
+ displayName: "TypeScript Language Server",
749
+ command: ["typescript-language-server", "--stdio"],
750
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
751
+ workspace: { type: "markers", include: MARKERS_NODE }
752
+ },
753
+ {
754
+ id: "pyright",
755
+ displayName: "Pyright",
756
+ command: ["pyright-langserver", "--stdio"],
757
+ extensions: [".py", ".pyi"],
758
+ workspace: { type: "markers", include: MARKERS_PY }
759
+ },
760
+ {
761
+ id: "rust-analyzer",
762
+ displayName: "rust-analyzer",
763
+ command: ["rust-analyzer"],
764
+ extensions: [".rs"],
765
+ workspace: { type: "markers", include: MARKERS_RUST }
766
+ }
767
+ ];
768
+ function findServerForFile(filePath) {
769
+ const lower = filePath.toLowerCase();
770
+ return DEFAULT_SERVERS.find((server) => server.extensions.some((ext) => lower.endsWith(ext)));
771
+ }
772
+ function resolveWorkspaceRoot(filePath, locator, fallbackDir) {
773
+ if (!locator) {
774
+ return fallbackDir;
775
+ }
776
+ if (locator.type === "fixed") {
777
+ return locator.path;
778
+ }
779
+ const include = locator.include ?? [];
780
+ const exclude = locator.exclude ?? [];
781
+ let current = fs3.statSync(filePath, { throwIfNoEntry: false })?.isDirectory() ? filePath : path4.dirname(filePath);
782
+ const root = path4.parse(current).root;
783
+ while (true) {
784
+ if (exclude.some((pattern) => fs3.existsSync(path4.join(current, pattern)))) {
785
+ break;
786
+ }
787
+ if (include.some((pattern) => fs3.existsSync(path4.join(current, pattern)))) {
788
+ return current;
789
+ }
790
+ if (current === root) {
791
+ break;
792
+ }
793
+ const parent = path4.dirname(current);
794
+ if (parent === current) {
795
+ break;
796
+ }
797
+ current = parent;
798
+ }
799
+ return fallbackDir;
800
+ }
801
+
802
+ // src/lsp/client.ts
803
+ var import_node_child_process = require("child_process");
804
+ var fs4 = __toESM(require("fs/promises"));
805
+ var path5 = __toESM(require("path"));
806
+ var import_node_url3 = require("url");
807
+ var import_node_events = require("events");
808
+ var import_vscode_jsonrpc = require("vscode-jsonrpc");
809
+ var import_main = require("vscode-jsonrpc/lib/node/main.js");
810
+ var import_vscode_languageserver_types = require("vscode-languageserver-types");
811
+ var DEFAULT_TIMEOUT_MS = 3e3;
812
+ var LspClient = class _LspClient {
813
+ constructor(config, root) {
814
+ this.config = config;
815
+ this.root = root;
816
+ }
817
+ connection = null;
818
+ process = null;
819
+ diagnostics = /* @__PURE__ */ new Map();
820
+ versions = /* @__PURE__ */ new Map();
821
+ emitter = new import_node_events.EventEmitter();
822
+ static async start(server, root) {
823
+ const client = new _LspClient(server, root);
824
+ await client.initialize();
825
+ return client;
826
+ }
827
+ async initialize() {
828
+ const [command, ...args] = this.config.command;
829
+ if (!command) {
830
+ throw new Error(`LSP server ${this.config.id} is missing a command executable`);
831
+ }
832
+ try {
833
+ this.process = (0, import_node_child_process.spawn)(command, args, {
834
+ cwd: this.root,
835
+ env: { ...process.env, ...this.config.env },
836
+ stdio: "pipe"
837
+ });
838
+ } catch (error) {
839
+ throw new Error(`Failed to spawn ${this.config.displayName} (${command}): ${String(error)}`);
840
+ }
841
+ const child = this.process;
842
+ child.stderr.on("data", (_chunk) => {
843
+ });
844
+ const reader = new import_main.StreamMessageReader(child.stdout);
845
+ const writer = new import_main.StreamMessageWriter(child.stdin);
846
+ this.connection = (0, import_vscode_jsonrpc.createMessageConnection)(reader, writer);
847
+ this.connection.onNotification("textDocument/publishDiagnostics", (payload) => {
848
+ const fsPath = (0, import_node_url3.fileURLToPath)(payload.uri);
849
+ this.diagnostics.set(fsPath, payload.diagnostics);
850
+ this.emitter.emit(`diagnostics:${fsPath}`);
851
+ });
852
+ this.connection.onError((err) => {
853
+ console.warn(`[lsp:${this.config.id}] connection error`, err);
854
+ });
855
+ this.connection.listen();
856
+ await this.connection.sendRequest("initialize", {
857
+ rootUri: (0, import_node_url3.pathToFileURL)(this.root).href,
858
+ processId: process.pid,
859
+ initializationOptions: this.config.initializationOptions ?? {},
860
+ capabilities: {
861
+ textDocument: {
862
+ synchronization: {
863
+ didOpen: true,
864
+ didChange: true
865
+ },
866
+ publishDiagnostics: {
867
+ versionSupport: true
868
+ }
869
+ },
870
+ workspace: {
871
+ workspaceFolders: true
872
+ }
873
+ },
874
+ workspaceFolders: [
875
+ {
876
+ name: path5.basename(this.root),
877
+ uri: (0, import_node_url3.pathToFileURL)(this.root).href
878
+ }
879
+ ]
880
+ });
881
+ await this.connection.sendNotification("initialized", {});
882
+ }
883
+ async openFile(filePath, waitForDiagnostics) {
884
+ if (!this.connection) return;
885
+ const absolute = path5.resolve(filePath);
886
+ const text = await fs4.readFile(absolute, "utf8");
887
+ const uri = (0, import_node_url3.pathToFileURL)(absolute).href;
888
+ const languageId = detectLanguageId(absolute);
889
+ const existingVersion = this.versions.get(absolute);
890
+ if (existingVersion === void 0) {
891
+ this.versions.set(absolute, 0);
892
+ await this.connection.sendNotification("textDocument/didOpen", {
893
+ textDocument: {
894
+ uri,
895
+ languageId,
896
+ version: 0,
897
+ text
898
+ }
899
+ });
900
+ } else {
901
+ const next = existingVersion + 1;
902
+ this.versions.set(absolute, next);
903
+ await this.connection.sendNotification("textDocument/didChange", {
904
+ textDocument: {
905
+ uri,
906
+ version: next
907
+ },
908
+ contentChanges: [{ text }]
909
+ });
910
+ }
911
+ if (waitForDiagnostics) {
912
+ await this.waitForDiagnostics(absolute);
913
+ }
914
+ }
915
+ getDiagnostics(filePath) {
916
+ const absolute = path5.resolve(filePath);
917
+ return this.diagnostics.get(absolute) ?? [];
918
+ }
919
+ waitForDiagnostics(filePath, timeoutMs = DEFAULT_TIMEOUT_MS) {
920
+ const absolute = path5.resolve(filePath);
921
+ return new Promise((resolve5) => {
922
+ const timer = setTimeout(resolve5, timeoutMs).unref();
923
+ this.emitter.once(`diagnostics:${absolute}`, () => {
924
+ clearTimeout(timer);
925
+ resolve5();
926
+ });
927
+ });
928
+ }
929
+ async shutdown() {
930
+ try {
931
+ await this.connection?.dispose();
932
+ } catch {
933
+ }
934
+ if (this.process && !this.process.killed) {
935
+ this.process.kill();
936
+ }
937
+ }
938
+ };
939
+ function detectLanguageId(filePath) {
940
+ const ext = path5.extname(filePath).toLowerCase();
941
+ switch (ext) {
942
+ case ".ts":
943
+ case ".mts":
944
+ case ".cts":
945
+ return "typescript";
946
+ case ".tsx":
947
+ return "typescriptreact";
948
+ case ".js":
949
+ case ".mjs":
950
+ case ".cjs":
951
+ return "javascript";
952
+ case ".jsx":
953
+ return "javascriptreact";
954
+ case ".py":
955
+ case ".pyi":
956
+ return "python";
957
+ case ".rs":
958
+ return "rust";
959
+ default:
960
+ return "plaintext";
961
+ }
962
+ }
963
+ function normalizeSeverity(severity) {
964
+ switch (severity) {
965
+ case import_vscode_languageserver_types.DiagnosticSeverity.Error:
966
+ return "error";
967
+ case import_vscode_languageserver_types.DiagnosticSeverity.Warning:
968
+ return "warning";
969
+ case import_vscode_languageserver_types.DiagnosticSeverity.Information:
970
+ return "info";
971
+ case import_vscode_languageserver_types.DiagnosticSeverity.Hint:
972
+ return "hint";
973
+ default:
974
+ return "error";
975
+ }
976
+ }
977
+
978
+ // src/lsp/manager.ts
979
+ var LspManager = class {
980
+ constructor(options) {
981
+ this.options = options;
982
+ }
983
+ clients = /* @__PURE__ */ new Map();
984
+ async collectDiagnostics(files) {
985
+ const unique = Array.from(new Set(files.map((file) => path6.resolve(file))));
986
+ const results = [];
987
+ for (const filePath of unique) {
988
+ const server = findServerForFile(filePath);
989
+ if (!server) {
990
+ continue;
991
+ }
992
+ const root = resolveWorkspaceRoot(filePath, server.workspace, this.options.workingDirectory);
993
+ const client = await this.getClient(server, root);
994
+ if (!client) {
995
+ continue;
996
+ }
997
+ try {
998
+ await client.openFile(filePath, this.options.waitForDiagnostics !== false);
999
+ } catch (error) {
1000
+ console.warn(`[lsp] failed to open ${filePath}:`, error);
1001
+ continue;
1002
+ }
1003
+ const normalized = client.getDiagnostics(filePath).map((diag) => normalizeDiagnostic(diag)).filter((diag) => diag.message.trim().length > 0);
1004
+ if (normalized.length > 0) {
1005
+ results.push({ path: filePath, diagnostics: normalized });
1006
+ }
1007
+ }
1008
+ return results;
1009
+ }
1010
+ async dispose() {
1011
+ await Promise.all(
1012
+ Array.from(this.clients.values()).map(async (promise) => {
1013
+ const client = await promise;
1014
+ await client?.shutdown();
1015
+ })
1016
+ );
1017
+ this.clients.clear();
1018
+ }
1019
+ async getClient(server, root) {
1020
+ const key = `${server.id}:${root}`;
1021
+ let existing = this.clients.get(key);
1022
+ if (!existing) {
1023
+ existing = this.createClient(server, root);
1024
+ this.clients.set(key, existing);
1025
+ }
1026
+ const client = await existing;
1027
+ if (!client) {
1028
+ this.clients.delete(key);
1029
+ }
1030
+ return client;
1031
+ }
1032
+ async createClient(server, root) {
1033
+ try {
1034
+ return await LspClient.start(server, root);
1035
+ } catch (error) {
1036
+ console.warn(`[lsp] unable to start ${server.displayName}:`, error);
1037
+ return null;
1038
+ }
1039
+ }
1040
+ };
1041
+ function normalizeDiagnostic(diag) {
1042
+ return {
1043
+ message: diag.message ?? "",
1044
+ severity: normalizeSeverity(diag.severity),
1045
+ source: diag.source,
1046
+ code: diag.code,
1047
+ range: diag.range
1048
+ };
1049
+ }
1050
+
1051
+ // src/lsp/format.ts
1052
+ var path7 = __toESM(require("path"));
1053
+ var MAX_DIAGNOSTICS_PER_FILE = 5;
1054
+ function formatDiagnosticsForTool(diagnostics) {
1055
+ return diagnostics.map(({ path: filePath, diagnostics: entries }) => {
1056
+ const rel = filePath;
1057
+ const lines = entries.slice(0, MAX_DIAGNOSTICS_PER_FILE).map((diag) => {
1058
+ const { line, character } = diag.range.start;
1059
+ const location = `${line + 1}:${character + 1}`;
1060
+ const source = diag.source ? ` \xB7 ${diag.source}` : "";
1061
+ return ` - [${diag.severity.toUpperCase()}] ${diag.message} (${location}${source})`;
1062
+ });
1063
+ const trimmed = entries.length > MAX_DIAGNOSTICS_PER_FILE ? " - \u2026" : "";
1064
+ return [`\u2022 ${rel}`, ...lines, trimmed].filter(Boolean).join("\n");
1065
+ }).join("\n");
1066
+ }
1067
+ function formatDiagnosticsForBackgroundEvent(diagnostics, cwd) {
1068
+ return diagnostics.map(({ path: filePath, diagnostics: entries }) => {
1069
+ const rel = path7.relative(cwd, filePath) || filePath;
1070
+ const lines = entries.slice(0, MAX_DIAGNOSTICS_PER_FILE).map((diag) => {
1071
+ const { line, character } = diag.range.start;
1072
+ const location = `${line + 1}:${character + 1}`;
1073
+ const source = diag.source ? ` \xB7 ${diag.source}` : "";
1074
+ return ` - [${diag.severity.toUpperCase()}] ${diag.message} (${location}${source})`;
1075
+ });
1076
+ const trimmed = entries.length > MAX_DIAGNOSTICS_PER_FILE ? " - \u2026" : "";
1077
+ return [`\u2022 ${rel}`, ...lines, trimmed].filter(Boolean).join("\n");
1078
+ }).join("\n");
1079
+ }
1080
+
1081
+ // src/lsp/bridge.ts
1082
+ var LspDiagnosticsBridge = class {
1083
+ constructor(options) {
1084
+ this.options = options;
1085
+ this.manager = new LspManager(options);
1086
+ }
1087
+ manager;
1088
+ attached = /* @__PURE__ */ new WeakSet();
1089
+ attach(thread) {
1090
+ if (this.attached.has(thread)) {
1091
+ return () => {
1092
+ };
1093
+ }
1094
+ this.attached.add(thread);
1095
+ const unsubscribe = thread.onEvent((event) => {
1096
+ if (event.type !== "item.completed") {
1097
+ return;
1098
+ }
1099
+ if (event.item.type === "file_change") {
1100
+ const targets = event.item.changes.filter((change) => change.kind !== "delete").map((change) => path8.resolve(this.options.workingDirectory, change.path));
1101
+ if (targets.length === 0) {
1102
+ return;
1103
+ }
1104
+ void this.processDiagnostics(thread, targets);
1105
+ return;
1106
+ }
1107
+ if (event.item.type === "mcp_tool_call") {
1108
+ const targets = extractReadFileTargets(event.item, this.options.workingDirectory);
1109
+ if (targets.length === 0) {
1110
+ return;
1111
+ }
1112
+ void this.processDiagnostics(thread, targets);
1113
+ }
1114
+ });
1115
+ return () => {
1116
+ this.attached.delete(thread);
1117
+ unsubscribe();
1118
+ };
1119
+ }
1120
+ async dispose() {
1121
+ await this.manager.dispose();
1122
+ }
1123
+ async processDiagnostics(thread, files) {
1124
+ try {
1125
+ const diagnostics = await this.manager.collectDiagnostics(files);
1126
+ if (diagnostics.length === 0) {
1127
+ return;
1128
+ }
1129
+ const summary = formatDiagnosticsForBackgroundEvent(
1130
+ diagnostics,
1131
+ this.options.workingDirectory
1132
+ );
1133
+ console.log(`
1134
+ \u{1F4DF} LSP diagnostics detected:
1135
+ ${summary}
1136
+ `);
1137
+ try {
1138
+ await thread.sendBackgroundEvent(`LSP diagnostics detected:
1139
+ ${summary}`);
1140
+ } catch {
1141
+ }
1142
+ } catch (error) {
1143
+ console.warn("[lsp] failed to collect diagnostics", error);
1144
+ }
1145
+ }
1146
+ };
1147
+ function extractReadFileTargets(item, cwd) {
1148
+ if (item.type !== "mcp_tool_call") {
1149
+ return [];
1150
+ }
1151
+ const toolName = item.tool?.toLowerCase?.();
1152
+ if (toolName !== "read_file" && toolName !== "read_file_v2") {
1153
+ return [];
1154
+ }
1155
+ let args = item.arguments;
1156
+ if (typeof args === "string") {
1157
+ try {
1158
+ args = JSON.parse(args);
1159
+ } catch {
1160
+ return [];
1161
+ }
1162
+ }
1163
+ if (!args || typeof args !== "object") {
1164
+ return [];
1165
+ }
1166
+ const filePath = args.file_path ?? args.path;
1167
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
1168
+ return [];
1169
+ }
1170
+ const resolved = path8.isAbsolute(filePath) ? filePath : path8.resolve(cwd, filePath);
1171
+ return [resolved];
1172
+ }
1173
+
1174
+ // src/lsp/hooks.ts
1175
+ function attachLspDiagnostics(thread, options) {
1176
+ const bridge = new LspDiagnosticsBridge(options);
1177
+ const detach = bridge.attach(thread);
1178
+ return () => {
1179
+ detach();
1180
+ void bridge.dispose().catch((error) => {
1181
+ console.warn("Failed to dispose LSP bridge", error);
1182
+ });
1183
+ };
1184
+ }
1185
+
1186
+ // src/cli/lspBridge.ts
1187
+ var RunCommandThreadRelay = class {
1188
+ constructor(binding, initialThreadId) {
1189
+ this.binding = binding;
1190
+ this.threadId = initialThreadId ?? null;
1191
+ }
1192
+ listeners = /* @__PURE__ */ new Set();
1193
+ threadId;
1194
+ onEvent(listener) {
1195
+ this.listeners.add(listener);
1196
+ return () => {
1197
+ this.listeners.delete(listener);
1198
+ };
1199
+ }
1200
+ async sendBackgroundEvent(message) {
1201
+ const trimmed = typeof message === "string" ? message.trim() : "";
1202
+ if (!trimmed) {
1203
+ throw new Error("Background event message must be a non-empty string");
1204
+ }
1205
+ if (!this.threadId) {
1206
+ throw new Error("Cannot emit a background event before the thread has started");
1207
+ }
1208
+ if (typeof this.binding.emitBackgroundEvent !== "function") {
1209
+ throw new Error("emitBackgroundEvent is not available in this build");
1210
+ }
1211
+ await this.binding.emitBackgroundEvent({ threadId: this.threadId, message: trimmed });
1212
+ }
1213
+ handleEvent(event) {
1214
+ if (event.type === "thread.started" && typeof event.thread_id === "string") {
1215
+ this.threadId = event.thread_id;
1216
+ }
1217
+ for (const listener of this.listeners) {
1218
+ try {
1219
+ listener(event);
1220
+ } catch (error) {
1221
+ console.warn("[codex-native] LSP listener failed", error);
1222
+ }
1223
+ }
1224
+ }
1225
+ setThreadId(id) {
1226
+ if (id) {
1227
+ this.threadId = id;
1228
+ }
1229
+ }
1230
+ };
1231
+ function createRunCommandLspBridge(params) {
1232
+ try {
1233
+ const relay = new RunCommandThreadRelay(params.binding, params.initialThreadId);
1234
+ const detach = attachLspDiagnostics(relay, {
1235
+ workingDirectory: params.workingDirectory,
1236
+ waitForDiagnostics: true
1237
+ });
1238
+ relay.setThreadId(params.initialThreadId);
1239
+ return {
1240
+ handleEvent: (event) => relay.handleEvent(event),
1241
+ dispose: () => detach()
1242
+ };
1243
+ } catch (error) {
1244
+ const message = error instanceof Error ? error.message : String(error);
1245
+ console.warn(`[codex-native] Failed to initialize LSP diagnostics bridge: ${message}`);
1246
+ return null;
1247
+ }
1248
+ }
1249
+
1250
+ // src/cli/run.ts
1251
+ async function executeRunCommand(argv, context) {
1252
+ const { combinedConfig } = context;
1253
+ emitWarnings(combinedConfig.warnings);
1254
+ const warningCount = combinedConfig.warnings.length;
1255
+ const prompt = await resolvePrompt(argv, combinedConfig.runDefaults.prompt, context.cwd);
1256
+ const request = await buildRunRequest({
1257
+ prompt,
1258
+ argv,
1259
+ combinedDefaults: combinedConfig.runDefaults,
1260
+ cwd: context.cwd
1261
+ });
1262
+ if (!request.skipGitRepoCheck) {
1263
+ await assertTrustedDirectory(request.workingDirectory);
1264
+ }
1265
+ validateModel(request.model, request.oss === true);
1266
+ const hookContext = {
1267
+ command: "run",
1268
+ cwd: context.cwd,
1269
+ options: argv
1270
+ };
1271
+ await runBeforeStartHooks(combinedConfig.beforeStartHooks, hookContext, combinedConfig.warnings);
1272
+ const binding = getNativeBinding();
1273
+ if (!binding) {
1274
+ throw new Error("Native N-API binding is not available.");
1275
+ }
1276
+ const queue = new AsyncQueue();
1277
+ let conversationId = null;
1278
+ const lspBridge = createRunCommandLspBridge({
1279
+ binding,
1280
+ workingDirectory: request.workingDirectory ?? context.cwd,
1281
+ initialThreadId: request.threadId
1282
+ });
1283
+ const handleEvent = async (eventJson) => {
1284
+ if (!eventJson) {
1285
+ return;
1286
+ }
1287
+ import_node_process.default.stdout.write(eventJson);
1288
+ import_node_process.default.stdout.write("\n");
1289
+ let eventPayload = eventJson;
1290
+ try {
1291
+ eventPayload = JSON.parse(eventJson);
1292
+ } catch {
1293
+ }
1294
+ conversationId ??= extractConversationId(eventPayload);
1295
+ const threadEvent = toThreadEvent(eventPayload);
1296
+ if (threadEvent && lspBridge) {
1297
+ lspBridge.handleEvent(threadEvent);
1298
+ }
1299
+ await runEventHooks(
1300
+ combinedConfig.onEventHooks,
1301
+ eventPayload,
1302
+ hookContext,
1303
+ combinedConfig.warnings
1304
+ );
1305
+ };
1306
+ let runPromise = Promise.resolve();
1307
+ runPromise = binding.runThreadStream(request, (err, eventJson) => {
1308
+ if (err) {
1309
+ queue.fail(err);
1310
+ return;
1311
+ }
1312
+ queue.push(eventJson ?? null);
1313
+ }).then(
1314
+ () => queue.end(),
1315
+ (error) => {
1316
+ queue.fail(error);
1317
+ }
1318
+ );
1319
+ let loopError;
1320
+ try {
1321
+ for await (const eventJson of queue) {
1322
+ try {
1323
+ await handleEvent(eventJson);
1324
+ } catch (error) {
1325
+ combinedConfig.warnings.push(
1326
+ `Event handler failed: ${error.message ?? String(error)}`
1327
+ );
1328
+ }
1329
+ }
1330
+ await runPromise;
1331
+ } catch (error) {
1332
+ loopError = error;
1333
+ throw error;
1334
+ } finally {
1335
+ queue.end();
1336
+ if (loopError) {
1337
+ await runPromise.catch(() => {
1338
+ });
1339
+ }
1340
+ if (lspBridge) {
1341
+ lspBridge.dispose();
1342
+ }
1343
+ }
1344
+ if (conversationId) {
1345
+ import_node_process.default.stdout.write(`
1346
+ To resume, run: codex-native tui --resume ${conversationId}
1347
+ `);
1348
+ }
1349
+ emitWarnings(combinedConfig.warnings, warningCount);
1350
+ }
1351
+ function toThreadEvent(payload) {
1352
+ if (!payload || typeof payload !== "object") {
1353
+ return null;
1354
+ }
1355
+ try {
1356
+ return convertRustEventToThreadEvent(payload);
1357
+ } catch {
1358
+ return null;
1359
+ }
1360
+ }
1361
+ async function resolvePrompt(argv, defaultPrompt, cwd) {
1362
+ if (argv.prompt && argv.prompt.trim().length > 0) {
1363
+ return argv.prompt;
1364
+ }
1365
+ if (defaultPrompt && defaultPrompt.trim().length > 0) {
1366
+ return defaultPrompt;
1367
+ }
1368
+ const stdinPrompt = await readPromptFromStdin();
1369
+ if (stdinPrompt && stdinPrompt.trim().length > 0) {
1370
+ return stdinPrompt;
1371
+ }
1372
+ if (argv.threadId) {
1373
+ return "";
1374
+ }
1375
+ const baseMessage = "No prompt provided. Supply a prompt or pipe one via stdin.";
1376
+ if (import_node_process.default.stdin.isTTY) {
1377
+ throw new Error(baseMessage);
1378
+ }
1379
+ throw new Error(baseMessage);
1380
+ }
1381
+ async function buildRunRequest(params) {
1382
+ const { prompt, argv, combinedDefaults, cwd } = params;
1383
+ const request = {
1384
+ ...combinedDefaults,
1385
+ prompt
1386
+ };
1387
+ if (combinedDefaults.images) {
1388
+ request.images = [...combinedDefaults.images];
1389
+ }
1390
+ if (combinedDefaults.workspaceWriteOptions) {
1391
+ request.workspaceWriteOptions = { ...combinedDefaults.workspaceWriteOptions };
1392
+ }
1393
+ if (argv.model !== void 0) request.model = argv.model;
1394
+ if (argv.oss !== void 0) request.oss = argv.oss;
1395
+ const sandboxMode = parseSandboxModeFlag(argv.sandbox, "--sandbox");
1396
+ if (sandboxMode !== void 0) {
1397
+ request.sandboxMode = sandboxMode;
1398
+ }
1399
+ const approvalMode = parseApprovalModeFlag(argv.approval, "--approval");
1400
+ if (approvalMode !== void 0) {
1401
+ request.approvalMode = approvalMode;
1402
+ }
1403
+ if (argv.threadId !== void 0) request.threadId = argv.threadId;
1404
+ if (argv.baseUrl !== void 0) request.baseUrl = argv.baseUrl;
1405
+ if (argv.apiKey !== void 0) request.apiKey = argv.apiKey;
1406
+ if (argv.linuxSandboxPath !== void 0) request.linuxSandboxPath = argv.linuxSandboxPath;
1407
+ if (argv.fullAuto !== void 0) request.fullAuto = argv.fullAuto;
1408
+ if (argv.skipGitRepoCheck !== void 0) request.skipGitRepoCheck = argv.skipGitRepoCheck;
1409
+ if (argv.cd !== void 0) request.workingDirectory = argv.cd;
1410
+ if (argv.reviewMode !== void 0) request.reviewMode = argv.reviewMode;
1411
+ if (argv.reviewHint !== void 0) request.reviewHint = argv.reviewHint;
1412
+ const images = [
1413
+ ...Array.isArray(request.images) ? request.images : [],
1414
+ ...argv.image ?? []
1415
+ ];
1416
+ request.images = images.length > 0 ? images : void 0;
1417
+ if (argv.schema) {
1418
+ request.outputSchema = await readJsonFile(argv.schema);
1419
+ }
1420
+ applyElevatedRunDefaults(request, cwd);
1421
+ return request;
1422
+ }
1423
+ async function readJsonFile(filePath) {
1424
+ const absolute = import_node_path4.default.resolve(import_node_process.default.cwd(), filePath);
1425
+ const data = await import_promises2.default.readFile(absolute, "utf8");
1426
+ try {
1427
+ return JSON.parse(data);
1428
+ } catch (error) {
1429
+ throw new Error(
1430
+ `Failed to parse JSON schema from ${absolute}: ${error.message ?? error}`
1431
+ );
1432
+ }
1433
+ }
1434
+ async function readPromptFromStdin() {
1435
+ if (import_node_process.default.stdin.isTTY) {
1436
+ return null;
1437
+ }
1438
+ const chunks = [];
1439
+ for await (const chunk of import_node_process.default.stdin) {
1440
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
1441
+ }
1442
+ if (chunks.length === 0) {
1443
+ return null;
1444
+ }
1445
+ return Buffer.concat(chunks).toString("utf8").trimEnd();
1446
+ }
1447
+ function extractConversationId(eventPayload) {
1448
+ if (!eventPayload || typeof eventPayload !== "object") {
1449
+ return null;
1450
+ }
1451
+ const record = eventPayload;
1452
+ if (typeof record.session_id === "string") {
1453
+ return record.session_id;
1454
+ }
1455
+ const sessionConfigured = record.SessionConfigured ?? record.sessionConfigured;
1456
+ if (sessionConfigured && typeof sessionConfigured === "object") {
1457
+ const configuredSessionId = sessionConfigured.session_id;
1458
+ if (typeof configuredSessionId === "string") {
1459
+ return configuredSessionId;
1460
+ }
1461
+ }
1462
+ const nestedSession = typeof record.session === "object" && record.session ? record.session.id : void 0;
1463
+ if (typeof nestedSession === "string") {
1464
+ return nestedSession;
1465
+ }
1466
+ return null;
1467
+ }
1468
+ function validateModel(model, oss) {
1469
+ if (!model) return;
1470
+ const trimmed = String(model).trim();
1471
+ if (oss) {
1472
+ if (!trimmed.startsWith("gpt-oss:")) {
1473
+ throw new Error(
1474
+ `Invalid model "${trimmed}" for OSS mode. Use models prefixed with "gpt-oss:", e.g. "gpt-oss:20b".`
1475
+ );
1476
+ }
1477
+ return;
1478
+ }
1479
+ const allowed = /* @__PURE__ */ new Set([
1480
+ // GPT models
1481
+ "gpt-5",
1482
+ "gpt-5-codex",
1483
+ "gpt-5-codex-mini",
1484
+ "gpt-5.1",
1485
+ "gpt-5.1-codex",
1486
+ "gpt-5.1-codex-mini",
1487
+ // Claude models
1488
+ "claude-sonnet-4-5-20250929",
1489
+ "claude-sonnet-4-20250514",
1490
+ "claude-opus-4-20250514"
1491
+ ]);
1492
+ if (!allowed.has(trimmed) && !trimmed.startsWith("claude-") && !trimmed.startsWith("gpt-")) {
1493
+ throw new Error(
1494
+ `Invalid model "${trimmed}". Supported models: ${Array.from(allowed).map((m) => `"${m}"`).join(", ")}, or any model starting with "claude-" or "gpt-".`
1495
+ );
1496
+ }
1497
+ }
1498
+ async function assertTrustedDirectory(workingDirectory) {
1499
+ const directory = workingDirectory ? import_node_path4.default.resolve(workingDirectory) : import_node_process.default.cwd();
1500
+ if (await findGitRoot(directory)) {
1501
+ return;
1502
+ }
1503
+ throw new Error(
1504
+ "Not inside a trusted directory and --skip-git-repo-check was not specified."
1505
+ );
1506
+ }
1507
+ async function findGitRoot(startDir) {
1508
+ let current = import_node_path4.default.resolve(startDir);
1509
+ while (true) {
1510
+ const gitPath = import_node_path4.default.join(current, ".git");
1511
+ if (import_node_fs3.default.existsSync(gitPath)) {
1512
+ try {
1513
+ const stats = await import_promises2.default.stat(gitPath);
1514
+ if (stats.isDirectory() || stats.isFile()) {
1515
+ return current;
1516
+ }
1517
+ } catch {
1518
+ }
1519
+ }
1520
+ const parent = import_node_path4.default.dirname(current);
1521
+ if (parent === current) {
1522
+ break;
1523
+ }
1524
+ current = parent;
1525
+ }
1526
+ return null;
1527
+ }
1528
+ var AsyncQueue = class {
1529
+ buffer = [];
1530
+ waiters = [];
1531
+ ended = false;
1532
+ error;
1533
+ push(value) {
1534
+ if (this.ended) return;
1535
+ if (value === null) {
1536
+ return;
1537
+ }
1538
+ if (this.waiters.length > 0) {
1539
+ const waiter = this.waiters.shift();
1540
+ waiter.resolve({ value, done: false });
1541
+ return;
1542
+ }
1543
+ this.buffer.push(value);
1544
+ }
1545
+ end() {
1546
+ if (this.ended) return;
1547
+ this.ended = true;
1548
+ const waiters = this.waiters;
1549
+ this.waiters = [];
1550
+ for (const waiter of waiters) {
1551
+ waiter.resolve({ value: void 0, done: true });
1552
+ }
1553
+ }
1554
+ fail(error) {
1555
+ if (this.ended) return;
1556
+ this.error = error;
1557
+ this.ended = true;
1558
+ const waiters = this.waiters;
1559
+ this.waiters = [];
1560
+ for (const waiter of waiters) {
1561
+ waiter.reject(error);
1562
+ }
1563
+ }
1564
+ async next() {
1565
+ if (this.buffer.length > 0) {
1566
+ const value = this.buffer.shift();
1567
+ return { value, done: false };
1568
+ }
1569
+ if (this.error) {
1570
+ return Promise.reject(this.error);
1571
+ }
1572
+ if (this.ended) {
1573
+ return { value: void 0, done: true };
1574
+ }
1575
+ return new Promise((resolve5, reject) => {
1576
+ this.waiters.push({ resolve: resolve5, reject });
1577
+ });
1578
+ }
1579
+ [Symbol.asyncIterator]() {
1580
+ return this;
1581
+ }
1582
+ };
1583
+
1584
+ // src/cli/tui.ts
1585
+ var import_node_process2 = __toESM(require("process"));
1586
+
1587
+ // src/exec.ts
1588
+ var CodexExec = class {
1589
+ native;
1590
+ constructor() {
1591
+ const nativeBinding = getNativeBinding();
1592
+ if (!nativeBinding) {
1593
+ throw new Error(
1594
+ "Native NAPI binding not available. Make sure @openai/codex-native is properly installed and built."
1595
+ );
1596
+ }
1597
+ this.native = nativeBinding;
1598
+ }
1599
+ requiresOutputSchemaFile() {
1600
+ return false;
1601
+ }
1602
+ async *run(args) {
1603
+ const binding = this.native;
1604
+ const queue = new AsyncQueue2();
1605
+ validateModel2(args.model, args.oss === true);
1606
+ const request = {
1607
+ prompt: args.input,
1608
+ threadId: args.threadId ?? void 0,
1609
+ images: args.images && args.images.length > 0 ? args.images : void 0,
1610
+ model: args.model,
1611
+ oss: args.oss,
1612
+ approvalMode: args.approvalMode,
1613
+ workspaceWriteOptions: args.workspaceWriteOptions,
1614
+ sandboxMode: args.sandboxMode,
1615
+ workingDirectory: args.workingDirectory,
1616
+ skipGitRepoCheck: args.skipGitRepoCheck,
1617
+ outputSchema: args.outputSchema,
1618
+ baseUrl: args.baseUrl,
1619
+ apiKey: args.apiKey,
1620
+ modelProvider: args.modelProvider,
1621
+ fullAuto: args.fullAuto,
1622
+ reviewMode: args.review ? true : void 0,
1623
+ reviewHint: args.review?.userFacingHint
1624
+ };
1625
+ let runPromise = Promise.resolve();
1626
+ try {
1627
+ runPromise = binding.runThreadStream(request, (err, eventJson) => {
1628
+ if (err) {
1629
+ queue.fail(err);
1630
+ return;
1631
+ }
1632
+ try {
1633
+ queue.push(eventJson ?? "null");
1634
+ } catch (error) {
1635
+ queue.fail(error);
1636
+ }
1637
+ }).then(
1638
+ () => {
1639
+ queue.end();
1640
+ },
1641
+ (error) => {
1642
+ queue.fail(error);
1643
+ }
1644
+ );
1645
+ } catch (error) {
1646
+ queue.fail(error);
1647
+ throw error;
1648
+ }
1649
+ let loopError;
1650
+ try {
1651
+ for await (const value of queue) {
1652
+ yield value;
1653
+ }
1654
+ await runPromise;
1655
+ } catch (error) {
1656
+ loopError = error;
1657
+ throw error;
1658
+ } finally {
1659
+ queue.end();
1660
+ if (loopError) {
1661
+ await runPromise.catch(() => {
1662
+ });
1663
+ }
1664
+ }
1665
+ }
1666
+ async compact(args) {
1667
+ validateModel2(args.model, args.oss === true);
1668
+ const request = {
1669
+ prompt: args.input,
1670
+ threadId: args.threadId ?? void 0,
1671
+ images: args.images && args.images.length > 0 ? args.images : void 0,
1672
+ model: args.model,
1673
+ modelProvider: args.modelProvider,
1674
+ oss: args.oss,
1675
+ sandboxMode: args.sandboxMode,
1676
+ approvalMode: args.approvalMode,
1677
+ workspaceWriteOptions: args.workspaceWriteOptions,
1678
+ workingDirectory: args.workingDirectory,
1679
+ skipGitRepoCheck: args.skipGitRepoCheck,
1680
+ outputSchema: args.outputSchema,
1681
+ baseUrl: args.baseUrl,
1682
+ apiKey: args.apiKey,
1683
+ fullAuto: args.fullAuto,
1684
+ reviewMode: args.review ? true : void 0,
1685
+ reviewHint: args.review?.userFacingHint
1686
+ };
1687
+ return this.native.compactThread(request);
1688
+ }
1689
+ async fork(args) {
1690
+ if (!args.threadId) {
1691
+ throw new Error("threadId is required to fork a conversation");
1692
+ }
1693
+ const request = {
1694
+ threadId: args.threadId,
1695
+ nthUserMessage: args.nthUserMessage,
1696
+ model: args.model,
1697
+ oss: args.oss,
1698
+ sandboxMode: args.sandboxMode,
1699
+ approvalMode: args.approvalMode,
1700
+ workspaceWriteOptions: args.workspaceWriteOptions,
1701
+ workingDirectory: args.workingDirectory,
1702
+ skipGitRepoCheck: args.skipGitRepoCheck,
1703
+ baseUrl: args.baseUrl,
1704
+ apiKey: args.apiKey,
1705
+ modelProvider: args.modelProvider,
1706
+ linuxSandboxPath: args.linuxSandboxPath,
1707
+ fullAuto: args.fullAuto
1708
+ };
1709
+ return this.native.forkThread(request);
1710
+ }
1711
+ async listConversations(request) {
1712
+ return this.native.listConversations(request);
1713
+ }
1714
+ async deleteConversation(request) {
1715
+ return this.native.deleteConversation(request);
1716
+ }
1717
+ async resumeConversationFromRollout(request) {
1718
+ return this.native.resumeConversationFromRollout(request);
1719
+ }
1720
+ };
1721
+ var AsyncQueue2 = class {
1722
+ buffer = [];
1723
+ waiters = [];
1724
+ ended = false;
1725
+ error;
1726
+ push(value) {
1727
+ if (this.ended) return;
1728
+ if (this.waiters.length > 0) {
1729
+ const waiter = this.waiters.shift();
1730
+ waiter.resolve({ value, done: false });
1731
+ return;
1732
+ }
1733
+ this.buffer.push(value);
1734
+ }
1735
+ end() {
1736
+ if (this.ended) return;
1737
+ this.ended = true;
1738
+ const waiters = this.waiters;
1739
+ this.waiters = [];
1740
+ for (const waiter of waiters) {
1741
+ waiter.resolve({ value: void 0, done: true });
1742
+ }
1743
+ }
1744
+ fail(error) {
1745
+ if (this.ended) return;
1746
+ this.error = error;
1747
+ this.ended = true;
1748
+ const waiters = this.waiters;
1749
+ this.waiters = [];
1750
+ for (const waiter of waiters) {
1751
+ waiter.reject(error);
1752
+ }
1753
+ }
1754
+ async next() {
1755
+ if (this.buffer.length > 0) {
1756
+ const value = this.buffer.shift();
1757
+ return { value, done: false };
1758
+ }
1759
+ if (this.error) {
1760
+ return Promise.reject(this.error);
1761
+ }
1762
+ if (this.ended) {
1763
+ return { value: void 0, done: true };
1764
+ }
1765
+ return new Promise((resolve5, reject) => {
1766
+ this.waiters.push({ resolve: resolve5, reject });
1767
+ });
1768
+ }
1769
+ [Symbol.asyncIterator]() {
1770
+ return this;
1771
+ }
1772
+ };
1773
+ function validateModel2(model, oss) {
1774
+ if (!model) return;
1775
+ const trimmed = String(model).trim();
1776
+ if (oss) {
1777
+ if (!trimmed.startsWith("gpt-oss:")) {
1778
+ throw new Error(
1779
+ `Invalid model "${trimmed}" for OSS mode. Use models prefixed with "gpt-oss:", e.g. "gpt-oss:20b".`
1780
+ );
1781
+ }
1782
+ return;
1783
+ }
1784
+ const allowed = /* @__PURE__ */ new Set([
1785
+ // GPT models
1786
+ "gpt-5",
1787
+ "gpt-5-codex",
1788
+ "gpt-5-codex-mini",
1789
+ "gpt-5.1",
1790
+ "gpt-5.1-codex",
1791
+ "gpt-5.1-codex-mini",
1792
+ // Claude models
1793
+ "claude-sonnet-4-5-20250929",
1794
+ "claude-sonnet-4-20250514",
1795
+ "claude-opus-4-20250514"
1796
+ ]);
1797
+ if (!allowed.has(trimmed) && !trimmed.startsWith("claude-") && !trimmed.startsWith("gpt-")) {
1798
+ throw new Error(
1799
+ `Invalid model "${trimmed}". Supported models: ${Array.from(allowed).map((m) => `"${m}"`).join(", ")}, or any model starting with "claude-" or "gpt-".`
1800
+ );
1801
+ }
1802
+ }
1803
+
1804
+ // src/thread.ts
1805
+ var fs7 = __toESM(require("fs"));
1806
+ var path11 = __toESM(require("path"));
1807
+
1808
+ // src/outputSchemaFile.ts
1809
+ var import_node_fs4 = require("fs");
1810
+ var import_node_os = __toESM(require("os"));
1811
+ var import_node_path5 = __toESM(require("path"));
1812
+ function normalizeOutputSchema(schema) {
1813
+ if (schema === void 0) {
1814
+ return void 0;
1815
+ }
1816
+ if (isJsonObject(schema) && (schema.type === "json_schema" || schema.type === "json-schema") && isJsonObject(schema.json_schema) && isJsonObject(schema.json_schema.schema)) {
1817
+ const strict = typeof schema.json_schema.strict === "boolean" ? schema.json_schema.strict : true;
1818
+ return normalizeJsonSchemaObject(schema.json_schema.schema, strict);
1819
+ }
1820
+ if (isJsonObject(schema) && isJsonObject(schema.schema)) {
1821
+ const strict = typeof schema.strict === "boolean" ? schema.strict : true;
1822
+ return normalizeJsonSchemaObject(schema.schema, strict);
1823
+ }
1824
+ if (!isJsonObject(schema)) {
1825
+ throw new Error(
1826
+ "outputSchema must be a plain JSON object or an OpenAI-style json_schema wrapper"
1827
+ );
1828
+ }
1829
+ return normalizeJsonSchemaObject(schema, true);
1830
+ }
1831
+ async function createOutputSchemaFile(schema) {
1832
+ const normalizedSchema = normalizeOutputSchema(schema);
1833
+ if (!normalizedSchema) {
1834
+ return { cleanup: async () => {
1835
+ } };
1836
+ }
1837
+ const schemaDir = await import_node_fs4.promises.mkdtemp(import_node_path5.default.join(import_node_os.default.tmpdir(), "codex-output-schema-"));
1838
+ const schemaPath = import_node_path5.default.join(schemaDir, "schema.json");
1839
+ const cleanup = async () => {
1840
+ try {
1841
+ await import_node_fs4.promises.rm(schemaDir, { recursive: true, force: true });
1842
+ } catch {
1843
+ }
1844
+ };
1845
+ try {
1846
+ await import_node_fs4.promises.writeFile(schemaPath, JSON.stringify(normalizedSchema), "utf8");
1847
+ return { schemaPath, cleanup };
1848
+ } catch (error) {
1849
+ await cleanup();
1850
+ throw error;
1851
+ }
1852
+ }
1853
+ function isJsonObject(value) {
1854
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1855
+ }
1856
+ function normalizeJsonSchemaObject(schema, strict) {
1857
+ const record = { ...schema };
1858
+ const hasExplicitAdditional = typeof record.additionalProperties === "boolean" || typeof record.additionalProperties === "object";
1859
+ const additionalProperties = hasExplicitAdditional ? record.additionalProperties : strict ? false : record.additionalProperties;
1860
+ return {
1861
+ ...record,
1862
+ ...hasExplicitAdditional || strict ? { additionalProperties } : {}
1863
+ };
1864
+ }
1865
+
1866
+ // src/tui.ts
1867
+ function startTui(request) {
1868
+ const binding = getNativeBinding();
1869
+ if (!binding) {
1870
+ throw new Error("Native binding is not available");
1871
+ }
1872
+ if (typeof binding.startTui === "function") {
1873
+ const nativeSession = binding.startTui(request);
1874
+ return wrapNativeSession(nativeSession);
1875
+ }
1876
+ if (typeof binding.runTui === "function") {
1877
+ return createLegacySession(binding, request);
1878
+ }
1879
+ throw new Error("Native binding does not expose startTui or runTui");
1880
+ }
1881
+ async function runTui(request, options = {}) {
1882
+ const session = startTui(request);
1883
+ const { signal } = options;
1884
+ let abortListener;
1885
+ try {
1886
+ if (signal) {
1887
+ if (signal.aborted) {
1888
+ session.shutdown();
1889
+ } else {
1890
+ abortListener = () => session.shutdown();
1891
+ signal.addEventListener("abort", abortListener, { once: true });
1892
+ }
1893
+ }
1894
+ return await session.wait();
1895
+ } finally {
1896
+ if (abortListener && signal) {
1897
+ signal.removeEventListener("abort", abortListener);
1898
+ }
1899
+ }
1900
+ }
1901
+ function wrapNativeSession(nativeSession) {
1902
+ return {
1903
+ wait: () => nativeSession.wait(),
1904
+ shutdown: () => nativeSession.shutdown(),
1905
+ get closed() {
1906
+ return nativeSession.closed;
1907
+ }
1908
+ };
1909
+ }
1910
+ function createLegacySession(binding, request) {
1911
+ if (typeof binding.runTui !== "function") {
1912
+ throw new Error("Native binding does not expose runTui");
1913
+ }
1914
+ let closed = false;
1915
+ const promise = binding.runTui(request).then(
1916
+ (result) => {
1917
+ closed = true;
1918
+ return result;
1919
+ },
1920
+ (error) => {
1921
+ closed = true;
1922
+ throw error;
1923
+ }
1924
+ );
1925
+ return {
1926
+ wait: () => promise,
1927
+ shutdown() {
1928
+ throw new Error(
1929
+ "Programmatic shutdown is not supported by this native binding build. Rebuild the SDK to enable startTui()."
1930
+ );
1931
+ },
1932
+ get closed() {
1933
+ return closed;
1934
+ }
1935
+ };
1936
+ }
1937
+
1938
+ // src/thread.ts
1939
+ var UNTRUSTED_DIRECTORY_ERROR = "Not inside a trusted directory and --skip-git-repo-check was not specified.";
1940
+ function findGitRoot2(startDir) {
1941
+ let current = path11.resolve(startDir);
1942
+ while (true) {
1943
+ const gitPath = path11.join(current, ".git");
1944
+ if (fs7.existsSync(gitPath)) {
1945
+ try {
1946
+ const stats = fs7.statSync(gitPath);
1947
+ if (stats.isDirectory() || stats.isFile()) {
1948
+ return current;
1949
+ }
1950
+ } catch {
1951
+ }
1952
+ }
1953
+ const parent = path11.dirname(current);
1954
+ if (parent === current) {
1955
+ break;
1956
+ }
1957
+ current = parent;
1958
+ }
1959
+ return null;
1960
+ }
1961
+ function assertTrustedDirectory2(workingDirectory) {
1962
+ const directory = workingDirectory ? path11.resolve(workingDirectory) : process.cwd();
1963
+ if (!findGitRoot2(directory)) {
1964
+ throw new Error(UNTRUSTED_DIRECTORY_ERROR);
1965
+ }
1966
+ }
1967
+ var Thread = class _Thread {
1968
+ _exec;
1969
+ _options;
1970
+ _id;
1971
+ _threadOptions;
1972
+ _eventListeners = [];
1973
+ _approvalHandler = null;
1974
+ /** Returns the ID of the thread. Populated after the first turn starts. */
1975
+ get id() {
1976
+ return this._id;
1977
+ }
1978
+ /**
1979
+ * Register an event listener for thread events.
1980
+ * @param listener Callback function that receives ThreadEvent objects
1981
+ * @returns Unsubscribe function to remove the listener
1982
+ */
1983
+ onEvent(listener) {
1984
+ this._eventListeners.push(listener);
1985
+ return () => {
1986
+ const index = this._eventListeners.indexOf(listener);
1987
+ if (index !== -1) {
1988
+ this._eventListeners.splice(index, 1);
1989
+ }
1990
+ };
1991
+ }
1992
+ /**
1993
+ * Remove an event listener.
1994
+ * @param listener The listener function to remove
1995
+ */
1996
+ offEvent(listener) {
1997
+ const index = this._eventListeners.indexOf(listener);
1998
+ if (index !== -1) {
1999
+ this._eventListeners.splice(index, 1);
2000
+ }
2001
+ }
2002
+ /**
2003
+ * Register a callback to handle approval requests from the agent.
2004
+ * The handler should return true to approve the action, false to deny it.
2005
+ *
2006
+ * @param handler Callback function that receives ApprovalRequest and returns approval decision
2007
+ * @example
2008
+ * ```typescript
2009
+ * thread.onApprovalRequest(async (request) => {
2010
+ * console.log(`Approval requested for ${request.type}`);
2011
+ * return true; // Auto-approve
2012
+ * });
2013
+ * ```
2014
+ */
2015
+ onApprovalRequest(handler) {
2016
+ this._approvalHandler = handler;
2017
+ const binding = getNativeBinding();
2018
+ if (binding && typeof binding.registerApprovalCallback === "function") {
2019
+ binding.registerApprovalCallback(handler);
2020
+ }
2021
+ }
2022
+ /**
2023
+ * Emit a background notification while the agent is running the current turn.
2024
+ * The message is surfaced to event subscribers but does not modify the user input queue.
2025
+ *
2026
+ * @throws Error if the thread has not been started yet.
2027
+ */
2028
+ async sendBackgroundEvent(message) {
2029
+ const trimmed = message?.toString();
2030
+ if (!trimmed || trimmed.trim().length === 0) {
2031
+ throw new Error("Background event message must be a non-empty string");
2032
+ }
2033
+ if (!this._id) {
2034
+ throw new Error("Cannot emit a background event before the thread has started");
2035
+ }
2036
+ const binding = getNativeBinding();
2037
+ if (!binding || typeof binding.emitBackgroundEvent !== "function") {
2038
+ throw new Error("emitBackgroundEvent is not available in this build");
2039
+ }
2040
+ await binding.emitBackgroundEvent({ threadId: this._id, message: trimmed });
2041
+ }
2042
+ /**
2043
+ * Programmatically update the agent's plan/todo list.
2044
+ * The plan will be applied at the start of the next turn.
2045
+ *
2046
+ * @param args The plan update arguments
2047
+ * @throws Error if no thread ID is available
2048
+ */
2049
+ updatePlan(args) {
2050
+ if (!this._id) {
2051
+ throw new Error("Cannot update plan: no active thread");
2052
+ }
2053
+ const binding = getNativeBinding();
2054
+ if (!binding || typeof binding.emitPlanUpdate !== "function") {
2055
+ throw new Error("emitPlanUpdate is not available in this build");
2056
+ }
2057
+ binding.emitPlanUpdate({
2058
+ threadId: this._id,
2059
+ explanation: args.explanation,
2060
+ plan: args.plan
2061
+ });
2062
+ }
2063
+ /**
2064
+ * Modify the agent's plan/todo list with granular operations.
2065
+ * Changes will be applied at the start of the next turn.
2066
+ *
2067
+ * @param operations Array of operations to perform on the plan
2068
+ * @throws Error if no thread ID is available
2069
+ */
2070
+ modifyPlan(operations) {
2071
+ if (!this._id) {
2072
+ throw new Error("Cannot modify plan: no active thread");
2073
+ }
2074
+ const binding = getNativeBinding();
2075
+ if (!binding || typeof binding.modifyPlan !== "function") {
2076
+ throw new Error("modifyPlan is not available in this build");
2077
+ }
2078
+ binding.modifyPlan({
2079
+ threadId: this._id,
2080
+ operations
2081
+ });
2082
+ }
2083
+ /**
2084
+ * Add a new todo item to the agent's plan.
2085
+ *
2086
+ * @param step The todo step description
2087
+ * @param status The initial status (defaults to "pending")
2088
+ */
2089
+ addTodo(step, status = "pending") {
2090
+ this.modifyPlan([{ type: "add", item: { step, status } }]);
2091
+ }
2092
+ /**
2093
+ * Update an existing todo item.
2094
+ *
2095
+ * @param index The index of the todo item to update
2096
+ * @param updates The updates to apply
2097
+ */
2098
+ updateTodo(index, updates) {
2099
+ this.modifyPlan([{ type: "update", index, updates }]);
2100
+ }
2101
+ /**
2102
+ * Remove a todo item from the plan.
2103
+ *
2104
+ * @param index The index of the todo item to remove
2105
+ */
2106
+ removeTodo(index) {
2107
+ this.modifyPlan([{ type: "remove", index }]);
2108
+ }
2109
+ /**
2110
+ * Reorder the todo items in the plan.
2111
+ *
2112
+ * @param newOrder Array of indices representing the new order
2113
+ */
2114
+ reorderTodos(newOrder) {
2115
+ this.modifyPlan([{ type: "reorder", newOrder }]);
2116
+ }
2117
+ /** Compacts the conversation history for this thread using Codex's builtin compaction. */
2118
+ async compact() {
2119
+ const skipGitRepoCheck = this._threadOptions?.skipGitRepoCheck ?? (typeof process !== "undefined" && process.env && process.env.CODEX_TEST_SKIP_GIT_REPO_CHECK === "1");
2120
+ if (!skipGitRepoCheck) {
2121
+ assertTrustedDirectory2(this._threadOptions?.workingDirectory);
2122
+ }
2123
+ const events = await this._exec.compact({
2124
+ input: "compact",
2125
+ threadId: this._id,
2126
+ baseUrl: this._options.baseUrl,
2127
+ apiKey: this._options.apiKey,
2128
+ model: this._threadOptions?.model ?? this._options.defaultModel,
2129
+ sandboxMode: this._threadOptions?.sandboxMode,
2130
+ approvalMode: this._threadOptions?.approvalMode,
2131
+ workspaceWriteOptions: this._threadOptions?.workspaceWriteOptions,
2132
+ workingDirectory: this._threadOptions?.workingDirectory,
2133
+ skipGitRepoCheck,
2134
+ modelProvider: this._options.modelProvider
2135
+ });
2136
+ if (!Array.isArray(events)) {
2137
+ throw new Error("Compact did not return event list");
2138
+ }
2139
+ }
2140
+ /**
2141
+ * Fork this thread at the specified user message, returning a new thread that starts
2142
+ * from the conversation history prior to that message.
2143
+ *
2144
+ * @param options Fork configuration including which user message to branch before and optional thread overrides.
2145
+ */
2146
+ async fork(options) {
2147
+ if (!this._id) {
2148
+ throw new Error("Cannot fork: no active thread");
2149
+ }
2150
+ const nthUserMessage = options?.nthUserMessage;
2151
+ if (typeof nthUserMessage !== "number" || !Number.isInteger(nthUserMessage) || nthUserMessage < 0) {
2152
+ throw new Error("nthUserMessage must be a non-negative integer");
2153
+ }
2154
+ const overrides = options.threadOptions ?? {};
2155
+ const nextThreadOptions = {
2156
+ ...this._threadOptions,
2157
+ ...overrides
2158
+ };
2159
+ const skipGitRepoCheck = nextThreadOptions.skipGitRepoCheck ?? (typeof process !== "undefined" && process.env && process.env.CODEX_TEST_SKIP_GIT_REPO_CHECK === "1");
2160
+ nextThreadOptions.skipGitRepoCheck = skipGitRepoCheck;
2161
+ if (!skipGitRepoCheck) {
2162
+ assertTrustedDirectory2(nextThreadOptions.workingDirectory);
2163
+ }
2164
+ const forkArgs = {
2165
+ threadId: this._id,
2166
+ nthUserMessage,
2167
+ baseUrl: this._options.baseUrl,
2168
+ apiKey: this._options.apiKey,
2169
+ model: nextThreadOptions.model ?? this._options.defaultModel,
2170
+ oss: nextThreadOptions.oss,
2171
+ sandboxMode: nextThreadOptions.sandboxMode,
2172
+ approvalMode: nextThreadOptions.approvalMode,
2173
+ workspaceWriteOptions: nextThreadOptions.workspaceWriteOptions,
2174
+ workingDirectory: nextThreadOptions.workingDirectory,
2175
+ skipGitRepoCheck,
2176
+ fullAuto: nextThreadOptions.fullAuto,
2177
+ modelProvider: this._options.modelProvider
2178
+ };
2179
+ const result = await this._exec.fork(forkArgs);
2180
+ return new _Thread(
2181
+ this._exec,
2182
+ this._options,
2183
+ nextThreadOptions,
2184
+ result.threadId
2185
+ );
2186
+ }
2187
+ /* @internal */
2188
+ constructor(exec, options, threadOptions, id = null) {
2189
+ this._exec = exec;
2190
+ this._options = options;
2191
+ this._id = id;
2192
+ this._threadOptions = threadOptions;
2193
+ }
2194
+ /** Provides the input to the agent and streams events as they are produced during the turn. */
2195
+ async runStreamed(input, turnOptions = {}) {
2196
+ return { events: this.runStreamedInternal(input, turnOptions, false) };
2197
+ }
2198
+ async *runStreamedInternal(input, turnOptions = {}, emitRawEvents = true) {
2199
+ const normalizedSchema = normalizeOutputSchema(turnOptions.outputSchema);
2200
+ const needsSchemaFile = this._exec.requiresOutputSchemaFile();
2201
+ const schemaFile = needsSchemaFile ? await createOutputSchemaFile(normalizedSchema) : { schemaPath: void 0, cleanup: async () => {
2202
+ } };
2203
+ const options = this._threadOptions;
2204
+ const { prompt, images } = normalizeInput(input);
2205
+ const skipGitRepoCheck = options?.skipGitRepoCheck ?? (typeof process !== "undefined" && process.env && process.env.CODEX_TEST_SKIP_GIT_REPO_CHECK === "1");
2206
+ if (!skipGitRepoCheck) {
2207
+ assertTrustedDirectory2(options?.workingDirectory);
2208
+ }
2209
+ const generator = this._exec.run({
2210
+ input: prompt,
2211
+ baseUrl: this._options.baseUrl,
2212
+ apiKey: this._options.apiKey,
2213
+ threadId: this._id,
2214
+ images,
2215
+ model: options?.model,
2216
+ oss: turnOptions?.oss ?? options?.oss,
2217
+ sandboxMode: options?.sandboxMode,
2218
+ approvalMode: options?.approvalMode,
2219
+ workspaceWriteOptions: options?.workspaceWriteOptions,
2220
+ workingDirectory: options?.workingDirectory,
2221
+ skipGitRepoCheck,
2222
+ outputSchemaFile: schemaFile.schemaPath,
2223
+ outputSchema: normalizedSchema,
2224
+ fullAuto: options?.fullAuto
2225
+ });
2226
+ try {
2227
+ for await (const item of generator) {
2228
+ let parsed;
2229
+ try {
2230
+ parsed = JSON.parse(item);
2231
+ } catch (error) {
2232
+ throw new Error(`Failed to parse item: ${item}. Parse error: ${error}`);
2233
+ }
2234
+ if (parsed === null) {
2235
+ continue;
2236
+ }
2237
+ if (emitRawEvents) {
2238
+ yield { type: "raw_event", raw: parsed };
2239
+ }
2240
+ const threadEvent = convertRustEventToThreadEvent(parsed);
2241
+ if (threadEvent.type === "thread.started") {
2242
+ this._id = threadEvent.thread_id;
2243
+ }
2244
+ for (const listener of this._eventListeners) {
2245
+ try {
2246
+ listener(threadEvent);
2247
+ } catch (error) {
2248
+ console.warn("Thread event listener threw error:", error);
2249
+ }
2250
+ }
2251
+ yield threadEvent;
2252
+ }
2253
+ } finally {
2254
+ await schemaFile.cleanup();
2255
+ }
2256
+ }
2257
+ /** Provides the input to the agent and returns the completed turn. */
2258
+ async run(input, turnOptions = {}) {
2259
+ const generator = this.runStreamedInternal(input, turnOptions, true);
2260
+ const items = [];
2261
+ let finalResponse = "";
2262
+ let usage = null;
2263
+ let turnFailure = null;
2264
+ for await (const event of generator) {
2265
+ if (event.type === "item.completed") {
2266
+ if (event.item.type === "agent_message") {
2267
+ finalResponse = event.item.text;
2268
+ }
2269
+ items.push(event.item);
2270
+ } else if (event.type === "turn.completed") {
2271
+ usage = event.usage;
2272
+ } else if (event.type === "turn.failed") {
2273
+ turnFailure = event.error;
2274
+ break;
2275
+ }
2276
+ }
2277
+ if (turnFailure) {
2278
+ throw new Error(turnFailure.message);
2279
+ }
2280
+ return { items, finalResponse, usage };
2281
+ }
2282
+ buildTuiRequest(overrides = {}) {
2283
+ const skipGitRepoCheck = this._threadOptions?.skipGitRepoCheck ?? (typeof process !== "undefined" && process.env && process.env.CODEX_TEST_SKIP_GIT_REPO_CHECK === "1");
2284
+ if (!skipGitRepoCheck) {
2285
+ assertTrustedDirectory2(this._threadOptions?.workingDirectory);
2286
+ }
2287
+ const request = { ...overrides };
2288
+ const assignIfUndefined = (key, value) => {
2289
+ if (request[key] === void 0 && value !== void 0) {
2290
+ request[key] = value;
2291
+ }
2292
+ };
2293
+ assignIfUndefined("model", this._threadOptions?.model ?? this._options.defaultModel);
2294
+ assignIfUndefined("oss", this._threadOptions?.oss);
2295
+ assignIfUndefined("sandboxMode", this._threadOptions?.sandboxMode);
2296
+ assignIfUndefined("approvalMode", this._threadOptions?.approvalMode);
2297
+ assignIfUndefined("fullAuto", this._threadOptions?.fullAuto);
2298
+ assignIfUndefined("workingDirectory", this._threadOptions?.workingDirectory);
2299
+ assignIfUndefined("baseUrl", this._options.baseUrl);
2300
+ assignIfUndefined("apiKey", this._options.apiKey);
2301
+ if (request.resumeSessionId === void 0 && request.resumePicker !== true && request.resumeLast !== true && this._id) {
2302
+ request.resumeSessionId = this._id;
2303
+ }
2304
+ return request;
2305
+ }
2306
+ /**
2307
+ * Launches the interactive Codex TUI (Terminal User Interface) for this thread and returns a session handle.
2308
+ *
2309
+ * The handle allows advanced workflows where the TUI can be started and stopped programmatically,
2310
+ * while preserving the underlying conversation state.
2311
+ */
2312
+ launchTui(overrides = {}) {
2313
+ const request = this.buildTuiRequest(overrides);
2314
+ const detachLsp = this.attachDefaultLspBridge(request);
2315
+ const session = startTui(request);
2316
+ return this.wrapTuiSession(session, detachLsp);
2317
+ }
2318
+ /**
2319
+ * Launches the interactive Codex TUI (Terminal User Interface) for this thread.
2320
+ *
2321
+ * This method enables seamless transition from programmatic agent interaction to
2322
+ * interactive terminal chat within the same session. The TUI takes over the terminal
2323
+ * and allows you to continue the conversation interactively.
2324
+ *
2325
+ * @param overrides - Optional configuration to override thread defaults. Supports all TUI options
2326
+ * including prompt, sandbox mode, approval mode, and resume options.
2327
+ * @param options - Optional run options including an AbortSignal to request shutdown.
2328
+ * @returns A Promise that resolves to TUI exit information including:
2329
+ * - tokenUsage: Token consumption statistics
2330
+ * - conversationId: Session ID for resuming later
2331
+ * - updateAction: Optional suggested update command
2332
+ * @throws {Error} If not in a trusted git repository (unless skipGitRepoCheck is set)
2333
+ * @throws {Error} If the terminal is not interactive (TTY required)
2334
+ */
2335
+ async tui(overrides = {}, options = {}) {
2336
+ const request = this.buildTuiRequest(overrides);
2337
+ const detachLsp = this.attachDefaultLspBridge(request);
2338
+ try {
2339
+ return await runTui(request, options);
2340
+ } finally {
2341
+ detachLsp();
2342
+ }
2343
+ }
2344
+ wrapTuiSession(session, cleanup) {
2345
+ let released = false;
2346
+ const release = () => {
2347
+ if (released) {
2348
+ return;
2349
+ }
2350
+ released = true;
2351
+ cleanup();
2352
+ };
2353
+ return {
2354
+ wait: async () => {
2355
+ try {
2356
+ return await session.wait();
2357
+ } finally {
2358
+ release();
2359
+ }
2360
+ },
2361
+ shutdown: () => {
2362
+ release();
2363
+ session.shutdown();
2364
+ },
2365
+ get closed() {
2366
+ return session.closed;
2367
+ }
2368
+ };
2369
+ }
2370
+ attachDefaultLspBridge(request) {
2371
+ const workingDirectory = request.workingDirectory ?? this._threadOptions?.workingDirectory ?? (typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".");
2372
+ return attachLspDiagnostics(this, {
2373
+ workingDirectory,
2374
+ waitForDiagnostics: true
2375
+ });
2376
+ }
2377
+ };
2378
+ function normalizeInput(input) {
2379
+ if (typeof input === "string") {
2380
+ return { prompt: input, images: [] };
2381
+ }
2382
+ const promptParts = [];
2383
+ const images = [];
2384
+ for (const item of input) {
2385
+ if (item.type === "text") {
2386
+ promptParts.push(item.text);
2387
+ } else if (item.type === "local_image") {
2388
+ images.push(item.path);
2389
+ }
2390
+ }
2391
+ return { prompt: promptParts.join("\n\n"), images };
2392
+ }
2393
+
2394
+ // src/reviewOptions.ts
2395
+ function buildReviewPrompt(target) {
2396
+ switch (target.type) {
2397
+ case "current_changes":
2398
+ return {
2399
+ prompt: "Review the current code changes (staged, unstaged, and untracked files) and provide prioritized findings.",
2400
+ hint: "current changes"
2401
+ };
2402
+ case "branch": {
2403
+ const branch = target.baseBranch;
2404
+ const prompt = `Review the code changes against the base branch '${branch}'. Start by finding the merge diff between the current branch and ${branch}'s upstream e.g. (\`git merge-base HEAD "$(git rev-parse --abbrev-ref "${branch}@{upstream}")"\`), then run \`git diff\` against that SHA to see what changes we would merge into the ${branch} branch. Provide prioritized, actionable findings.`;
2405
+ return {
2406
+ prompt,
2407
+ hint: `changes against '${branch}'`
2408
+ };
2409
+ }
2410
+ case "commit": {
2411
+ const shortSha = target.sha.slice(0, 7);
2412
+ const subject = target.subject ?? target.sha;
2413
+ return {
2414
+ prompt: `Review the code changes introduced by commit ${target.sha} ("${subject}"). Provide prioritized, actionable findings.`,
2415
+ hint: `commit ${shortSha}`
2416
+ };
2417
+ }
2418
+ case "custom": {
2419
+ const hint = target.hint ?? "custom review";
2420
+ return {
2421
+ prompt: target.prompt,
2422
+ hint
2423
+ };
2424
+ }
2425
+ default: {
2426
+ const exhaustive = target;
2427
+ throw new Error(`Unsupported review target: ${String(exhaustive)}`);
2428
+ }
2429
+ }
2430
+ }
2431
+
2432
+ // src/codex.ts
2433
+ var Codex = class {
2434
+ exec;
2435
+ options;
2436
+ nativeBinding;
2437
+ lspForTools;
2438
+ constructor(options = {}) {
2439
+ const predefinedTools = options.tools ? [...options.tools] : [];
2440
+ const preserveRegisteredTools = options.preserveRegisteredTools === true;
2441
+ this.nativeBinding = getNativeBinding();
2442
+ this.options = { ...options, tools: [] };
2443
+ if (this.nativeBinding) {
2444
+ if (!preserveRegisteredTools && typeof this.nativeBinding.clearRegisteredTools === "function") {
2445
+ this.nativeBinding.clearRegisteredTools();
2446
+ }
2447
+ for (const tool of predefinedTools) {
2448
+ this.registerTool(tool);
2449
+ }
2450
+ }
2451
+ this.lspForTools = this.createLspManagerForTools();
2452
+ if (this.lspForTools && this.nativeBinding) {
2453
+ this.registerDefaultReadFileInterceptor();
2454
+ }
2455
+ this.exec = new CodexExec();
2456
+ }
2457
+ /**
2458
+ * Register a tool for Codex. When `tool.name` matches a built-in Codex tool,
2459
+ * the native implementation is replaced for this Codex instance.
2460
+ */
2461
+ registerTool(tool) {
2462
+ if (!this.nativeBinding) {
2463
+ throw new Error("Native tool registration requires the NAPI binding");
2464
+ }
2465
+ if (typeof this.nativeBinding.registerTool !== "function") {
2466
+ console.warn("registerTool is not available in this build - tools feature may be incomplete");
2467
+ return;
2468
+ }
2469
+ const { handler, ...info } = tool;
2470
+ this.nativeBinding.registerTool(info, handler);
2471
+ if (!this.options.tools) {
2472
+ this.options.tools = [];
2473
+ }
2474
+ this.options.tools.push(tool);
2475
+ }
2476
+ /**
2477
+ * Register a tool interceptor for Codex. Interceptors can modify tool invocations
2478
+ * and results, and can call the built-in implementation.
2479
+ */
2480
+ registerToolInterceptor(toolName, handler) {
2481
+ if (!this.nativeBinding) {
2482
+ throw new Error("Native tool interceptor registration requires the NAPI binding");
2483
+ }
2484
+ if (typeof this.nativeBinding.registerToolInterceptor !== "function" || typeof this.nativeBinding.callToolBuiltin !== "function") {
2485
+ console.warn("registerToolInterceptor is not available in this build - interceptor feature may be incomplete");
2486
+ return;
2487
+ }
2488
+ this.nativeBinding.registerToolInterceptor(toolName, async (...args) => {
2489
+ const context = args.length === 1 ? args[0] : args[1];
2490
+ if (!context || typeof context !== "object") {
2491
+ throw new Error("Native interceptor callback did not receive a context object");
2492
+ }
2493
+ const { invocation, token } = context;
2494
+ const callBuiltin = (override) => this.nativeBinding.callToolBuiltin(token, override ?? invocation);
2495
+ return handler({ invocation, callBuiltin });
2496
+ });
2497
+ }
2498
+ /**
2499
+ * Clear all registered tools, restoring built-in defaults.
2500
+ */
2501
+ clearTools() {
2502
+ if (!this.nativeBinding) {
2503
+ throw new Error("Native tool management requires the NAPI binding");
2504
+ }
2505
+ if (typeof this.nativeBinding.clearRegisteredTools === "function") {
2506
+ this.nativeBinding.clearRegisteredTools();
2507
+ }
2508
+ if (this.options.tools) {
2509
+ this.options.tools = [];
2510
+ }
2511
+ }
2512
+ buildConversationConfig(options = {}) {
2513
+ return {
2514
+ model: options.model ?? this.options.defaultModel,
2515
+ modelProvider: options.modelProvider ?? this.options.modelProvider,
2516
+ oss: options.oss,
2517
+ sandboxMode: options.sandboxMode,
2518
+ approvalMode: options.approvalMode,
2519
+ workspaceWriteOptions: options.workspaceWriteOptions,
2520
+ workingDirectory: options.workingDirectory,
2521
+ skipGitRepoCheck: options.skipGitRepoCheck,
2522
+ reasoningEffort: options.reasoningEffort,
2523
+ reasoningSummary: options.reasoningSummary,
2524
+ fullAuto: options.fullAuto,
2525
+ baseUrl: this.options.baseUrl,
2526
+ apiKey: this.options.apiKey
2527
+ };
2528
+ }
2529
+ createLspManagerForTools() {
2530
+ const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".";
2531
+ const options = {
2532
+ workingDirectory: cwd,
2533
+ waitForDiagnostics: true
2534
+ };
2535
+ try {
2536
+ return new LspManager(options);
2537
+ } catch {
2538
+ return null;
2539
+ }
2540
+ }
2541
+ registerDefaultReadFileInterceptor() {
2542
+ if (!this.lspForTools) {
2543
+ return;
2544
+ }
2545
+ try {
2546
+ this.registerToolInterceptor("read_file", async ({ invocation, callBuiltin }) => {
2547
+ let base;
2548
+ try {
2549
+ base = await callBuiltin();
2550
+ } catch (err) {
2551
+ return {
2552
+ success: false,
2553
+ error: err instanceof Error ? err.message : String(err),
2554
+ output: void 0
2555
+ };
2556
+ }
2557
+ if (!base.output || base.success === false) {
2558
+ return base;
2559
+ }
2560
+ let filePath;
2561
+ if (invocation.arguments) {
2562
+ try {
2563
+ const args = JSON.parse(invocation.arguments);
2564
+ const candidate = typeof args.file_path === "string" && args.file_path || typeof args.path === "string" && args.path || void 0;
2565
+ if (candidate && candidate.trim().length > 0) {
2566
+ filePath = candidate;
2567
+ }
2568
+ } catch {
2569
+ }
2570
+ }
2571
+ if (!filePath) {
2572
+ return base;
2573
+ }
2574
+ let diagnosticsText = "";
2575
+ try {
2576
+ const results = await this.lspForTools.collectDiagnostics([filePath]);
2577
+ if (!results.length) {
2578
+ return base;
2579
+ }
2580
+ diagnosticsText = formatDiagnosticsForTool(results);
2581
+ } catch {
2582
+ return base;
2583
+ }
2584
+ if (!diagnosticsText) {
2585
+ return base;
2586
+ }
2587
+ const header = `LSP diagnostics for ${filePath}:
2588
+ ${diagnosticsText}`;
2589
+ return prependSystemHintToToolResult(base, header);
2590
+ });
2591
+ } catch {
2592
+ }
2593
+ }
2594
+ /**
2595
+ * Register a programmatic approval callback that Codex will call before executing
2596
+ * sensitive operations (e.g., shell commands, file writes).
2597
+ */
2598
+ setApprovalCallback(handler) {
2599
+ if (!this.nativeBinding || typeof this.nativeBinding.registerApprovalCallback !== "function") {
2600
+ console.warn("Approval callback is not available in this build");
2601
+ return;
2602
+ }
2603
+ this.nativeBinding.registerApprovalCallback(handler);
2604
+ }
2605
+ /**
2606
+ * Starts a new conversation with an agent.
2607
+ * @returns A new thread instance.
2608
+ */
2609
+ startThread(options = {}) {
2610
+ const threadOptions = {
2611
+ ...options,
2612
+ model: options.model ?? this.options.defaultModel
2613
+ };
2614
+ return new Thread(this.exec, this.options, threadOptions);
2615
+ }
2616
+ /**
2617
+ * Resumes a conversation with an agent based on the thread id.
2618
+ * Threads are persisted in ~/.codex/sessions.
2619
+ *
2620
+ * @param id The id of the thread to resume.
2621
+ * @returns A new thread instance.
2622
+ */
2623
+ resumeThread(id, options = {}) {
2624
+ const threadOptions = {
2625
+ ...options,
2626
+ model: options.model ?? this.options.defaultModel
2627
+ };
2628
+ return new Thread(this.exec, this.options, threadOptions, id);
2629
+ }
2630
+ async listConversations(options = {}) {
2631
+ const request = {
2632
+ config: this.buildConversationConfig(options),
2633
+ pageSize: options.pageSize,
2634
+ cursor: options.cursor,
2635
+ modelProviders: options.modelProviders
2636
+ };
2637
+ return this.exec.listConversations(request);
2638
+ }
2639
+ async deleteConversation(id, options = {}) {
2640
+ const result = await this.exec.deleteConversation({
2641
+ id,
2642
+ config: this.buildConversationConfig(options)
2643
+ });
2644
+ return result.deleted;
2645
+ }
2646
+ async resumeConversationFromRollout(rolloutPath, options = {}) {
2647
+ const result = await this.exec.resumeConversationFromRollout({
2648
+ rolloutPath,
2649
+ config: this.buildConversationConfig(options)
2650
+ });
2651
+ const threadOptions = {
2652
+ ...options,
2653
+ model: options.model ?? this.options.defaultModel
2654
+ };
2655
+ return new Thread(this.exec, this.options, threadOptions, result.threadId);
2656
+ }
2657
+ /**
2658
+ * Starts a review task using the built-in Codex review flow.
2659
+ */
2660
+ async review(options) {
2661
+ const generator = this.reviewStreamedInternal(options);
2662
+ const items = [];
2663
+ let finalResponse = "";
2664
+ let usage = null;
2665
+ let turnFailure = null;
2666
+ for await (const event of generator) {
2667
+ if (event === null) continue;
2668
+ if (event.type === "item.completed") {
2669
+ if (event.item.type === "agent_message") {
2670
+ finalResponse = event.item.text;
2671
+ }
2672
+ items.push(event.item);
2673
+ } else if (event.type === "exited_review_mode") {
2674
+ if (event.review_output) {
2675
+ const reviewOutput = event.review_output;
2676
+ let reviewText = "";
2677
+ if (reviewOutput.overall_explanation) {
2678
+ reviewText += reviewOutput.overall_explanation;
2679
+ }
2680
+ if (reviewOutput.findings && reviewOutput.findings.length > 0) {
2681
+ if (reviewText) reviewText += "\n\n";
2682
+ reviewText += "## Review Findings\n\n";
2683
+ reviewOutput.findings.forEach((finding, index) => {
2684
+ reviewText += `### ${index + 1}. ${finding.title}
2685
+ `;
2686
+ reviewText += `${finding.body}
2687
+ `;
2688
+ reviewText += `**Priority:** ${finding.priority} | **Confidence:** ${finding.confidence_score}
2689
+ `;
2690
+ reviewText += `**Location:** ${finding.code_location.absolute_file_path}:${finding.code_location.line_range.start}-${finding.code_location.line_range.end}
2691
+
2692
+ `;
2693
+ });
2694
+ }
2695
+ finalResponse = reviewText;
2696
+ }
2697
+ } else if (event.type === "turn.completed") {
2698
+ usage = event.usage;
2699
+ } else if (event.type === "turn.failed") {
2700
+ turnFailure = event.error;
2701
+ break;
2702
+ }
2703
+ }
2704
+ if (turnFailure) {
2705
+ throw new Error(turnFailure.message);
2706
+ }
2707
+ return { items, finalResponse, usage };
2708
+ }
2709
+ /**
2710
+ * Starts a review task and returns the event stream.
2711
+ */
2712
+ async reviewStreamed(options) {
2713
+ return { events: this.reviewStreamedInternal(options) };
2714
+ }
2715
+ async *reviewStreamedInternal(options) {
2716
+ const { target, threadOptions = {}, turnOptions = {} } = options;
2717
+ const { prompt, hint } = buildReviewPrompt(target);
2718
+ const normalizedSchema = normalizeOutputSchema(turnOptions.outputSchema);
2719
+ const needsSchemaFile = this.exec.requiresOutputSchemaFile();
2720
+ const schemaFile = needsSchemaFile ? await createOutputSchemaFile(normalizedSchema) : { schemaPath: void 0, cleanup: async () => {
2721
+ } };
2722
+ const generator = this.exec.run({
2723
+ input: prompt,
2724
+ baseUrl: this.options.baseUrl,
2725
+ apiKey: this.options.apiKey,
2726
+ model: threadOptions.model,
2727
+ modelProvider: threadOptions.modelProvider ?? this.options.modelProvider,
2728
+ oss: threadOptions.oss,
2729
+ sandboxMode: threadOptions.sandboxMode,
2730
+ approvalMode: threadOptions.approvalMode,
2731
+ workspaceWriteOptions: threadOptions.workspaceWriteOptions,
2732
+ workingDirectory: threadOptions.workingDirectory,
2733
+ skipGitRepoCheck: threadOptions.skipGitRepoCheck,
2734
+ outputSchemaFile: schemaFile.schemaPath,
2735
+ outputSchema: normalizedSchema,
2736
+ fullAuto: threadOptions.fullAuto,
2737
+ review: {
2738
+ userFacingHint: hint
2739
+ }
2740
+ });
2741
+ try {
2742
+ for await (const item of generator) {
2743
+ let parsed;
2744
+ try {
2745
+ parsed = JSON.parse(item);
2746
+ } catch (error) {
2747
+ throw new Error(`Failed to parse item: ${item}`, { cause: error });
2748
+ }
2749
+ yield parsed;
2750
+ }
2751
+ } finally {
2752
+ await schemaFile.cleanup();
2753
+ }
2754
+ }
2755
+ };
2756
+ function prependSystemHintToToolResult(base, hint) {
2757
+ const trimmedHint = hint.trim();
2758
+ if (!trimmedHint) {
2759
+ return base;
2760
+ }
2761
+ const existing = base.output ?? "";
2762
+ const separator = existing.length === 0 || existing.startsWith("\n") ? "\n\n" : "\n\n";
2763
+ const output = existing.length === 0 ? `[SYSTEM_HINT]
2764
+ ${trimmedHint}` : `[SYSTEM_HINT]
2765
+ ${trimmedHint}${separator}${existing}`;
2766
+ return {
2767
+ ...base,
2768
+ output
2769
+ };
2770
+ }
2771
+
2772
+ // src/cli/tui.ts
2773
+ async function executeTuiCommand(argv, context) {
2774
+ if (!import_node_process2.default.stdout.isTTY || !import_node_process2.default.stdin.isTTY) {
2775
+ throw new Error("The interactive TUI requires an interactive terminal (TTY).");
2776
+ }
2777
+ const { combinedConfig } = context;
2778
+ emitWarnings(combinedConfig.warnings);
2779
+ const warningCount = combinedConfig.warnings.length;
2780
+ const { request, thread: threadOptions } = buildTuiConfig({
2781
+ argv,
2782
+ defaults: combinedConfig.tuiDefaults,
2783
+ cwd: context.cwd
2784
+ });
2785
+ applyElevatedTuiDefaults({ request, thread: threadOptions, cwd: context.cwd });
2786
+ const hookContext = {
2787
+ command: "tui",
2788
+ cwd: context.cwd,
2789
+ options: argv
2790
+ };
2791
+ await runBeforeStartHooks(combinedConfig.beforeStartHooks, hookContext, combinedConfig.warnings);
2792
+ const codex = new Codex({
2793
+ baseUrl: request.baseUrl,
2794
+ apiKey: request.apiKey,
2795
+ preserveRegisteredTools: true
2796
+ });
2797
+ const thread = codex.startThread(threadOptions);
2798
+ const exitInfo = await thread.tui(request);
2799
+ if (exitInfo.conversationId) {
2800
+ import_node_process2.default.stdout.write(`
2801
+ Conversation ID: ${exitInfo.conversationId}
2802
+ `);
2803
+ }
2804
+ if (exitInfo.updateAction) {
2805
+ import_node_process2.default.stdout.write(
2806
+ `Update available (${exitInfo.updateAction.kind}): ${exitInfo.updateAction.command}
2807
+ `
2808
+ );
2809
+ }
2810
+ emitWarnings(combinedConfig.warnings, warningCount);
2811
+ }
2812
+ function buildTuiConfig(params) {
2813
+ const { argv, defaults, cwd } = params;
2814
+ const request = {
2815
+ ...defaults
2816
+ };
2817
+ if (argv.prompt !== void 0) request.prompt = argv.prompt;
2818
+ if (argv.model !== void 0) request.model = argv.model;
2819
+ if (argv.oss !== void 0) request.oss = argv.oss;
2820
+ const sandboxMode = parseSandboxModeFlag(argv.sandbox, "--sandbox");
2821
+ if (sandboxMode !== void 0) {
2822
+ request.sandboxMode = sandboxMode;
2823
+ }
2824
+ const approvalMode = parseApprovalModeFlag(argv.approval, "--approval");
2825
+ if (approvalMode !== void 0) {
2826
+ request.approvalMode = approvalMode;
2827
+ }
2828
+ if (argv.resume !== void 0) request.resumeSessionId = argv.resume;
2829
+ if (argv.resumeLast !== void 0) request.resumeLast = argv.resumeLast;
2830
+ if (argv.resumePicker !== void 0) request.resumePicker = argv.resumePicker;
2831
+ if (argv.fullAuto !== void 0) request.fullAuto = argv.fullAuto;
2832
+ if (argv.dangerouslyBypassApprovalsAndSandbox !== void 0) {
2833
+ request.dangerouslyBypassApprovalsAndSandbox = argv.dangerouslyBypassApprovalsAndSandbox;
2834
+ }
2835
+ if (argv.cd !== void 0) request.workingDirectory = argv.cd;
2836
+ if (argv.configProfile !== void 0) request.configProfile = argv.configProfile;
2837
+ if (argv.webSearch !== void 0) request.webSearch = argv.webSearch;
2838
+ if (argv.linuxSandboxPath !== void 0) request.linuxSandboxPath = argv.linuxSandboxPath;
2839
+ if (argv.baseUrl !== void 0) request.baseUrl = argv.baseUrl;
2840
+ if (argv.apiKey !== void 0) request.apiKey = argv.apiKey;
2841
+ if (argv.configOverrides) {
2842
+ const defaultsOverrides = Array.isArray(request.configOverrides) ? [...request.configOverrides] : [];
2843
+ request.configOverrides = [...defaultsOverrides, ...argv.configOverrides];
2844
+ }
2845
+ if (argv.addDir) {
2846
+ const defaultsAddDir = Array.isArray(request.addDir) ? [...request.addDir] : [];
2847
+ request.addDir = [...defaultsAddDir, ...argv.addDir];
2848
+ }
2849
+ if (argv.image) {
2850
+ const defaultsImages = Array.isArray(request.images) ? [...request.images] : [];
2851
+ request.images = [...defaultsImages, ...argv.image];
2852
+ }
2853
+ const thread = {
2854
+ model: request.model,
2855
+ oss: request.oss,
2856
+ sandboxMode: request.sandboxMode,
2857
+ approvalMode: request.approvalMode,
2858
+ workingDirectory: request.workingDirectory ?? cwd,
2859
+ skipGitRepoCheck: false
2860
+ };
2861
+ return { request, thread };
2862
+ }
2863
+
2864
+ // src/cli/reverie.ts
2865
+ var import_node_path6 = __toESM(require("path"));
2866
+ var import_node_os2 = __toESM(require("os"));
2867
+ var import_node_util = require("util");
2868
+ var DEFAULT_MODEL = "mixedbread-ai/mxbai-embed-large-v1";
2869
+ var INDEX_OPTION_DEFS = {
2870
+ "codex-home": { type: "string" },
2871
+ "project-root": { type: "string" },
2872
+ limit: { type: "string" },
2873
+ "max-candidates": { type: "string" },
2874
+ "batch-size": { type: "string" },
2875
+ normalize: { type: "boolean" },
2876
+ cache: { type: "boolean" },
2877
+ "embed-model": { type: "string" },
2878
+ "embed-cache-dir": { type: "string" },
2879
+ "embed-max-length": { type: "string" },
2880
+ "no-progress": { type: "boolean" },
2881
+ "skip-embed-init": { type: "boolean" }
2882
+ };
2883
+ async function executeReverieCommand(args) {
2884
+ const [first, ...rest] = args;
2885
+ const isFlag = first?.startsWith("-");
2886
+ const command = !first || isFlag ? "index" : first;
2887
+ const tail = !first || isFlag ? args : rest;
2888
+ if (command !== "index") {
2889
+ throw new Error(`Unknown reverie command '${command}'. Supported subcommands: index`);
2890
+ }
2891
+ await runReverieIndex(tail);
2892
+ }
2893
+ async function runReverieIndex(args) {
2894
+ const { values } = (0, import_node_util.parseArgs)({ args, options: INDEX_OPTION_DEFS, allowPositionals: false, strict: true });
2895
+ const codexHome = resolveCodexHome(values["codex-home"]);
2896
+ const projectRoot = resolveProjectRoot(values["project-root"]);
2897
+ const limit = parseOptionalInt(values.limit);
2898
+ const maxCandidates = parseOptionalInt(values["max-candidates"]);
2899
+ const batchSize = parseOptionalInt(values["batch-size"]);
2900
+ const embedMaxLength = parseOptionalInt(values["embed-max-length"]);
2901
+ const normalize = typeof values.normalize === "boolean" ? values.normalize : void 0;
2902
+ const cache = typeof values.cache === "boolean" ? values.cache : void 0;
2903
+ const embedModel = typeof values["embed-model"] === "string" ? values["embed-model"] : DEFAULT_MODEL;
2904
+ const embedCacheDir = typeof values["embed-cache-dir"] === "string" ? values["embed-cache-dir"] : void 0;
2905
+ const showDownloadProgress = values["no-progress"] ? false : true;
2906
+ const skipEmbedInit = values["skip-embed-init"] === true;
2907
+ if (!skipEmbedInit) {
2908
+ await fastEmbedInit({
2909
+ model: embedModel,
2910
+ cacheDir: embedCacheDir ? import_node_path6.default.resolve(embedCacheDir) : defaultCacheDir(),
2911
+ maxLength: embedMaxLength ?? void 0,
2912
+ showDownloadProgress
2913
+ });
2914
+ }
2915
+ const options = {
2916
+ limit,
2917
+ maxCandidates,
2918
+ projectRoot,
2919
+ batchSize,
2920
+ normalize,
2921
+ cache
2922
+ };
2923
+ console.log(`\u{1F4C2} Codex home: ${codexHome}`);
2924
+ console.log(`\u{1F4C1} Project root: ${projectRoot}`);
2925
+ const stats = await reverieIndexSemantic(codexHome, options);
2926
+ console.log(
2927
+ `\u2705 Indexed ${stats.documentsEmbedded} conversation(s) across ${stats.batches} batch(es); cache warmed at ${projectRoot}`
2928
+ );
2929
+ }
2930
+ function resolveCodexHome(explicit) {
2931
+ if (explicit) {
2932
+ return import_node_path6.default.resolve(explicit);
2933
+ }
2934
+ if (process.env.CODEX_HOME) {
2935
+ return process.env.CODEX_HOME;
2936
+ }
2937
+ const home = import_node_os2.default.homedir() || process.cwd();
2938
+ return import_node_path6.default.join(home, ".codex");
2939
+ }
2940
+ function resolveProjectRoot(explicit) {
2941
+ if (explicit) {
2942
+ return import_node_path6.default.resolve(explicit);
2943
+ }
2944
+ return process.cwd();
2945
+ }
2946
+ function parseOptionalInt(value) {
2947
+ if (typeof value === "string" && value.trim().length > 0) {
2948
+ const parsed = Number(value);
2949
+ if (!Number.isNaN(parsed)) {
2950
+ return parsed;
2951
+ }
2952
+ }
2953
+ return void 0;
2954
+ }
2955
+ function defaultCacheDir() {
2956
+ if (process.env.CODEX_EMBED_CACHE) {
2957
+ return import_node_path6.default.resolve(process.env.CODEX_EMBED_CACHE);
2958
+ }
2959
+ return import_node_path6.default.join(import_node_os2.default.tmpdir(), "codex-embed-cache");
2960
+ }
2961
+
2962
+ // src/cli/runtime.ts
2963
+ async function buildCombinedConfig(params) {
2964
+ const { cwd, config } = params;
2965
+ const warnings = [...config.warnings];
2966
+ const combined = {
2967
+ runDefaults: {},
2968
+ tuiDefaults: {},
2969
+ tools: [],
2970
+ interceptors: [],
2971
+ approval: void 0,
2972
+ beforeStartHooks: [],
2973
+ onEventHooks: [],
2974
+ warnings,
2975
+ allowReservedInterceptors: false
2976
+ };
2977
+ const pluginContext = { cwd, configPath: config.configPath };
2978
+ if (config.config) {
2979
+ accumulateConfig({
2980
+ combined,
2981
+ config: config.config,
2982
+ source: config.configPath ?? "config",
2983
+ warnings
2984
+ });
2985
+ }
2986
+ for (const loaded of config.plugins) {
2987
+ const pluginConfig = await evaluatePlugin(loaded, pluginContext, warnings);
2988
+ if (pluginConfig) {
2989
+ accumulateConfig({
2990
+ combined,
2991
+ config: pluginConfig,
2992
+ source: loaded.spec,
2993
+ warnings
2994
+ });
2995
+ }
2996
+ }
2997
+ combined.warnings = warnings;
2998
+ return combined;
2999
+ }
3000
+ function applyNativeRegistrations(combined) {
3001
+ const binding = getNativeBinding();
3002
+ if (!binding) {
3003
+ throw new Error("Native binding is not available.");
3004
+ }
3005
+ binding.clearRegisteredTools();
3006
+ const seenTools = /* @__PURE__ */ new Set();
3007
+ for (const tool of combined.tools) {
3008
+ const { handler, ...info } = tool;
3009
+ const name = String(info.name);
3010
+ if (seenTools.has(name)) {
3011
+ combined.warnings.push(`Duplicate tool "${name}" ignored (first definition wins).`);
3012
+ continue;
3013
+ }
3014
+ seenTools.add(name);
3015
+ binding.registerTool(info, handler);
3016
+ }
3017
+ if (combined.approval && typeof binding.registerApprovalCallback === "function") {
3018
+ binding.registerApprovalCallback(combined.approval.handler);
3019
+ }
3020
+ const RESERVED = /* @__PURE__ */ new Set(["local_shell", "exec_command", "apply_patch", "web_search"]);
3021
+ const seenInterceptors = /* @__PURE__ */ new Set();
3022
+ for (const interceptor of combined.interceptors) {
3023
+ const name = interceptor.toolName;
3024
+ if (RESERVED.has(name) && !combined.allowReservedInterceptors) {
3025
+ combined.warnings.push(
3026
+ `Interceptor for "${name}" ignored: reserved for approval gating. Use approvals() hook instead.`
3027
+ );
3028
+ continue;
3029
+ }
3030
+ if (seenInterceptors.has(name)) {
3031
+ combined.warnings.push(
3032
+ `Multiple interceptors for "${name}" detected; only the first will be used.`
3033
+ );
3034
+ continue;
3035
+ }
3036
+ seenInterceptors.add(name);
3037
+ binding.registerToolInterceptor(interceptor.toolName, interceptor.handler);
3038
+ }
3039
+ }
3040
+ async function evaluatePlugin(loaded, context, warnings) {
3041
+ const { plugin, spec } = loaded;
3042
+ try {
3043
+ if (typeof plugin === "function") {
3044
+ const result = await plugin(context);
3045
+ return coerceConfig(result, spec, warnings);
3046
+ }
3047
+ if (plugin && typeof plugin === "object") {
3048
+ const candidate = plugin;
3049
+ if (typeof candidate.setup === "function") {
3050
+ await candidate.setup(context);
3051
+ }
3052
+ if (typeof candidate.config === "function") {
3053
+ return coerceConfig(await candidate.config(context), spec, warnings);
3054
+ }
3055
+ if (candidate.config) {
3056
+ return coerceConfig(candidate.config, spec, warnings);
3057
+ }
3058
+ return coerceConfig(candidate, spec, warnings);
3059
+ }
3060
+ return coerceConfig(plugin, spec, warnings);
3061
+ } catch (error) {
3062
+ warnings.push(`Plugin "${spec}" threw an error: ${error.message}`);
3063
+ return null;
3064
+ }
3065
+ }
3066
+ function coerceConfig(value, source, warnings) {
3067
+ if (!value) {
3068
+ return null;
3069
+ }
3070
+ if (typeof value === "object") {
3071
+ return value;
3072
+ }
3073
+ warnings.push(`Plugin "${source}" did not return a config object.`);
3074
+ return null;
3075
+ }
3076
+ function accumulateConfig(params) {
3077
+ const { combined, config, source, warnings } = params;
3078
+ if (config.defaults?.run) {
3079
+ combined.runDefaults = { ...combined.runDefaults, ...config.defaults.run };
3080
+ }
3081
+ if (config.defaults?.tui) {
3082
+ combined.tuiDefaults = { ...combined.tuiDefaults, ...config.defaults.tui };
3083
+ }
3084
+ if (Array.isArray(config.tools)) {
3085
+ for (const tool of config.tools) {
3086
+ if (!tool || typeof tool !== "object" || typeof tool.handler !== "function") {
3087
+ warnings.push(`Invalid tool definition supplied by "${source}".`);
3088
+ continue;
3089
+ }
3090
+ combined.tools.push(tool);
3091
+ }
3092
+ }
3093
+ if (Array.isArray(config.interceptors)) {
3094
+ for (const interceptor of config.interceptors) {
3095
+ if (!interceptor || typeof interceptor !== "object" || typeof interceptor.toolName !== "string" || typeof interceptor.handler !== "function") {
3096
+ warnings.push(`Invalid interceptor definition supplied by "${source}".`);
3097
+ continue;
3098
+ }
3099
+ combined.interceptors.push(interceptor);
3100
+ }
3101
+ }
3102
+ if (config.approvals) {
3103
+ if (typeof config.approvals !== "function") {
3104
+ warnings.push(`Approval callback from "${source}" must be a function.`);
3105
+ } else {
3106
+ if (combined.approval) {
3107
+ warnings.push(
3108
+ `Approval callback from "${source}" overrides handler from "${combined.approval.source}".`
3109
+ );
3110
+ }
3111
+ combined.approval = { source, handler: config.approvals };
3112
+ }
3113
+ }
3114
+ if (config.hooks) {
3115
+ addHooks(combined, config.hooks, source, warnings);
3116
+ }
3117
+ if (config.allowReservedInterceptors === true) {
3118
+ combined.allowReservedInterceptors = true;
3119
+ }
3120
+ }
3121
+ function addHooks(combined, hooks, source, warnings) {
3122
+ if (hooks.beforeStart) {
3123
+ const beforeStartCallbacks = Array.isArray(hooks.beforeStart) ? hooks.beforeStart : [hooks.beforeStart];
3124
+ for (const callback of beforeStartCallbacks) {
3125
+ if (typeof callback !== "function") {
3126
+ warnings.push(`beforeStart hook from "${source}" must be a function.`);
3127
+ continue;
3128
+ }
3129
+ combined.beforeStartHooks.push({ source, callback });
3130
+ }
3131
+ }
3132
+ if (hooks.onEvent) {
3133
+ const eventCallbacks = Array.isArray(hooks.onEvent) ? hooks.onEvent : [hooks.onEvent];
3134
+ for (const callback of eventCallbacks) {
3135
+ if (typeof callback !== "function") {
3136
+ warnings.push(`onEvent hook from "${source}" must be a function.`);
3137
+ continue;
3138
+ }
3139
+ combined.onEventHooks.push({ source, callback });
3140
+ }
3141
+ }
3142
+ }
3143
+
3144
+ // src/cli/index.ts
3145
+ var VERSION = package_default.version;
3146
+ var SANDBOX_CHOICES = ["read-only", "workspace-write", "danger-full-access"];
3147
+ var APPROVAL_CHOICES = ["never", "on-request", "on-failure", "untrusted"];
3148
+ var APPLY_PATCH_FLAG = "--codex-run-as-apply-patch";
3149
+ var CLI_ENTRYPOINT_ENV2 = "CODEX_NODE_CLI_ENTRYPOINT";
3150
+ try {
3151
+ const entrypoint = (0, import_node_url4.fileURLToPath)(importMetaUrl);
3152
+ import_node_process3.default.env[CLI_ENTRYPOINT_ENV2] = entrypoint;
3153
+ } catch {
3154
+ if (import_node_process3.default.argv[1]) {
3155
+ import_node_process3.default.env[CLI_ENTRYPOINT_ENV2] = import_node_process3.default.argv[1];
3156
+ }
3157
+ }
3158
+ var GLOBAL_OPTION_DEFS = {
3159
+ config: { type: "string" },
3160
+ "no-config": { type: "boolean" },
3161
+ plugin: { type: "string", multiple: true }
3162
+ };
3163
+ var RUN_OPTION_DEFS = {
3164
+ model: { type: "string" },
3165
+ oss: { type: "boolean" },
3166
+ sandbox: { type: "string" },
3167
+ approval: { type: "string" },
3168
+ schema: { type: "string" },
3169
+ "thread-id": { type: "string" },
3170
+ "base-url": { type: "string" },
3171
+ "api-key": { type: "string" },
3172
+ "linux-sandbox-path": { type: "string" },
3173
+ "full-auto": { type: "boolean" },
3174
+ "skip-git-repo-check": { type: "boolean" },
3175
+ cd: { type: "string" },
3176
+ image: { type: "string", multiple: true },
3177
+ "review-mode": { type: "boolean" },
3178
+ "review-hint": { type: "string" }
3179
+ };
3180
+ var TUI_OPTION_DEFS = {
3181
+ model: { type: "string" },
3182
+ oss: { type: "boolean" },
3183
+ sandbox: { type: "string" },
3184
+ approval: { type: "string" },
3185
+ resume: { type: "string" },
3186
+ "resume-last": { type: "boolean" },
3187
+ "resume-picker": { type: "boolean" },
3188
+ "full-auto": { type: "boolean" },
3189
+ "dangerously-bypass-approvals-and-sandbox": { type: "boolean" },
3190
+ cd: { type: "string" },
3191
+ "config-profile": { type: "string" },
3192
+ "config-overrides": { type: "string", multiple: true },
3193
+ "add-dir": { type: "string", multiple: true },
3194
+ image: { type: "string", multiple: true },
3195
+ "web-search": { type: "boolean" },
3196
+ "linux-sandbox-path": { type: "string" },
3197
+ "base-url": { type: "string" },
3198
+ "api-key": { type: "string" }
3199
+ };
3200
+ async function main() {
3201
+ const rawArgs = import_node_process3.default.argv.slice(2);
3202
+ if (maybeHandleApplyPatch(rawArgs)) {
3203
+ return;
3204
+ }
3205
+ if (hasFlag(rawArgs, "--version") || hasFlag(rawArgs, "-v")) {
3206
+ printVersion();
3207
+ return;
3208
+ }
3209
+ const generalHelpRequested = hasFlag(rawArgs, "--help") || hasFlag(rawArgs, "-h");
3210
+ if (generalHelpRequested && !hasExplicitCommand(rawArgs)) {
3211
+ printGeneralHelp();
3212
+ return;
3213
+ }
3214
+ const { command, args } = selectCommand(rawArgs);
3215
+ if (hasCommandHelpFlag(args)) {
3216
+ printCommandHelp(command);
3217
+ return;
3218
+ }
3219
+ if (command === "reverie-index") {
3220
+ await executeReverieCommand(args);
3221
+ return;
3222
+ }
3223
+ const options = command === "tui" ? parseTuiCommand(args) : parseRunCommand(args);
3224
+ validateOptionChoices(command, options);
3225
+ const context = await createContext(options);
3226
+ if (command === "tui") {
3227
+ await executeTuiCommand(options, context);
3228
+ } else {
3229
+ await executeRunCommand(options, context);
3230
+ }
3231
+ }
3232
+ function maybeHandleApplyPatch(args) {
3233
+ if (args.length === 0 || args[0] !== APPLY_PATCH_FLAG) {
3234
+ return false;
3235
+ }
3236
+ const patch = args[1];
3237
+ if (!patch) {
3238
+ console.error(`${APPLY_PATCH_FLAG} requires a patch argument.`);
3239
+ import_node_process3.default.exitCode = 1;
3240
+ return true;
3241
+ }
3242
+ try {
3243
+ runApplyPatch(patch);
3244
+ } catch (error) {
3245
+ const message = error instanceof Error ? error.message : String(error);
3246
+ console.error(`apply_patch failed: ${message}`);
3247
+ import_node_process3.default.exitCode = 1;
3248
+ }
3249
+ return true;
3250
+ }
3251
+ function selectCommand(argv) {
3252
+ if (argv.length > 0) {
3253
+ const [first, ...rest] = argv;
3254
+ if (first === "tui") {
3255
+ return { command: "tui", args: rest };
3256
+ }
3257
+ if (first === "run") {
3258
+ return { command: "run", args: rest };
3259
+ }
3260
+ if (first === "reverie") {
3261
+ if (rest.length === 0) {
3262
+ return { command: "reverie-index", args: [] };
3263
+ }
3264
+ return { command: "reverie-index", args: rest };
3265
+ }
3266
+ }
3267
+ if (argv.length === 0) {
3268
+ const isInteractive2 = import_node_process3.default.stdout.isTTY && import_node_process3.default.stdin.isTTY;
3269
+ return { command: isInteractive2 ? "tui" : "run", args: [] };
3270
+ }
3271
+ const isInteractive = import_node_process3.default.stdout.isTTY && import_node_process3.default.stdin.isTTY;
3272
+ return { command: isInteractive ? "tui" : "run", args: argv };
3273
+ }
3274
+ function hasExplicitCommand(argv) {
3275
+ if (argv.length === 0) {
3276
+ return false;
3277
+ }
3278
+ const first = argv[0];
3279
+ return first === "tui" || first === "run" || first === "reverie";
3280
+ }
3281
+ function parseRunCommand(args) {
3282
+ const { values, positionals } = (0, import_node_util2.parseArgs)({
3283
+ args,
3284
+ options: { ...GLOBAL_OPTION_DEFS, ...RUN_OPTION_DEFS },
3285
+ allowPositionals: true,
3286
+ strict: true
3287
+ });
3288
+ const options = camelCaseKeys(values);
3289
+ const runOptions = {
3290
+ ...options
3291
+ };
3292
+ if (!runOptions.prompt && positionals.length > 0) {
3293
+ runOptions.prompt = positionals[0];
3294
+ }
3295
+ return runOptions;
3296
+ }
3297
+ function parseTuiCommand(args) {
3298
+ const { values, positionals } = (0, import_node_util2.parseArgs)({
3299
+ args,
3300
+ options: { ...GLOBAL_OPTION_DEFS, ...TUI_OPTION_DEFS },
3301
+ allowPositionals: true,
3302
+ strict: true
3303
+ });
3304
+ const options = camelCaseKeys(values);
3305
+ const tuiOptions = {
3306
+ ...options
3307
+ };
3308
+ if (!tuiOptions.prompt && positionals.length > 0) {
3309
+ tuiOptions.prompt = positionals[0];
3310
+ }
3311
+ return tuiOptions;
3312
+ }
3313
+ async function createContext(options) {
3314
+ const cwd = import_node_process3.default.cwd();
3315
+ const configOptions = {
3316
+ cwd,
3317
+ explicitConfigPath: options.config,
3318
+ noConfig: options.noConfig,
3319
+ pluginPaths: normalizeStringArray(options.plugin)
3320
+ };
3321
+ const config = await loadCliConfig(configOptions);
3322
+ const combinedConfig = await buildCombinedConfig({ cwd, config });
3323
+ applyNativeRegistrations(combinedConfig);
3324
+ return { cwd, config, combinedConfig };
3325
+ }
3326
+ function normalizeStringArray(value) {
3327
+ if (Array.isArray(value)) {
3328
+ return value.map((item) => String(item));
3329
+ }
3330
+ if (typeof value === "string") {
3331
+ return [value];
3332
+ }
3333
+ return [];
3334
+ }
3335
+ function camelCaseKeys(record) {
3336
+ return Object.entries(record).reduce((acc, [key, value]) => {
3337
+ acc[toCamelCase(key)] = value;
3338
+ return acc;
3339
+ }, {});
3340
+ }
3341
+ function toCamelCase(value) {
3342
+ return value.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
3343
+ }
3344
+ function hasFlag(args, flag) {
3345
+ return args.includes(flag);
3346
+ }
3347
+ function hasCommandHelpFlag(args) {
3348
+ return hasFlag(args, "--help") || hasFlag(args, "-h");
3349
+ }
3350
+ function printVersion() {
3351
+ console.log(VERSION);
3352
+ }
3353
+ function printGeneralHelp() {
3354
+ console.log(`codex-native v${VERSION}
3355
+
3356
+ Usage:
3357
+ codex-native [options] [prompt]
3358
+ codex-native run [options] [prompt]
3359
+ codex-native tui [options] [prompt]
3360
+
3361
+ Default behavior:
3362
+ Running 'codex-native' without arguments launches the interactive TUI.
3363
+ Use 'codex-native run <prompt>' for non-interactive exec mode.
3364
+
3365
+ Commands:
3366
+ (default) Launch the interactive TUI (with optional initial prompt)
3367
+ run Run Codex in non-interactive exec mode
3368
+ tui Explicitly launch the interactive TUI
3369
+ reverie index Pre-compute reverie embeddings for the current repo
3370
+
3371
+ Global options:
3372
+ --config <path> Path to codex.config.js (or similar)
3373
+ --no-config Skip automatic config discovery
3374
+ --plugin <path> Additional plugin module (repeatable)
3375
+
3376
+ Run options:
3377
+ --model <slug> Model slug to use
3378
+ --oss Use the built-in OSS provider
3379
+ --sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
3380
+ --approval <policy> ${APPROVAL_CHOICES.join(" | ")}
3381
+ --schema <file> Path to final-output JSON schema
3382
+ --thread-id <id> Resume an existing thread
3383
+ --base-url <url> Override the Codex API base URL
3384
+ --api-key <key> API key for Codex requests
3385
+ --linux-sandbox-path Path to codex-linux-sandbox binary
3386
+ --full-auto Enable workspace-write auto approvals
3387
+ --skip-git-repo-check Skip git repository validation
3388
+ --cd <path> Working directory for the run
3389
+ --image <path> Attach an image (repeatable)
3390
+ --review-mode Enable review mode
3391
+ --review-hint <text> Hint text for review mode
3392
+
3393
+ TUI options:
3394
+ --model <slug> Model slug to use
3395
+ --oss Use the built-in OSS provider
3396
+ --sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
3397
+ --approval <policy> ${APPROVAL_CHOICES.join(" | ")}
3398
+ --resume <id> Resume a saved session by id
3399
+ --resume-last Resume the most recent saved session
3400
+ --resume-picker Show the resume picker on startup
3401
+ --full-auto Enable workspace-write auto approvals
3402
+ --dangerously-bypass-approvals-and-sandbox
3403
+ Disable approvals and sandboxing (unsafe)
3404
+ --cd <path> Working directory for the session
3405
+ --config-profile <name> Config profile to activate
3406
+ --config-overrides <kv> Config overrides (key=value, repeatable)
3407
+ --add-dir <path> Additional writable directory (repeatable)
3408
+ --image <path> Attach an image (repeatable)
3409
+ --web-search Enable web search tool
3410
+ --linux-sandbox-path Path to codex-linux-sandbox binary
3411
+ --base-url <url> Override the Codex API base URL
3412
+ --api-key <key> API key for Codex requests
3413
+ `);
3414
+ }
3415
+ function printCommandHelp(command) {
3416
+ if (command === "reverie-index") {
3417
+ console.log(`codex-native reverie index [options]
3418
+
3419
+ Options:
3420
+ --codex-home <path> Override CODEX_HOME (defaults to ~/.codex)
3421
+ --project-root <path> Project root for scoping + embedding cache (default: cwd)
3422
+ --limit <n> Maximum conversations to index (default: 10)
3423
+ --max-candidates <n> Scan window before filtering (default: 80)
3424
+ --batch-size <n> Batch size forwarded to FastEmbed
3425
+ --normalize Force vector normalization (default: embed config)
3426
+ --no-normalize Disable normalization
3427
+ --cache / --no-cache Override embedding cache behavior
3428
+ --embed-model <name> FastEmbed model (default: BAAI/bge-large-en-v1.5)
3429
+ --embed-cache-dir <dir> Cache directory (defaults to $CODEX_EMBED_CACHE or system tmp)
3430
+ --embed-max-length <n> Override FastEmbed max token length
3431
+ --no-progress Hide FastEmbed download progress
3432
+ --skip-embed-init Assume fastEmbedInit was already called in this process
3433
+ `);
3434
+ return;
3435
+ }
3436
+ if (command === "tui") {
3437
+ console.log(`codex-native tui [options] [prompt]
3438
+
3439
+ Options:
3440
+ --model <slug> Model slug to use
3441
+ --oss Use the built-in OSS provider
3442
+ --sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
3443
+ --approval <policy> ${APPROVAL_CHOICES.join(" | ")}
3444
+ --resume <id> Resume a saved session by id
3445
+ --resume-last Resume the most recent saved session
3446
+ --resume-picker Show the resume picker on startup
3447
+ --full-auto Enable workspace-write auto approvals
3448
+ --dangerously-bypass-approvals-and-sandbox
3449
+ Disable approvals and sandboxing (unsafe)
3450
+ --cd <path> Working directory for the session
3451
+ --config-profile <name> Config profile to activate
3452
+ --config-overrides <kv> Config overrides (key=value, repeatable)
3453
+ --add-dir <path> Additional writable directory (repeatable)
3454
+ --image <path> Attach an image (repeatable)
3455
+ --web-search Enable web search tool
3456
+ --linux-sandbox-path Path to codex-linux-sandbox binary
3457
+ --base-url <url> Override the Codex API base URL
3458
+ --api-key <key> API key for Codex requests
3459
+ `);
3460
+ } else {
3461
+ console.log(`codex-native run [options] [prompt]
3462
+
3463
+ Options:
3464
+ --model <slug> Model slug to use
3465
+ --oss Use the built-in OSS provider
3466
+ --sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
3467
+ --approval <policy> ${APPROVAL_CHOICES.join(" | ")}
3468
+ --schema <file> Path to final-output JSON schema
3469
+ --thread-id <id> Resume an existing thread
3470
+ --base-url <url> Override the Codex API base URL
3471
+ --api-key <key> API key for Codex requests
3472
+ --linux-sandbox-path Path to codex-linux-sandbox binary
3473
+ --full-auto Enable workspace-write auto approvals
3474
+ --skip-git-repo-check Skip git repository validation
3475
+ --cd <path> Working directory for the run
3476
+ --image <path> Attach an image (repeatable)
3477
+ --review-mode Enable review mode
3478
+ --review-hint <text> Hint text for review mode
3479
+ `);
3480
+ }
3481
+ }
3482
+ function validateOptionChoices(command, options) {
3483
+ const sandbox = options.sandbox;
3484
+ if (sandbox && !SANDBOX_CHOICES.includes(sandbox)) {
3485
+ throw new Error(
3486
+ `Invalid sandbox mode "${sandbox}". Valid modes: ${SANDBOX_CHOICES.join(", ")}.`
3487
+ );
3488
+ }
3489
+ const approval = options.approval;
3490
+ if (approval && !APPROVAL_CHOICES.includes(approval)) {
3491
+ throw new Error(
3492
+ `Invalid approval policy "${approval}". Valid policies: ${APPROVAL_CHOICES.join(", ")}.`
3493
+ );
3494
+ }
3495
+ }
3496
+ function logError(error) {
3497
+ if (error instanceof Error) {
3498
+ console.error(error.message);
3499
+ if (import_node_process3.default.env.CODEX_NATIVE_DEBUG) {
3500
+ console.error(error.stack);
3501
+ }
3502
+ } else {
3503
+ console.error(String(error));
3504
+ }
3505
+ }
3506
+ main().catch((error) => {
3507
+ logError(error);
3508
+ import_node_process3.default.exitCode = 1;
3509
+ });
3510
+ //# sourceMappingURL=cli.cjs.map