@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/README.md +482 -5
- package/codex_native.darwin-arm64.node +0 -0
- package/dist/chunk-ZTUGAPWF.mjs +1996 -0
- package/dist/chunk-ZTUGAPWF.mjs.map +1 -0
- package/dist/cli.cjs +3510 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +1661 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.cjs +4722 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +1944 -272
- package/dist/index.d.ts +2234 -0
- package/dist/index.mjs +2501 -454
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +487 -0
- package/index.js +626 -0
- package/npm/darwin-arm64/README.md +2 -2
- package/npm/darwin-arm64/package.json +3 -3
- package/npm/darwin-x64/README.md +2 -2
- package/npm/darwin-x64/package.json +3 -3
- package/npm/linux-arm64-gnu/README.md +2 -2
- package/npm/linux-arm64-gnu/package.json +3 -3
- package/npm/linux-arm64-musl/README.md +2 -2
- package/npm/linux-arm64-musl/package.json +3 -3
- package/npm/linux-x64-gnu/README.md +2 -2
- package/npm/linux-x64-gnu/package.json +3 -3
- package/npm/linux-x64-musl/README.md +2 -2
- package/npm/linux-x64-musl/package.json +3 -3
- package/npm/win32-arm64-msvc/README.md +2 -2
- package/npm/win32-arm64-msvc/package.json +3 -3
- package/npm/win32-x64-msvc/README.md +2 -2
- package/npm/win32-x64-msvc/package.json +3 -3
- package/package.json +61 -22
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,1661 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
Codex,
|
|
4
|
+
attachLspDiagnostics,
|
|
5
|
+
convertRustEventToThreadEvent,
|
|
6
|
+
fastEmbedInit,
|
|
7
|
+
getNativeBinding,
|
|
8
|
+
reverieIndexSemantic,
|
|
9
|
+
runApplyPatch
|
|
10
|
+
} from "./chunk-ZTUGAPWF.mjs";
|
|
11
|
+
|
|
12
|
+
// src/cli/index.ts
|
|
13
|
+
import process4 from "process";
|
|
14
|
+
import { parseArgs as parseArgs2 } from "util";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
|
|
17
|
+
// package.json
|
|
18
|
+
var package_default = {
|
|
19
|
+
name: "@codex-native/sdk",
|
|
20
|
+
version: "0.0.3",
|
|
21
|
+
description: "Native NAPI-based Codex SDK - complete standalone implementation.",
|
|
22
|
+
main: "dist/index.cjs",
|
|
23
|
+
module: "dist/index.mjs",
|
|
24
|
+
types: "dist/index.d.ts",
|
|
25
|
+
exports: {
|
|
26
|
+
".": {
|
|
27
|
+
types: "./dist/index.d.ts",
|
|
28
|
+
import: "./dist/index.mjs",
|
|
29
|
+
require: "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
bin: {
|
|
33
|
+
"codex-native": "dist/cli.cjs"
|
|
34
|
+
},
|
|
35
|
+
files: [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md",
|
|
38
|
+
"npm",
|
|
39
|
+
"*.node",
|
|
40
|
+
"index.js",
|
|
41
|
+
"index.d.ts"
|
|
42
|
+
],
|
|
43
|
+
dependencies: {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
45
|
+
"@opencode-ai/sdk": "^1.0.68",
|
|
46
|
+
"@toon-format/toon": "^1.0.0",
|
|
47
|
+
pyright: "^1.1.386",
|
|
48
|
+
"typescript-language-server": "^4.3.3",
|
|
49
|
+
"vscode-jsonrpc": "^8.2.1",
|
|
50
|
+
"vscode-languageserver-types": "^3.17.5"
|
|
51
|
+
},
|
|
52
|
+
scripts: {
|
|
53
|
+
clean: "rm -rf dist *.node npm target/release/*.node && cargo clean --release",
|
|
54
|
+
"clean:all": "rm -rf dist *.node npm target node_modules && cargo clean",
|
|
55
|
+
build: "npm run build:rust && npm run build:napi && npm run build:ts && npm run verify:build",
|
|
56
|
+
"build:ci": "npm run build:napi && npm run build:ts && npm run verify:build",
|
|
57
|
+
"build:rust": "cargo fmt && cargo clippy --release --features napi-bindings -- -D warnings",
|
|
58
|
+
"build:napi": "napi build --platform --release --features napi-bindings",
|
|
59
|
+
"build:napi:debug": "napi build --platform --features napi-bindings",
|
|
60
|
+
"build:ts": "tsup",
|
|
61
|
+
"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')"`,
|
|
62
|
+
"verify:pack": "node ./scripts/verify-pack.mjs",
|
|
63
|
+
"create-npm-dirs": "napi create-npm-dirs",
|
|
64
|
+
artifacts: "napi artifacts",
|
|
65
|
+
prepublishOnly: "napi prepublish -t npm",
|
|
66
|
+
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');}"`,
|
|
67
|
+
test: "node ./scripts/run-jest.mjs --runInBand",
|
|
68
|
+
"test:watch": "node ./scripts/run-jest.mjs --watch",
|
|
69
|
+
coverage: "node ./scripts/run-jest.mjs --coverage",
|
|
70
|
+
typecheck: "tsc --noEmit",
|
|
71
|
+
lint: "cargo fmt --check && cargo clippy --release --features napi-bindings -- -D warnings && npm run typecheck",
|
|
72
|
+
version: "napi version",
|
|
73
|
+
"release:patch": "npm version patch && npm run release",
|
|
74
|
+
"release:minor": "npm version minor && npm run release",
|
|
75
|
+
"release:major": "npm version major && npm run release",
|
|
76
|
+
"publish:platforms": "npm run create-npm-dirs && node scripts/publish-platform-packages.mjs",
|
|
77
|
+
release: "npm run build && npm run test && npm run verify:pack && npm run publish:platforms && node scripts/publish-sdk.mjs",
|
|
78
|
+
"release:dry": "npm run build && npm run test && npm run verify:pack && npm publish --dry-run"
|
|
79
|
+
},
|
|
80
|
+
napi: {
|
|
81
|
+
binaryName: "codex_native",
|
|
82
|
+
targets: [
|
|
83
|
+
"aarch64-apple-darwin",
|
|
84
|
+
"x86_64-apple-darwin",
|
|
85
|
+
"aarch64-unknown-linux-gnu",
|
|
86
|
+
"x86_64-unknown-linux-gnu",
|
|
87
|
+
"aarch64-unknown-linux-musl",
|
|
88
|
+
"x86_64-unknown-linux-musl",
|
|
89
|
+
"aarch64-pc-windows-msvc",
|
|
90
|
+
"x86_64-pc-windows-msvc"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
optionalDependencies: {
|
|
94
|
+
"@codex-native/sdk-darwin-arm64": "0.0.3",
|
|
95
|
+
"@codex-native/sdk-darwin-x64": "0.0.3",
|
|
96
|
+
"@codex-native/sdk-linux-arm64-gnu": "0.0.3",
|
|
97
|
+
"@codex-native/sdk-linux-x64-gnu": "0.0.3",
|
|
98
|
+
"@codex-native/sdk-linux-arm64-musl": "0.0.3",
|
|
99
|
+
"@codex-native/sdk-linux-x64-musl": "0.0.3",
|
|
100
|
+
"@codex-native/sdk-win32-arm64-msvc": "0.0.3",
|
|
101
|
+
"@codex-native/sdk-win32-x64-msvc": "0.0.3"
|
|
102
|
+
},
|
|
103
|
+
engines: {
|
|
104
|
+
node: ">=18"
|
|
105
|
+
},
|
|
106
|
+
publishConfig: {
|
|
107
|
+
access: "public"
|
|
108
|
+
},
|
|
109
|
+
devDependencies: {
|
|
110
|
+
"@babel/core": "7.25.2",
|
|
111
|
+
"@babel/preset-env": "7.25.4",
|
|
112
|
+
"@babel/preset-typescript": "7.28.5",
|
|
113
|
+
"@jest/globals": "^29.7.0",
|
|
114
|
+
"@napi-rs/cli": "^3.4.1",
|
|
115
|
+
"@openai/agents": "^0.3.0",
|
|
116
|
+
"@types/jest": "29.5.11",
|
|
117
|
+
"@types/node": "^22.0.0",
|
|
118
|
+
"babel-jest": "29.7.0",
|
|
119
|
+
jest: "^29.7.0",
|
|
120
|
+
"ts-jest": "^29.1.1",
|
|
121
|
+
tsup: "^8.5.0",
|
|
122
|
+
tsx: "^4.20.6",
|
|
123
|
+
typescript: "^5.7.2",
|
|
124
|
+
zod: "^3.25.76"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/cli/config.ts
|
|
129
|
+
import fs from "fs";
|
|
130
|
+
import fsPromises from "fs/promises";
|
|
131
|
+
import path from "path";
|
|
132
|
+
import { createRequire } from "module";
|
|
133
|
+
import { pathToFileURL } from "url";
|
|
134
|
+
var requireFromThisModule = createRequire(import.meta.url);
|
|
135
|
+
var CONFIG_CANDIDATES = [
|
|
136
|
+
"codex.config.js",
|
|
137
|
+
"codex.config.cjs",
|
|
138
|
+
"codex.config.mjs",
|
|
139
|
+
"codex.config.ts",
|
|
140
|
+
"codex.js",
|
|
141
|
+
".codexrc.js",
|
|
142
|
+
".codexrc.cjs",
|
|
143
|
+
".codexrc.mjs",
|
|
144
|
+
".codexrc.json"
|
|
145
|
+
];
|
|
146
|
+
async function loadCliConfig(options) {
|
|
147
|
+
const warnings = [];
|
|
148
|
+
const discovery = await resolveConfigPath(options);
|
|
149
|
+
const configPath = discovery?.path ?? null;
|
|
150
|
+
let config = null;
|
|
151
|
+
if (discovery) {
|
|
152
|
+
const loadResult = await loadConfig(discovery, warnings);
|
|
153
|
+
config = loadResult ?? null;
|
|
154
|
+
if (config && typeof config !== "object") {
|
|
155
|
+
warnings.push(
|
|
156
|
+
`Config at ${discovery.path} must export an object. Received ${typeof config}.`
|
|
157
|
+
);
|
|
158
|
+
config = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const plugins = await resolvePlugins({
|
|
162
|
+
config,
|
|
163
|
+
configPath,
|
|
164
|
+
cliPluginPaths: options.pluginPaths ?? [],
|
|
165
|
+
cwd: options.cwd,
|
|
166
|
+
warnings
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
configPath,
|
|
170
|
+
config,
|
|
171
|
+
plugins,
|
|
172
|
+
warnings
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function resolveConfigPath(options) {
|
|
176
|
+
if (options.explicitConfigPath) {
|
|
177
|
+
const explicitPath = path.resolve(options.cwd, options.explicitConfigPath);
|
|
178
|
+
if (!fs.existsSync(explicitPath)) {
|
|
179
|
+
throw new Error(`Config file not found at ${explicitPath}`);
|
|
180
|
+
}
|
|
181
|
+
return classifyPath(explicitPath);
|
|
182
|
+
}
|
|
183
|
+
if (options.noConfig) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
let currentDir = path.resolve(options.cwd);
|
|
187
|
+
const visited = /* @__PURE__ */ new Set();
|
|
188
|
+
while (!visited.has(currentDir)) {
|
|
189
|
+
visited.add(currentDir);
|
|
190
|
+
for (const candidate of CONFIG_CANDIDATES) {
|
|
191
|
+
const candidatePath = path.join(currentDir, candidate);
|
|
192
|
+
if (fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile()) {
|
|
193
|
+
return classifyPath(candidatePath);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const packageJsonPath = path.join(currentDir, "package.json");
|
|
197
|
+
if (fs.existsSync(packageJsonPath) && fs.statSync(packageJsonPath).isFile()) {
|
|
198
|
+
const manifest = await readJson(packageJsonPath);
|
|
199
|
+
if (manifest && manifest.codexNative != null) {
|
|
200
|
+
return { path: packageJsonPath, type: "package-json", field: "codexNative" };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const parentDir = path.dirname(currentDir);
|
|
204
|
+
if (parentDir === currentDir) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
currentDir = parentDir;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
async function loadConfig(discovery, warnings) {
|
|
212
|
+
if (discovery.type === "package-json") {
|
|
213
|
+
const manifest = await readJson(discovery.path);
|
|
214
|
+
if (!manifest) {
|
|
215
|
+
warnings.push(`Failed to parse ${discovery.path}; ignoring config.`);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
const raw = manifest[discovery.field];
|
|
219
|
+
if (typeof raw === "string") {
|
|
220
|
+
const baseDir = path.dirname(discovery.path);
|
|
221
|
+
const nestedPath = path.resolve(baseDir, raw);
|
|
222
|
+
if (!fs.existsSync(nestedPath)) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Config path "${raw}" referenced by ${discovery.field} in ${discovery.path} was not found.`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return loadConfig({ path: nestedPath, type: "file" }, warnings);
|
|
228
|
+
}
|
|
229
|
+
if (typeof raw === "object" && raw !== null) {
|
|
230
|
+
return raw;
|
|
231
|
+
}
|
|
232
|
+
warnings.push(
|
|
233
|
+
`The ${discovery.field} field in ${discovery.path} must be an object or path string.`
|
|
234
|
+
);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const ext = path.extname(discovery.path).toLowerCase();
|
|
238
|
+
if (ext === ".json") {
|
|
239
|
+
const json = await readJson(discovery.path);
|
|
240
|
+
if (json === null) {
|
|
241
|
+
warnings.push(`Failed to parse JSON config at ${discovery.path}`);
|
|
242
|
+
}
|
|
243
|
+
return json;
|
|
244
|
+
}
|
|
245
|
+
if (ext === ".js" || ext === ".cjs") {
|
|
246
|
+
return extractModuleDefault(await loadCommonJsModule(discovery.path));
|
|
247
|
+
}
|
|
248
|
+
if (ext === ".mjs") {
|
|
249
|
+
return extractModuleDefault(await importModule(discovery.path));
|
|
250
|
+
}
|
|
251
|
+
if (ext === ".ts") {
|
|
252
|
+
return extractModuleDefault(await loadTypeScriptModule(discovery.path, warnings));
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`Unsupported config extension "${ext}" at ${discovery.path}`);
|
|
255
|
+
}
|
|
256
|
+
async function resolvePlugins(params) {
|
|
257
|
+
const plugins = [];
|
|
258
|
+
const { config, configPath, cliPluginPaths, cwd, warnings } = params;
|
|
259
|
+
const configDir = configPath ? path.dirname(configPath) : cwd;
|
|
260
|
+
const rawConfigPlugins = config?.plugins;
|
|
261
|
+
const configPlugins = Array.isArray(rawConfigPlugins) ? rawConfigPlugins : [];
|
|
262
|
+
for (const spec of configPlugins) {
|
|
263
|
+
if (typeof spec === "string") {
|
|
264
|
+
const loaded = await loadPlugin(spec, configDir, "config", warnings);
|
|
265
|
+
if (loaded) {
|
|
266
|
+
plugins.push(loaded);
|
|
267
|
+
}
|
|
268
|
+
} else if (spec != null) {
|
|
269
|
+
plugins.push({
|
|
270
|
+
source: "config",
|
|
271
|
+
spec: "<inline>",
|
|
272
|
+
plugin: spec
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const spec of cliPluginPaths) {
|
|
277
|
+
const loaded = await loadPlugin(spec, cwd, "cli", warnings);
|
|
278
|
+
if (loaded) {
|
|
279
|
+
plugins.push(loaded);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return plugins;
|
|
283
|
+
}
|
|
284
|
+
async function loadPlugin(spec, baseDir, source, warnings) {
|
|
285
|
+
try {
|
|
286
|
+
const resolved = resolveModule(spec, baseDir);
|
|
287
|
+
const moduleExports = await loadModuleForPath(resolved);
|
|
288
|
+
return {
|
|
289
|
+
source,
|
|
290
|
+
spec,
|
|
291
|
+
resolvedPath: resolved,
|
|
292
|
+
plugin: extractModuleDefault(moduleExports)
|
|
293
|
+
};
|
|
294
|
+
} catch (err) {
|
|
295
|
+
warnings.push(`Failed to load plugin "${spec}": ${err.message}`);
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function loadModuleForPath(modulePath) {
|
|
300
|
+
const ext = path.extname(modulePath).toLowerCase();
|
|
301
|
+
if (ext === ".cjs") {
|
|
302
|
+
return loadCommonJsModule(modulePath);
|
|
303
|
+
}
|
|
304
|
+
if (ext === ".mjs") {
|
|
305
|
+
return importModule(modulePath);
|
|
306
|
+
}
|
|
307
|
+
if (ext === ".ts") {
|
|
308
|
+
return loadTypeScriptModule(modulePath);
|
|
309
|
+
}
|
|
310
|
+
if (ext === ".json") {
|
|
311
|
+
return readJson(modulePath);
|
|
312
|
+
}
|
|
313
|
+
if (ext === ".js") {
|
|
314
|
+
try {
|
|
315
|
+
return loadCommonJsModule(modulePath);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (err instanceof Error && err.message.includes("ERR_REQUIRE_ESM")) {
|
|
318
|
+
return importModule(modulePath);
|
|
319
|
+
}
|
|
320
|
+
throw err;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return loadCommonJsModule(modulePath);
|
|
324
|
+
}
|
|
325
|
+
async function loadCommonJsModule(modulePath) {
|
|
326
|
+
return requireFromThisModule(modulePath);
|
|
327
|
+
}
|
|
328
|
+
async function importModule(modulePath) {
|
|
329
|
+
const href = pathToFileURL(modulePath).href;
|
|
330
|
+
return import(href);
|
|
331
|
+
}
|
|
332
|
+
async function loadTypeScriptModule(modulePath, warnings) {
|
|
333
|
+
try {
|
|
334
|
+
const { register } = requireFromThisModule("tsx/cjs/api");
|
|
335
|
+
const unregister = register({ transpileOnly: true });
|
|
336
|
+
try {
|
|
337
|
+
return requireFromThisModule(modulePath);
|
|
338
|
+
} finally {
|
|
339
|
+
await maybeCall(unregister);
|
|
340
|
+
}
|
|
341
|
+
} catch (cjsError) {
|
|
342
|
+
try {
|
|
343
|
+
const tsxEsmSpecifier = "tsx/esm/api";
|
|
344
|
+
const apiModule = await import(tsxEsmSpecifier);
|
|
345
|
+
const unregister = typeof apiModule.register === "function" ? apiModule.register({ transpileOnly: true }) : apiModule.default({ transpileOnly: true });
|
|
346
|
+
try {
|
|
347
|
+
return importModule(modulePath);
|
|
348
|
+
} finally {
|
|
349
|
+
await maybeCall(unregister);
|
|
350
|
+
}
|
|
351
|
+
} catch (esmError) {
|
|
352
|
+
const message = [
|
|
353
|
+
`Failed to load TypeScript module ${modulePath}.`,
|
|
354
|
+
'Install the "tsx" package or convert the config to JavaScript.'
|
|
355
|
+
].join(" ");
|
|
356
|
+
if (warnings) {
|
|
357
|
+
warnings.push(message);
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
throw new Error(message);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function readJson(filePath) {
|
|
365
|
+
try {
|
|
366
|
+
const raw = await fsPromises.readFile(filePath, "utf8");
|
|
367
|
+
return JSON.parse(raw);
|
|
368
|
+
} catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function extractModuleDefault(module) {
|
|
373
|
+
if (module && typeof module === "object" && "default" in module) {
|
|
374
|
+
const value = module.default;
|
|
375
|
+
if (value !== void 0) {
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return module;
|
|
380
|
+
}
|
|
381
|
+
function resolveModule(specifier, baseDir) {
|
|
382
|
+
if (path.isAbsolute(specifier)) {
|
|
383
|
+
return specifier;
|
|
384
|
+
}
|
|
385
|
+
return requireFromThisModule.resolve(specifier, { paths: [baseDir] });
|
|
386
|
+
}
|
|
387
|
+
function classifyPath(filePath) {
|
|
388
|
+
if (path.basename(filePath) === "package.json") {
|
|
389
|
+
return { path: filePath, type: "package-json", field: "codexNative" };
|
|
390
|
+
}
|
|
391
|
+
return { path: filePath, type: "file" };
|
|
392
|
+
}
|
|
393
|
+
async function maybeCall(candidate) {
|
|
394
|
+
if (typeof candidate === "function") {
|
|
395
|
+
await Promise.resolve(candidate());
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/cli/run.ts
|
|
400
|
+
import fs2 from "fs";
|
|
401
|
+
import fsPromises2 from "fs/promises";
|
|
402
|
+
import path3 from "path";
|
|
403
|
+
import process2 from "process";
|
|
404
|
+
|
|
405
|
+
// src/cli/optionParsers.ts
|
|
406
|
+
var SANDBOX_MODE_VALUES = ["read-only", "workspace-write", "danger-full-access"];
|
|
407
|
+
var APPROVAL_MODE_VALUES = ["never", "on-request", "on-failure", "untrusted"];
|
|
408
|
+
function isSandboxMode(value) {
|
|
409
|
+
return SANDBOX_MODE_VALUES.includes(value);
|
|
410
|
+
}
|
|
411
|
+
function isApprovalMode(value) {
|
|
412
|
+
return APPROVAL_MODE_VALUES.includes(value);
|
|
413
|
+
}
|
|
414
|
+
function parseSandboxModeFlag(value, origin) {
|
|
415
|
+
if (value === void 0) {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
if (isSandboxMode(value)) {
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
throw new Error(
|
|
422
|
+
`Invalid sandbox mode "${value}" from ${origin}. Valid values: ${SANDBOX_MODE_VALUES.join(
|
|
423
|
+
", "
|
|
424
|
+
)}.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
function parseApprovalModeFlag(value, origin) {
|
|
428
|
+
if (value === void 0) {
|
|
429
|
+
return void 0;
|
|
430
|
+
}
|
|
431
|
+
if (isApprovalMode(value)) {
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
throw new Error(
|
|
435
|
+
`Invalid approval mode "${value}" from ${origin}. Valid values: ${APPROVAL_MODE_VALUES.join(
|
|
436
|
+
", "
|
|
437
|
+
)}.`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/cli/hooks.ts
|
|
442
|
+
function emitWarnings(warnings, fromIndex = 0) {
|
|
443
|
+
for (let i = fromIndex; i < warnings.length; i += 1) {
|
|
444
|
+
const message = warnings[i];
|
|
445
|
+
process.stderr.write(`[codex-native] Warning: ${message}
|
|
446
|
+
`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async function runBeforeStartHooks(hooks, context, warnings) {
|
|
450
|
+
for (const hook of hooks) {
|
|
451
|
+
try {
|
|
452
|
+
await hook.callback(context);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
warnings.push(
|
|
455
|
+
`beforeStart hook "${hook.source}" threw: ${error.message ?? String(error)}`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async function runEventHooks(hooks, event, context, warnings) {
|
|
461
|
+
for (const hook of hooks) {
|
|
462
|
+
try {
|
|
463
|
+
await hook.callback(event, context);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
warnings.push(`onEvent hook "${hook.source}" threw: ${error.message ?? String(error)}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/cli/elevatedDefaults.ts
|
|
471
|
+
import path2 from "path";
|
|
472
|
+
var FULL_ACCESS_SANDBOX = "workspace-write";
|
|
473
|
+
var FULL_ACCESS_APPROVAL = "never";
|
|
474
|
+
function applyElevatedRunDefaults(request, cwd) {
|
|
475
|
+
const workingDirectory = resolveWorkingDirectory(request.workingDirectory, cwd);
|
|
476
|
+
request.workingDirectory = workingDirectory;
|
|
477
|
+
ensureSandboxModes(request);
|
|
478
|
+
request.workspaceWriteOptions = ensureWorkspaceWriteOptions(
|
|
479
|
+
request.workspaceWriteOptions,
|
|
480
|
+
workingDirectory
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
function applyElevatedTuiDefaults(params) {
|
|
484
|
+
const { request, thread, cwd } = params;
|
|
485
|
+
const workingDirectory = resolveWorkingDirectory(
|
|
486
|
+
request.workingDirectory ?? thread.workingDirectory,
|
|
487
|
+
cwd
|
|
488
|
+
);
|
|
489
|
+
request.workingDirectory = workingDirectory;
|
|
490
|
+
thread.workingDirectory = workingDirectory;
|
|
491
|
+
ensureSandboxModes(request);
|
|
492
|
+
thread.sandboxMode = request.sandboxMode ?? thread.sandboxMode ?? FULL_ACCESS_SANDBOX;
|
|
493
|
+
thread.approvalMode = request.approvalMode ?? thread.approvalMode ?? FULL_ACCESS_APPROVAL;
|
|
494
|
+
thread.workspaceWriteOptions = ensureWorkspaceWriteOptions(
|
|
495
|
+
thread.workspaceWriteOptions,
|
|
496
|
+
workingDirectory
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
function ensureSandboxModes(target) {
|
|
500
|
+
if (!target.sandboxMode) {
|
|
501
|
+
target.sandboxMode = FULL_ACCESS_SANDBOX;
|
|
502
|
+
}
|
|
503
|
+
if (!target.approvalMode) {
|
|
504
|
+
target.approvalMode = FULL_ACCESS_APPROVAL;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function ensureWorkspaceWriteOptions(options, workingDirectory) {
|
|
508
|
+
const resolved = path2.resolve(workingDirectory);
|
|
509
|
+
const writableRoots = new Set(options?.writableRoots ?? []);
|
|
510
|
+
writableRoots.add(resolved);
|
|
511
|
+
return {
|
|
512
|
+
...options,
|
|
513
|
+
networkAccess: options?.networkAccess ?? true,
|
|
514
|
+
writableRoots: Array.from(writableRoots)
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function resolveWorkingDirectory(candidate, cwd) {
|
|
518
|
+
if (!candidate || candidate.trim().length === 0) {
|
|
519
|
+
return path2.resolve(cwd);
|
|
520
|
+
}
|
|
521
|
+
return path2.isAbsolute(candidate) ? candidate : path2.resolve(cwd, candidate);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/cli/lspBridge.ts
|
|
525
|
+
var RunCommandThreadRelay = class {
|
|
526
|
+
constructor(binding, initialThreadId) {
|
|
527
|
+
this.binding = binding;
|
|
528
|
+
this.threadId = initialThreadId ?? null;
|
|
529
|
+
}
|
|
530
|
+
listeners = /* @__PURE__ */ new Set();
|
|
531
|
+
threadId;
|
|
532
|
+
onEvent(listener) {
|
|
533
|
+
this.listeners.add(listener);
|
|
534
|
+
return () => {
|
|
535
|
+
this.listeners.delete(listener);
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
async sendBackgroundEvent(message) {
|
|
539
|
+
const trimmed = typeof message === "string" ? message.trim() : "";
|
|
540
|
+
if (!trimmed) {
|
|
541
|
+
throw new Error("Background event message must be a non-empty string");
|
|
542
|
+
}
|
|
543
|
+
if (!this.threadId) {
|
|
544
|
+
throw new Error("Cannot emit a background event before the thread has started");
|
|
545
|
+
}
|
|
546
|
+
if (typeof this.binding.emitBackgroundEvent !== "function") {
|
|
547
|
+
throw new Error("emitBackgroundEvent is not available in this build");
|
|
548
|
+
}
|
|
549
|
+
await this.binding.emitBackgroundEvent({ threadId: this.threadId, message: trimmed });
|
|
550
|
+
}
|
|
551
|
+
handleEvent(event) {
|
|
552
|
+
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
553
|
+
this.threadId = event.thread_id;
|
|
554
|
+
}
|
|
555
|
+
for (const listener of this.listeners) {
|
|
556
|
+
try {
|
|
557
|
+
listener(event);
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.warn("[codex-native] LSP listener failed", error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
setThreadId(id) {
|
|
564
|
+
if (id) {
|
|
565
|
+
this.threadId = id;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
function createRunCommandLspBridge(params) {
|
|
570
|
+
try {
|
|
571
|
+
const relay = new RunCommandThreadRelay(params.binding, params.initialThreadId);
|
|
572
|
+
const detach = attachLspDiagnostics(relay, {
|
|
573
|
+
workingDirectory: params.workingDirectory,
|
|
574
|
+
waitForDiagnostics: true
|
|
575
|
+
});
|
|
576
|
+
relay.setThreadId(params.initialThreadId);
|
|
577
|
+
return {
|
|
578
|
+
handleEvent: (event) => relay.handleEvent(event),
|
|
579
|
+
dispose: () => detach()
|
|
580
|
+
};
|
|
581
|
+
} catch (error) {
|
|
582
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
583
|
+
console.warn(`[codex-native] Failed to initialize LSP diagnostics bridge: ${message}`);
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/cli/run.ts
|
|
589
|
+
async function executeRunCommand(argv, context) {
|
|
590
|
+
const { combinedConfig } = context;
|
|
591
|
+
emitWarnings(combinedConfig.warnings);
|
|
592
|
+
const warningCount = combinedConfig.warnings.length;
|
|
593
|
+
const prompt = await resolvePrompt(argv, combinedConfig.runDefaults.prompt, context.cwd);
|
|
594
|
+
const request = await buildRunRequest({
|
|
595
|
+
prompt,
|
|
596
|
+
argv,
|
|
597
|
+
combinedDefaults: combinedConfig.runDefaults,
|
|
598
|
+
cwd: context.cwd
|
|
599
|
+
});
|
|
600
|
+
if (!request.skipGitRepoCheck) {
|
|
601
|
+
await assertTrustedDirectory(request.workingDirectory);
|
|
602
|
+
}
|
|
603
|
+
validateModel(request.model, request.oss === true);
|
|
604
|
+
const hookContext = {
|
|
605
|
+
command: "run",
|
|
606
|
+
cwd: context.cwd,
|
|
607
|
+
options: argv
|
|
608
|
+
};
|
|
609
|
+
await runBeforeStartHooks(combinedConfig.beforeStartHooks, hookContext, combinedConfig.warnings);
|
|
610
|
+
const binding = getNativeBinding();
|
|
611
|
+
if (!binding) {
|
|
612
|
+
throw new Error("Native N-API binding is not available.");
|
|
613
|
+
}
|
|
614
|
+
const queue = new AsyncQueue();
|
|
615
|
+
let conversationId = null;
|
|
616
|
+
const lspBridge = createRunCommandLspBridge({
|
|
617
|
+
binding,
|
|
618
|
+
workingDirectory: request.workingDirectory ?? context.cwd,
|
|
619
|
+
initialThreadId: request.threadId
|
|
620
|
+
});
|
|
621
|
+
const handleEvent = async (eventJson) => {
|
|
622
|
+
if (!eventJson) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
process2.stdout.write(eventJson);
|
|
626
|
+
process2.stdout.write("\n");
|
|
627
|
+
let eventPayload = eventJson;
|
|
628
|
+
try {
|
|
629
|
+
eventPayload = JSON.parse(eventJson);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
conversationId ??= extractConversationId(eventPayload);
|
|
633
|
+
const threadEvent = toThreadEvent(eventPayload);
|
|
634
|
+
if (threadEvent && lspBridge) {
|
|
635
|
+
lspBridge.handleEvent(threadEvent);
|
|
636
|
+
}
|
|
637
|
+
await runEventHooks(
|
|
638
|
+
combinedConfig.onEventHooks,
|
|
639
|
+
eventPayload,
|
|
640
|
+
hookContext,
|
|
641
|
+
combinedConfig.warnings
|
|
642
|
+
);
|
|
643
|
+
};
|
|
644
|
+
let runPromise = Promise.resolve();
|
|
645
|
+
runPromise = binding.runThreadStream(request, (err, eventJson) => {
|
|
646
|
+
if (err) {
|
|
647
|
+
queue.fail(err);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
queue.push(eventJson ?? null);
|
|
651
|
+
}).then(
|
|
652
|
+
() => queue.end(),
|
|
653
|
+
(error) => {
|
|
654
|
+
queue.fail(error);
|
|
655
|
+
}
|
|
656
|
+
);
|
|
657
|
+
let loopError;
|
|
658
|
+
try {
|
|
659
|
+
for await (const eventJson of queue) {
|
|
660
|
+
try {
|
|
661
|
+
await handleEvent(eventJson);
|
|
662
|
+
} catch (error) {
|
|
663
|
+
combinedConfig.warnings.push(
|
|
664
|
+
`Event handler failed: ${error.message ?? String(error)}`
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
await runPromise;
|
|
669
|
+
} catch (error) {
|
|
670
|
+
loopError = error;
|
|
671
|
+
throw error;
|
|
672
|
+
} finally {
|
|
673
|
+
queue.end();
|
|
674
|
+
if (loopError) {
|
|
675
|
+
await runPromise.catch(() => {
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
if (lspBridge) {
|
|
679
|
+
lspBridge.dispose();
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (conversationId) {
|
|
683
|
+
process2.stdout.write(`
|
|
684
|
+
To resume, run: codex-native tui --resume ${conversationId}
|
|
685
|
+
`);
|
|
686
|
+
}
|
|
687
|
+
emitWarnings(combinedConfig.warnings, warningCount);
|
|
688
|
+
}
|
|
689
|
+
function toThreadEvent(payload) {
|
|
690
|
+
if (!payload || typeof payload !== "object") {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
return convertRustEventToThreadEvent(payload);
|
|
695
|
+
} catch {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
async function resolvePrompt(argv, defaultPrompt, cwd) {
|
|
700
|
+
if (argv.prompt && argv.prompt.trim().length > 0) {
|
|
701
|
+
return argv.prompt;
|
|
702
|
+
}
|
|
703
|
+
if (defaultPrompt && defaultPrompt.trim().length > 0) {
|
|
704
|
+
return defaultPrompt;
|
|
705
|
+
}
|
|
706
|
+
const stdinPrompt = await readPromptFromStdin();
|
|
707
|
+
if (stdinPrompt && stdinPrompt.trim().length > 0) {
|
|
708
|
+
return stdinPrompt;
|
|
709
|
+
}
|
|
710
|
+
if (argv.threadId) {
|
|
711
|
+
return "";
|
|
712
|
+
}
|
|
713
|
+
const baseMessage = "No prompt provided. Supply a prompt or pipe one via stdin.";
|
|
714
|
+
if (process2.stdin.isTTY) {
|
|
715
|
+
throw new Error(baseMessage);
|
|
716
|
+
}
|
|
717
|
+
throw new Error(baseMessage);
|
|
718
|
+
}
|
|
719
|
+
async function buildRunRequest(params) {
|
|
720
|
+
const { prompt, argv, combinedDefaults, cwd } = params;
|
|
721
|
+
const request = {
|
|
722
|
+
...combinedDefaults,
|
|
723
|
+
prompt
|
|
724
|
+
};
|
|
725
|
+
if (combinedDefaults.images) {
|
|
726
|
+
request.images = [...combinedDefaults.images];
|
|
727
|
+
}
|
|
728
|
+
if (combinedDefaults.workspaceWriteOptions) {
|
|
729
|
+
request.workspaceWriteOptions = { ...combinedDefaults.workspaceWriteOptions };
|
|
730
|
+
}
|
|
731
|
+
if (argv.model !== void 0) request.model = argv.model;
|
|
732
|
+
if (argv.oss !== void 0) request.oss = argv.oss;
|
|
733
|
+
const sandboxMode = parseSandboxModeFlag(argv.sandbox, "--sandbox");
|
|
734
|
+
if (sandboxMode !== void 0) {
|
|
735
|
+
request.sandboxMode = sandboxMode;
|
|
736
|
+
}
|
|
737
|
+
const approvalMode = parseApprovalModeFlag(argv.approval, "--approval");
|
|
738
|
+
if (approvalMode !== void 0) {
|
|
739
|
+
request.approvalMode = approvalMode;
|
|
740
|
+
}
|
|
741
|
+
if (argv.threadId !== void 0) request.threadId = argv.threadId;
|
|
742
|
+
if (argv.baseUrl !== void 0) request.baseUrl = argv.baseUrl;
|
|
743
|
+
if (argv.apiKey !== void 0) request.apiKey = argv.apiKey;
|
|
744
|
+
if (argv.linuxSandboxPath !== void 0) request.linuxSandboxPath = argv.linuxSandboxPath;
|
|
745
|
+
if (argv.fullAuto !== void 0) request.fullAuto = argv.fullAuto;
|
|
746
|
+
if (argv.skipGitRepoCheck !== void 0) request.skipGitRepoCheck = argv.skipGitRepoCheck;
|
|
747
|
+
if (argv.cd !== void 0) request.workingDirectory = argv.cd;
|
|
748
|
+
if (argv.reviewMode !== void 0) request.reviewMode = argv.reviewMode;
|
|
749
|
+
if (argv.reviewHint !== void 0) request.reviewHint = argv.reviewHint;
|
|
750
|
+
const images = [
|
|
751
|
+
...Array.isArray(request.images) ? request.images : [],
|
|
752
|
+
...argv.image ?? []
|
|
753
|
+
];
|
|
754
|
+
request.images = images.length > 0 ? images : void 0;
|
|
755
|
+
if (argv.schema) {
|
|
756
|
+
request.outputSchema = await readJsonFile(argv.schema);
|
|
757
|
+
}
|
|
758
|
+
applyElevatedRunDefaults(request, cwd);
|
|
759
|
+
return request;
|
|
760
|
+
}
|
|
761
|
+
async function readJsonFile(filePath) {
|
|
762
|
+
const absolute = path3.resolve(process2.cwd(), filePath);
|
|
763
|
+
const data = await fsPromises2.readFile(absolute, "utf8");
|
|
764
|
+
try {
|
|
765
|
+
return JSON.parse(data);
|
|
766
|
+
} catch (error) {
|
|
767
|
+
throw new Error(
|
|
768
|
+
`Failed to parse JSON schema from ${absolute}: ${error.message ?? error}`
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
async function readPromptFromStdin() {
|
|
773
|
+
if (process2.stdin.isTTY) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
const chunks = [];
|
|
777
|
+
for await (const chunk of process2.stdin) {
|
|
778
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
779
|
+
}
|
|
780
|
+
if (chunks.length === 0) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
return Buffer.concat(chunks).toString("utf8").trimEnd();
|
|
784
|
+
}
|
|
785
|
+
function extractConversationId(eventPayload) {
|
|
786
|
+
if (!eventPayload || typeof eventPayload !== "object") {
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
const record = eventPayload;
|
|
790
|
+
if (typeof record.session_id === "string") {
|
|
791
|
+
return record.session_id;
|
|
792
|
+
}
|
|
793
|
+
const sessionConfigured = record.SessionConfigured ?? record.sessionConfigured;
|
|
794
|
+
if (sessionConfigured && typeof sessionConfigured === "object") {
|
|
795
|
+
const configuredSessionId = sessionConfigured.session_id;
|
|
796
|
+
if (typeof configuredSessionId === "string") {
|
|
797
|
+
return configuredSessionId;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const nestedSession = typeof record.session === "object" && record.session ? record.session.id : void 0;
|
|
801
|
+
if (typeof nestedSession === "string") {
|
|
802
|
+
return nestedSession;
|
|
803
|
+
}
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
function validateModel(model, oss) {
|
|
807
|
+
if (!model) return;
|
|
808
|
+
const trimmed = String(model).trim();
|
|
809
|
+
if (oss) {
|
|
810
|
+
if (!trimmed.startsWith("gpt-oss:")) {
|
|
811
|
+
throw new Error(
|
|
812
|
+
`Invalid model "${trimmed}" for OSS mode. Use models prefixed with "gpt-oss:", e.g. "gpt-oss:20b".`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
818
|
+
// GPT models
|
|
819
|
+
"gpt-5",
|
|
820
|
+
"gpt-5-codex",
|
|
821
|
+
"gpt-5-codex-mini",
|
|
822
|
+
"gpt-5.1",
|
|
823
|
+
"gpt-5.1-codex",
|
|
824
|
+
"gpt-5.1-codex-mini",
|
|
825
|
+
// Claude models
|
|
826
|
+
"claude-sonnet-4-5-20250929",
|
|
827
|
+
"claude-sonnet-4-20250514",
|
|
828
|
+
"claude-opus-4-20250514"
|
|
829
|
+
]);
|
|
830
|
+
if (!allowed.has(trimmed) && !trimmed.startsWith("claude-") && !trimmed.startsWith("gpt-")) {
|
|
831
|
+
throw new Error(
|
|
832
|
+
`Invalid model "${trimmed}". Supported models: ${Array.from(allowed).map((m) => `"${m}"`).join(", ")}, or any model starting with "claude-" or "gpt-".`
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
async function assertTrustedDirectory(workingDirectory) {
|
|
837
|
+
const directory = workingDirectory ? path3.resolve(workingDirectory) : process2.cwd();
|
|
838
|
+
if (await findGitRoot(directory)) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
throw new Error(
|
|
842
|
+
"Not inside a trusted directory and --skip-git-repo-check was not specified."
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
async function findGitRoot(startDir) {
|
|
846
|
+
let current = path3.resolve(startDir);
|
|
847
|
+
while (true) {
|
|
848
|
+
const gitPath = path3.join(current, ".git");
|
|
849
|
+
if (fs2.existsSync(gitPath)) {
|
|
850
|
+
try {
|
|
851
|
+
const stats = await fsPromises2.stat(gitPath);
|
|
852
|
+
if (stats.isDirectory() || stats.isFile()) {
|
|
853
|
+
return current;
|
|
854
|
+
}
|
|
855
|
+
} catch {
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const parent = path3.dirname(current);
|
|
859
|
+
if (parent === current) {
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
current = parent;
|
|
863
|
+
}
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
var AsyncQueue = class {
|
|
867
|
+
buffer = [];
|
|
868
|
+
waiters = [];
|
|
869
|
+
ended = false;
|
|
870
|
+
error;
|
|
871
|
+
push(value) {
|
|
872
|
+
if (this.ended) return;
|
|
873
|
+
if (value === null) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (this.waiters.length > 0) {
|
|
877
|
+
const waiter = this.waiters.shift();
|
|
878
|
+
waiter.resolve({ value, done: false });
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
this.buffer.push(value);
|
|
882
|
+
}
|
|
883
|
+
end() {
|
|
884
|
+
if (this.ended) return;
|
|
885
|
+
this.ended = true;
|
|
886
|
+
const waiters = this.waiters;
|
|
887
|
+
this.waiters = [];
|
|
888
|
+
for (const waiter of waiters) {
|
|
889
|
+
waiter.resolve({ value: void 0, done: true });
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
fail(error) {
|
|
893
|
+
if (this.ended) return;
|
|
894
|
+
this.error = error;
|
|
895
|
+
this.ended = true;
|
|
896
|
+
const waiters = this.waiters;
|
|
897
|
+
this.waiters = [];
|
|
898
|
+
for (const waiter of waiters) {
|
|
899
|
+
waiter.reject(error);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
async next() {
|
|
903
|
+
if (this.buffer.length > 0) {
|
|
904
|
+
const value = this.buffer.shift();
|
|
905
|
+
return { value, done: false };
|
|
906
|
+
}
|
|
907
|
+
if (this.error) {
|
|
908
|
+
return Promise.reject(this.error);
|
|
909
|
+
}
|
|
910
|
+
if (this.ended) {
|
|
911
|
+
return { value: void 0, done: true };
|
|
912
|
+
}
|
|
913
|
+
return new Promise((resolve, reject) => {
|
|
914
|
+
this.waiters.push({ resolve, reject });
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
[Symbol.asyncIterator]() {
|
|
918
|
+
return this;
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// src/cli/tui.ts
|
|
923
|
+
import process3 from "process";
|
|
924
|
+
async function executeTuiCommand(argv, context) {
|
|
925
|
+
if (!process3.stdout.isTTY || !process3.stdin.isTTY) {
|
|
926
|
+
throw new Error("The interactive TUI requires an interactive terminal (TTY).");
|
|
927
|
+
}
|
|
928
|
+
const { combinedConfig } = context;
|
|
929
|
+
emitWarnings(combinedConfig.warnings);
|
|
930
|
+
const warningCount = combinedConfig.warnings.length;
|
|
931
|
+
const { request, thread: threadOptions } = buildTuiConfig({
|
|
932
|
+
argv,
|
|
933
|
+
defaults: combinedConfig.tuiDefaults,
|
|
934
|
+
cwd: context.cwd
|
|
935
|
+
});
|
|
936
|
+
applyElevatedTuiDefaults({ request, thread: threadOptions, cwd: context.cwd });
|
|
937
|
+
const hookContext = {
|
|
938
|
+
command: "tui",
|
|
939
|
+
cwd: context.cwd,
|
|
940
|
+
options: argv
|
|
941
|
+
};
|
|
942
|
+
await runBeforeStartHooks(combinedConfig.beforeStartHooks, hookContext, combinedConfig.warnings);
|
|
943
|
+
const codex = new Codex({
|
|
944
|
+
baseUrl: request.baseUrl,
|
|
945
|
+
apiKey: request.apiKey,
|
|
946
|
+
preserveRegisteredTools: true
|
|
947
|
+
});
|
|
948
|
+
const thread = codex.startThread(threadOptions);
|
|
949
|
+
const exitInfo = await thread.tui(request);
|
|
950
|
+
if (exitInfo.conversationId) {
|
|
951
|
+
process3.stdout.write(`
|
|
952
|
+
Conversation ID: ${exitInfo.conversationId}
|
|
953
|
+
`);
|
|
954
|
+
}
|
|
955
|
+
if (exitInfo.updateAction) {
|
|
956
|
+
process3.stdout.write(
|
|
957
|
+
`Update available (${exitInfo.updateAction.kind}): ${exitInfo.updateAction.command}
|
|
958
|
+
`
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
emitWarnings(combinedConfig.warnings, warningCount);
|
|
962
|
+
}
|
|
963
|
+
function buildTuiConfig(params) {
|
|
964
|
+
const { argv, defaults, cwd } = params;
|
|
965
|
+
const request = {
|
|
966
|
+
...defaults
|
|
967
|
+
};
|
|
968
|
+
if (argv.prompt !== void 0) request.prompt = argv.prompt;
|
|
969
|
+
if (argv.model !== void 0) request.model = argv.model;
|
|
970
|
+
if (argv.oss !== void 0) request.oss = argv.oss;
|
|
971
|
+
const sandboxMode = parseSandboxModeFlag(argv.sandbox, "--sandbox");
|
|
972
|
+
if (sandboxMode !== void 0) {
|
|
973
|
+
request.sandboxMode = sandboxMode;
|
|
974
|
+
}
|
|
975
|
+
const approvalMode = parseApprovalModeFlag(argv.approval, "--approval");
|
|
976
|
+
if (approvalMode !== void 0) {
|
|
977
|
+
request.approvalMode = approvalMode;
|
|
978
|
+
}
|
|
979
|
+
if (argv.resume !== void 0) request.resumeSessionId = argv.resume;
|
|
980
|
+
if (argv.resumeLast !== void 0) request.resumeLast = argv.resumeLast;
|
|
981
|
+
if (argv.resumePicker !== void 0) request.resumePicker = argv.resumePicker;
|
|
982
|
+
if (argv.fullAuto !== void 0) request.fullAuto = argv.fullAuto;
|
|
983
|
+
if (argv.dangerouslyBypassApprovalsAndSandbox !== void 0) {
|
|
984
|
+
request.dangerouslyBypassApprovalsAndSandbox = argv.dangerouslyBypassApprovalsAndSandbox;
|
|
985
|
+
}
|
|
986
|
+
if (argv.cd !== void 0) request.workingDirectory = argv.cd;
|
|
987
|
+
if (argv.configProfile !== void 0) request.configProfile = argv.configProfile;
|
|
988
|
+
if (argv.webSearch !== void 0) request.webSearch = argv.webSearch;
|
|
989
|
+
if (argv.linuxSandboxPath !== void 0) request.linuxSandboxPath = argv.linuxSandboxPath;
|
|
990
|
+
if (argv.baseUrl !== void 0) request.baseUrl = argv.baseUrl;
|
|
991
|
+
if (argv.apiKey !== void 0) request.apiKey = argv.apiKey;
|
|
992
|
+
if (argv.configOverrides) {
|
|
993
|
+
const defaultsOverrides = Array.isArray(request.configOverrides) ? [...request.configOverrides] : [];
|
|
994
|
+
request.configOverrides = [...defaultsOverrides, ...argv.configOverrides];
|
|
995
|
+
}
|
|
996
|
+
if (argv.addDir) {
|
|
997
|
+
const defaultsAddDir = Array.isArray(request.addDir) ? [...request.addDir] : [];
|
|
998
|
+
request.addDir = [...defaultsAddDir, ...argv.addDir];
|
|
999
|
+
}
|
|
1000
|
+
if (argv.image) {
|
|
1001
|
+
const defaultsImages = Array.isArray(request.images) ? [...request.images] : [];
|
|
1002
|
+
request.images = [...defaultsImages, ...argv.image];
|
|
1003
|
+
}
|
|
1004
|
+
const thread = {
|
|
1005
|
+
model: request.model,
|
|
1006
|
+
oss: request.oss,
|
|
1007
|
+
sandboxMode: request.sandboxMode,
|
|
1008
|
+
approvalMode: request.approvalMode,
|
|
1009
|
+
workingDirectory: request.workingDirectory ?? cwd,
|
|
1010
|
+
skipGitRepoCheck: false
|
|
1011
|
+
};
|
|
1012
|
+
return { request, thread };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/cli/reverie.ts
|
|
1016
|
+
import path4 from "path";
|
|
1017
|
+
import os from "os";
|
|
1018
|
+
import { parseArgs } from "util";
|
|
1019
|
+
var DEFAULT_MODEL = "mixedbread-ai/mxbai-embed-large-v1";
|
|
1020
|
+
var INDEX_OPTION_DEFS = {
|
|
1021
|
+
"codex-home": { type: "string" },
|
|
1022
|
+
"project-root": { type: "string" },
|
|
1023
|
+
limit: { type: "string" },
|
|
1024
|
+
"max-candidates": { type: "string" },
|
|
1025
|
+
"batch-size": { type: "string" },
|
|
1026
|
+
normalize: { type: "boolean" },
|
|
1027
|
+
cache: { type: "boolean" },
|
|
1028
|
+
"embed-model": { type: "string" },
|
|
1029
|
+
"embed-cache-dir": { type: "string" },
|
|
1030
|
+
"embed-max-length": { type: "string" },
|
|
1031
|
+
"no-progress": { type: "boolean" },
|
|
1032
|
+
"skip-embed-init": { type: "boolean" }
|
|
1033
|
+
};
|
|
1034
|
+
async function executeReverieCommand(args) {
|
|
1035
|
+
const [first, ...rest] = args;
|
|
1036
|
+
const isFlag = first?.startsWith("-");
|
|
1037
|
+
const command = !first || isFlag ? "index" : first;
|
|
1038
|
+
const tail = !first || isFlag ? args : rest;
|
|
1039
|
+
if (command !== "index") {
|
|
1040
|
+
throw new Error(`Unknown reverie command '${command}'. Supported subcommands: index`);
|
|
1041
|
+
}
|
|
1042
|
+
await runReverieIndex(tail);
|
|
1043
|
+
}
|
|
1044
|
+
async function runReverieIndex(args) {
|
|
1045
|
+
const { values } = parseArgs({ args, options: INDEX_OPTION_DEFS, allowPositionals: false, strict: true });
|
|
1046
|
+
const codexHome = resolveCodexHome(values["codex-home"]);
|
|
1047
|
+
const projectRoot = resolveProjectRoot(values["project-root"]);
|
|
1048
|
+
const limit = parseOptionalInt(values.limit);
|
|
1049
|
+
const maxCandidates = parseOptionalInt(values["max-candidates"]);
|
|
1050
|
+
const batchSize = parseOptionalInt(values["batch-size"]);
|
|
1051
|
+
const embedMaxLength = parseOptionalInt(values["embed-max-length"]);
|
|
1052
|
+
const normalize = typeof values.normalize === "boolean" ? values.normalize : void 0;
|
|
1053
|
+
const cache = typeof values.cache === "boolean" ? values.cache : void 0;
|
|
1054
|
+
const embedModel = typeof values["embed-model"] === "string" ? values["embed-model"] : DEFAULT_MODEL;
|
|
1055
|
+
const embedCacheDir = typeof values["embed-cache-dir"] === "string" ? values["embed-cache-dir"] : void 0;
|
|
1056
|
+
const showDownloadProgress = values["no-progress"] ? false : true;
|
|
1057
|
+
const skipEmbedInit = values["skip-embed-init"] === true;
|
|
1058
|
+
if (!skipEmbedInit) {
|
|
1059
|
+
await fastEmbedInit({
|
|
1060
|
+
model: embedModel,
|
|
1061
|
+
cacheDir: embedCacheDir ? path4.resolve(embedCacheDir) : defaultCacheDir(),
|
|
1062
|
+
maxLength: embedMaxLength ?? void 0,
|
|
1063
|
+
showDownloadProgress
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
const options = {
|
|
1067
|
+
limit,
|
|
1068
|
+
maxCandidates,
|
|
1069
|
+
projectRoot,
|
|
1070
|
+
batchSize,
|
|
1071
|
+
normalize,
|
|
1072
|
+
cache
|
|
1073
|
+
};
|
|
1074
|
+
console.log(`\u{1F4C2} Codex home: ${codexHome}`);
|
|
1075
|
+
console.log(`\u{1F4C1} Project root: ${projectRoot}`);
|
|
1076
|
+
const stats = await reverieIndexSemantic(codexHome, options);
|
|
1077
|
+
console.log(
|
|
1078
|
+
`\u2705 Indexed ${stats.documentsEmbedded} conversation(s) across ${stats.batches} batch(es); cache warmed at ${projectRoot}`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
function resolveCodexHome(explicit) {
|
|
1082
|
+
if (explicit) {
|
|
1083
|
+
return path4.resolve(explicit);
|
|
1084
|
+
}
|
|
1085
|
+
if (process.env.CODEX_HOME) {
|
|
1086
|
+
return process.env.CODEX_HOME;
|
|
1087
|
+
}
|
|
1088
|
+
const home = os.homedir() || process.cwd();
|
|
1089
|
+
return path4.join(home, ".codex");
|
|
1090
|
+
}
|
|
1091
|
+
function resolveProjectRoot(explicit) {
|
|
1092
|
+
if (explicit) {
|
|
1093
|
+
return path4.resolve(explicit);
|
|
1094
|
+
}
|
|
1095
|
+
return process.cwd();
|
|
1096
|
+
}
|
|
1097
|
+
function parseOptionalInt(value) {
|
|
1098
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1099
|
+
const parsed = Number(value);
|
|
1100
|
+
if (!Number.isNaN(parsed)) {
|
|
1101
|
+
return parsed;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return void 0;
|
|
1105
|
+
}
|
|
1106
|
+
function defaultCacheDir() {
|
|
1107
|
+
if (process.env.CODEX_EMBED_CACHE) {
|
|
1108
|
+
return path4.resolve(process.env.CODEX_EMBED_CACHE);
|
|
1109
|
+
}
|
|
1110
|
+
return path4.join(os.tmpdir(), "codex-embed-cache");
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// src/cli/runtime.ts
|
|
1114
|
+
async function buildCombinedConfig(params) {
|
|
1115
|
+
const { cwd, config } = params;
|
|
1116
|
+
const warnings = [...config.warnings];
|
|
1117
|
+
const combined = {
|
|
1118
|
+
runDefaults: {},
|
|
1119
|
+
tuiDefaults: {},
|
|
1120
|
+
tools: [],
|
|
1121
|
+
interceptors: [],
|
|
1122
|
+
approval: void 0,
|
|
1123
|
+
beforeStartHooks: [],
|
|
1124
|
+
onEventHooks: [],
|
|
1125
|
+
warnings,
|
|
1126
|
+
allowReservedInterceptors: false
|
|
1127
|
+
};
|
|
1128
|
+
const pluginContext = { cwd, configPath: config.configPath };
|
|
1129
|
+
if (config.config) {
|
|
1130
|
+
accumulateConfig({
|
|
1131
|
+
combined,
|
|
1132
|
+
config: config.config,
|
|
1133
|
+
source: config.configPath ?? "config",
|
|
1134
|
+
warnings
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
for (const loaded of config.plugins) {
|
|
1138
|
+
const pluginConfig = await evaluatePlugin(loaded, pluginContext, warnings);
|
|
1139
|
+
if (pluginConfig) {
|
|
1140
|
+
accumulateConfig({
|
|
1141
|
+
combined,
|
|
1142
|
+
config: pluginConfig,
|
|
1143
|
+
source: loaded.spec,
|
|
1144
|
+
warnings
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
combined.warnings = warnings;
|
|
1149
|
+
return combined;
|
|
1150
|
+
}
|
|
1151
|
+
function applyNativeRegistrations(combined) {
|
|
1152
|
+
const binding = getNativeBinding();
|
|
1153
|
+
if (!binding) {
|
|
1154
|
+
throw new Error("Native binding is not available.");
|
|
1155
|
+
}
|
|
1156
|
+
binding.clearRegisteredTools();
|
|
1157
|
+
const seenTools = /* @__PURE__ */ new Set();
|
|
1158
|
+
for (const tool of combined.tools) {
|
|
1159
|
+
const { handler, ...info } = tool;
|
|
1160
|
+
const name = String(info.name);
|
|
1161
|
+
if (seenTools.has(name)) {
|
|
1162
|
+
combined.warnings.push(`Duplicate tool "${name}" ignored (first definition wins).`);
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
seenTools.add(name);
|
|
1166
|
+
binding.registerTool(info, handler);
|
|
1167
|
+
}
|
|
1168
|
+
if (combined.approval && typeof binding.registerApprovalCallback === "function") {
|
|
1169
|
+
binding.registerApprovalCallback(combined.approval.handler);
|
|
1170
|
+
}
|
|
1171
|
+
const RESERVED = /* @__PURE__ */ new Set(["local_shell", "exec_command", "apply_patch", "web_search"]);
|
|
1172
|
+
const seenInterceptors = /* @__PURE__ */ new Set();
|
|
1173
|
+
for (const interceptor of combined.interceptors) {
|
|
1174
|
+
const name = interceptor.toolName;
|
|
1175
|
+
if (RESERVED.has(name) && !combined.allowReservedInterceptors) {
|
|
1176
|
+
combined.warnings.push(
|
|
1177
|
+
`Interceptor for "${name}" ignored: reserved for approval gating. Use approvals() hook instead.`
|
|
1178
|
+
);
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
if (seenInterceptors.has(name)) {
|
|
1182
|
+
combined.warnings.push(
|
|
1183
|
+
`Multiple interceptors for "${name}" detected; only the first will be used.`
|
|
1184
|
+
);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
seenInterceptors.add(name);
|
|
1188
|
+
binding.registerToolInterceptor(interceptor.toolName, interceptor.handler);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
async function evaluatePlugin(loaded, context, warnings) {
|
|
1192
|
+
const { plugin, spec } = loaded;
|
|
1193
|
+
try {
|
|
1194
|
+
if (typeof plugin === "function") {
|
|
1195
|
+
const result = await plugin(context);
|
|
1196
|
+
return coerceConfig(result, spec, warnings);
|
|
1197
|
+
}
|
|
1198
|
+
if (plugin && typeof plugin === "object") {
|
|
1199
|
+
const candidate = plugin;
|
|
1200
|
+
if (typeof candidate.setup === "function") {
|
|
1201
|
+
await candidate.setup(context);
|
|
1202
|
+
}
|
|
1203
|
+
if (typeof candidate.config === "function") {
|
|
1204
|
+
return coerceConfig(await candidate.config(context), spec, warnings);
|
|
1205
|
+
}
|
|
1206
|
+
if (candidate.config) {
|
|
1207
|
+
return coerceConfig(candidate.config, spec, warnings);
|
|
1208
|
+
}
|
|
1209
|
+
return coerceConfig(candidate, spec, warnings);
|
|
1210
|
+
}
|
|
1211
|
+
return coerceConfig(plugin, spec, warnings);
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
warnings.push(`Plugin "${spec}" threw an error: ${error.message}`);
|
|
1214
|
+
return null;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function coerceConfig(value, source, warnings) {
|
|
1218
|
+
if (!value) {
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
if (typeof value === "object") {
|
|
1222
|
+
return value;
|
|
1223
|
+
}
|
|
1224
|
+
warnings.push(`Plugin "${source}" did not return a config object.`);
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
function accumulateConfig(params) {
|
|
1228
|
+
const { combined, config, source, warnings } = params;
|
|
1229
|
+
if (config.defaults?.run) {
|
|
1230
|
+
combined.runDefaults = { ...combined.runDefaults, ...config.defaults.run };
|
|
1231
|
+
}
|
|
1232
|
+
if (config.defaults?.tui) {
|
|
1233
|
+
combined.tuiDefaults = { ...combined.tuiDefaults, ...config.defaults.tui };
|
|
1234
|
+
}
|
|
1235
|
+
if (Array.isArray(config.tools)) {
|
|
1236
|
+
for (const tool of config.tools) {
|
|
1237
|
+
if (!tool || typeof tool !== "object" || typeof tool.handler !== "function") {
|
|
1238
|
+
warnings.push(`Invalid tool definition supplied by "${source}".`);
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
combined.tools.push(tool);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (Array.isArray(config.interceptors)) {
|
|
1245
|
+
for (const interceptor of config.interceptors) {
|
|
1246
|
+
if (!interceptor || typeof interceptor !== "object" || typeof interceptor.toolName !== "string" || typeof interceptor.handler !== "function") {
|
|
1247
|
+
warnings.push(`Invalid interceptor definition supplied by "${source}".`);
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
combined.interceptors.push(interceptor);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (config.approvals) {
|
|
1254
|
+
if (typeof config.approvals !== "function") {
|
|
1255
|
+
warnings.push(`Approval callback from "${source}" must be a function.`);
|
|
1256
|
+
} else {
|
|
1257
|
+
if (combined.approval) {
|
|
1258
|
+
warnings.push(
|
|
1259
|
+
`Approval callback from "${source}" overrides handler from "${combined.approval.source}".`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
combined.approval = { source, handler: config.approvals };
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
if (config.hooks) {
|
|
1266
|
+
addHooks(combined, config.hooks, source, warnings);
|
|
1267
|
+
}
|
|
1268
|
+
if (config.allowReservedInterceptors === true) {
|
|
1269
|
+
combined.allowReservedInterceptors = true;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
function addHooks(combined, hooks, source, warnings) {
|
|
1273
|
+
if (hooks.beforeStart) {
|
|
1274
|
+
const beforeStartCallbacks = Array.isArray(hooks.beforeStart) ? hooks.beforeStart : [hooks.beforeStart];
|
|
1275
|
+
for (const callback of beforeStartCallbacks) {
|
|
1276
|
+
if (typeof callback !== "function") {
|
|
1277
|
+
warnings.push(`beforeStart hook from "${source}" must be a function.`);
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
combined.beforeStartHooks.push({ source, callback });
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
if (hooks.onEvent) {
|
|
1284
|
+
const eventCallbacks = Array.isArray(hooks.onEvent) ? hooks.onEvent : [hooks.onEvent];
|
|
1285
|
+
for (const callback of eventCallbacks) {
|
|
1286
|
+
if (typeof callback !== "function") {
|
|
1287
|
+
warnings.push(`onEvent hook from "${source}" must be a function.`);
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
combined.onEventHooks.push({ source, callback });
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/cli/index.ts
|
|
1296
|
+
var VERSION = package_default.version;
|
|
1297
|
+
var SANDBOX_CHOICES = ["read-only", "workspace-write", "danger-full-access"];
|
|
1298
|
+
var APPROVAL_CHOICES = ["never", "on-request", "on-failure", "untrusted"];
|
|
1299
|
+
var APPLY_PATCH_FLAG = "--codex-run-as-apply-patch";
|
|
1300
|
+
var CLI_ENTRYPOINT_ENV = "CODEX_NODE_CLI_ENTRYPOINT";
|
|
1301
|
+
try {
|
|
1302
|
+
const entrypoint = fileURLToPath(import.meta.url);
|
|
1303
|
+
process4.env[CLI_ENTRYPOINT_ENV] = entrypoint;
|
|
1304
|
+
} catch {
|
|
1305
|
+
if (process4.argv[1]) {
|
|
1306
|
+
process4.env[CLI_ENTRYPOINT_ENV] = process4.argv[1];
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
var GLOBAL_OPTION_DEFS = {
|
|
1310
|
+
config: { type: "string" },
|
|
1311
|
+
"no-config": { type: "boolean" },
|
|
1312
|
+
plugin: { type: "string", multiple: true }
|
|
1313
|
+
};
|
|
1314
|
+
var RUN_OPTION_DEFS = {
|
|
1315
|
+
model: { type: "string" },
|
|
1316
|
+
oss: { type: "boolean" },
|
|
1317
|
+
sandbox: { type: "string" },
|
|
1318
|
+
approval: { type: "string" },
|
|
1319
|
+
schema: { type: "string" },
|
|
1320
|
+
"thread-id": { type: "string" },
|
|
1321
|
+
"base-url": { type: "string" },
|
|
1322
|
+
"api-key": { type: "string" },
|
|
1323
|
+
"linux-sandbox-path": { type: "string" },
|
|
1324
|
+
"full-auto": { type: "boolean" },
|
|
1325
|
+
"skip-git-repo-check": { type: "boolean" },
|
|
1326
|
+
cd: { type: "string" },
|
|
1327
|
+
image: { type: "string", multiple: true },
|
|
1328
|
+
"review-mode": { type: "boolean" },
|
|
1329
|
+
"review-hint": { type: "string" }
|
|
1330
|
+
};
|
|
1331
|
+
var TUI_OPTION_DEFS = {
|
|
1332
|
+
model: { type: "string" },
|
|
1333
|
+
oss: { type: "boolean" },
|
|
1334
|
+
sandbox: { type: "string" },
|
|
1335
|
+
approval: { type: "string" },
|
|
1336
|
+
resume: { type: "string" },
|
|
1337
|
+
"resume-last": { type: "boolean" },
|
|
1338
|
+
"resume-picker": { type: "boolean" },
|
|
1339
|
+
"full-auto": { type: "boolean" },
|
|
1340
|
+
"dangerously-bypass-approvals-and-sandbox": { type: "boolean" },
|
|
1341
|
+
cd: { type: "string" },
|
|
1342
|
+
"config-profile": { type: "string" },
|
|
1343
|
+
"config-overrides": { type: "string", multiple: true },
|
|
1344
|
+
"add-dir": { type: "string", multiple: true },
|
|
1345
|
+
image: { type: "string", multiple: true },
|
|
1346
|
+
"web-search": { type: "boolean" },
|
|
1347
|
+
"linux-sandbox-path": { type: "string" },
|
|
1348
|
+
"base-url": { type: "string" },
|
|
1349
|
+
"api-key": { type: "string" }
|
|
1350
|
+
};
|
|
1351
|
+
async function main() {
|
|
1352
|
+
const rawArgs = process4.argv.slice(2);
|
|
1353
|
+
if (maybeHandleApplyPatch(rawArgs)) {
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
if (hasFlag(rawArgs, "--version") || hasFlag(rawArgs, "-v")) {
|
|
1357
|
+
printVersion();
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const generalHelpRequested = hasFlag(rawArgs, "--help") || hasFlag(rawArgs, "-h");
|
|
1361
|
+
if (generalHelpRequested && !hasExplicitCommand(rawArgs)) {
|
|
1362
|
+
printGeneralHelp();
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
const { command, args } = selectCommand(rawArgs);
|
|
1366
|
+
if (hasCommandHelpFlag(args)) {
|
|
1367
|
+
printCommandHelp(command);
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (command === "reverie-index") {
|
|
1371
|
+
await executeReverieCommand(args);
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const options = command === "tui" ? parseTuiCommand(args) : parseRunCommand(args);
|
|
1375
|
+
validateOptionChoices(command, options);
|
|
1376
|
+
const context = await createContext(options);
|
|
1377
|
+
if (command === "tui") {
|
|
1378
|
+
await executeTuiCommand(options, context);
|
|
1379
|
+
} else {
|
|
1380
|
+
await executeRunCommand(options, context);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function maybeHandleApplyPatch(args) {
|
|
1384
|
+
if (args.length === 0 || args[0] !== APPLY_PATCH_FLAG) {
|
|
1385
|
+
return false;
|
|
1386
|
+
}
|
|
1387
|
+
const patch = args[1];
|
|
1388
|
+
if (!patch) {
|
|
1389
|
+
console.error(`${APPLY_PATCH_FLAG} requires a patch argument.`);
|
|
1390
|
+
process4.exitCode = 1;
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
try {
|
|
1394
|
+
runApplyPatch(patch);
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1397
|
+
console.error(`apply_patch failed: ${message}`);
|
|
1398
|
+
process4.exitCode = 1;
|
|
1399
|
+
}
|
|
1400
|
+
return true;
|
|
1401
|
+
}
|
|
1402
|
+
function selectCommand(argv) {
|
|
1403
|
+
if (argv.length > 0) {
|
|
1404
|
+
const [first, ...rest] = argv;
|
|
1405
|
+
if (first === "tui") {
|
|
1406
|
+
return { command: "tui", args: rest };
|
|
1407
|
+
}
|
|
1408
|
+
if (first === "run") {
|
|
1409
|
+
return { command: "run", args: rest };
|
|
1410
|
+
}
|
|
1411
|
+
if (first === "reverie") {
|
|
1412
|
+
if (rest.length === 0) {
|
|
1413
|
+
return { command: "reverie-index", args: [] };
|
|
1414
|
+
}
|
|
1415
|
+
return { command: "reverie-index", args: rest };
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (argv.length === 0) {
|
|
1419
|
+
const isInteractive2 = process4.stdout.isTTY && process4.stdin.isTTY;
|
|
1420
|
+
return { command: isInteractive2 ? "tui" : "run", args: [] };
|
|
1421
|
+
}
|
|
1422
|
+
const isInteractive = process4.stdout.isTTY && process4.stdin.isTTY;
|
|
1423
|
+
return { command: isInteractive ? "tui" : "run", args: argv };
|
|
1424
|
+
}
|
|
1425
|
+
function hasExplicitCommand(argv) {
|
|
1426
|
+
if (argv.length === 0) {
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
const first = argv[0];
|
|
1430
|
+
return first === "tui" || first === "run" || first === "reverie";
|
|
1431
|
+
}
|
|
1432
|
+
function parseRunCommand(args) {
|
|
1433
|
+
const { values, positionals } = parseArgs2({
|
|
1434
|
+
args,
|
|
1435
|
+
options: { ...GLOBAL_OPTION_DEFS, ...RUN_OPTION_DEFS },
|
|
1436
|
+
allowPositionals: true,
|
|
1437
|
+
strict: true
|
|
1438
|
+
});
|
|
1439
|
+
const options = camelCaseKeys(values);
|
|
1440
|
+
const runOptions = {
|
|
1441
|
+
...options
|
|
1442
|
+
};
|
|
1443
|
+
if (!runOptions.prompt && positionals.length > 0) {
|
|
1444
|
+
runOptions.prompt = positionals[0];
|
|
1445
|
+
}
|
|
1446
|
+
return runOptions;
|
|
1447
|
+
}
|
|
1448
|
+
function parseTuiCommand(args) {
|
|
1449
|
+
const { values, positionals } = parseArgs2({
|
|
1450
|
+
args,
|
|
1451
|
+
options: { ...GLOBAL_OPTION_DEFS, ...TUI_OPTION_DEFS },
|
|
1452
|
+
allowPositionals: true,
|
|
1453
|
+
strict: true
|
|
1454
|
+
});
|
|
1455
|
+
const options = camelCaseKeys(values);
|
|
1456
|
+
const tuiOptions = {
|
|
1457
|
+
...options
|
|
1458
|
+
};
|
|
1459
|
+
if (!tuiOptions.prompt && positionals.length > 0) {
|
|
1460
|
+
tuiOptions.prompt = positionals[0];
|
|
1461
|
+
}
|
|
1462
|
+
return tuiOptions;
|
|
1463
|
+
}
|
|
1464
|
+
async function createContext(options) {
|
|
1465
|
+
const cwd = process4.cwd();
|
|
1466
|
+
const configOptions = {
|
|
1467
|
+
cwd,
|
|
1468
|
+
explicitConfigPath: options.config,
|
|
1469
|
+
noConfig: options.noConfig,
|
|
1470
|
+
pluginPaths: normalizeStringArray(options.plugin)
|
|
1471
|
+
};
|
|
1472
|
+
const config = await loadCliConfig(configOptions);
|
|
1473
|
+
const combinedConfig = await buildCombinedConfig({ cwd, config });
|
|
1474
|
+
applyNativeRegistrations(combinedConfig);
|
|
1475
|
+
return { cwd, config, combinedConfig };
|
|
1476
|
+
}
|
|
1477
|
+
function normalizeStringArray(value) {
|
|
1478
|
+
if (Array.isArray(value)) {
|
|
1479
|
+
return value.map((item) => String(item));
|
|
1480
|
+
}
|
|
1481
|
+
if (typeof value === "string") {
|
|
1482
|
+
return [value];
|
|
1483
|
+
}
|
|
1484
|
+
return [];
|
|
1485
|
+
}
|
|
1486
|
+
function camelCaseKeys(record) {
|
|
1487
|
+
return Object.entries(record).reduce((acc, [key, value]) => {
|
|
1488
|
+
acc[toCamelCase(key)] = value;
|
|
1489
|
+
return acc;
|
|
1490
|
+
}, {});
|
|
1491
|
+
}
|
|
1492
|
+
function toCamelCase(value) {
|
|
1493
|
+
return value.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
1494
|
+
}
|
|
1495
|
+
function hasFlag(args, flag) {
|
|
1496
|
+
return args.includes(flag);
|
|
1497
|
+
}
|
|
1498
|
+
function hasCommandHelpFlag(args) {
|
|
1499
|
+
return hasFlag(args, "--help") || hasFlag(args, "-h");
|
|
1500
|
+
}
|
|
1501
|
+
function printVersion() {
|
|
1502
|
+
console.log(VERSION);
|
|
1503
|
+
}
|
|
1504
|
+
function printGeneralHelp() {
|
|
1505
|
+
console.log(`codex-native v${VERSION}
|
|
1506
|
+
|
|
1507
|
+
Usage:
|
|
1508
|
+
codex-native [options] [prompt]
|
|
1509
|
+
codex-native run [options] [prompt]
|
|
1510
|
+
codex-native tui [options] [prompt]
|
|
1511
|
+
|
|
1512
|
+
Default behavior:
|
|
1513
|
+
Running 'codex-native' without arguments launches the interactive TUI.
|
|
1514
|
+
Use 'codex-native run <prompt>' for non-interactive exec mode.
|
|
1515
|
+
|
|
1516
|
+
Commands:
|
|
1517
|
+
(default) Launch the interactive TUI (with optional initial prompt)
|
|
1518
|
+
run Run Codex in non-interactive exec mode
|
|
1519
|
+
tui Explicitly launch the interactive TUI
|
|
1520
|
+
reverie index Pre-compute reverie embeddings for the current repo
|
|
1521
|
+
|
|
1522
|
+
Global options:
|
|
1523
|
+
--config <path> Path to codex.config.js (or similar)
|
|
1524
|
+
--no-config Skip automatic config discovery
|
|
1525
|
+
--plugin <path> Additional plugin module (repeatable)
|
|
1526
|
+
|
|
1527
|
+
Run options:
|
|
1528
|
+
--model <slug> Model slug to use
|
|
1529
|
+
--oss Use the built-in OSS provider
|
|
1530
|
+
--sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
|
|
1531
|
+
--approval <policy> ${APPROVAL_CHOICES.join(" | ")}
|
|
1532
|
+
--schema <file> Path to final-output JSON schema
|
|
1533
|
+
--thread-id <id> Resume an existing thread
|
|
1534
|
+
--base-url <url> Override the Codex API base URL
|
|
1535
|
+
--api-key <key> API key for Codex requests
|
|
1536
|
+
--linux-sandbox-path Path to codex-linux-sandbox binary
|
|
1537
|
+
--full-auto Enable workspace-write auto approvals
|
|
1538
|
+
--skip-git-repo-check Skip git repository validation
|
|
1539
|
+
--cd <path> Working directory for the run
|
|
1540
|
+
--image <path> Attach an image (repeatable)
|
|
1541
|
+
--review-mode Enable review mode
|
|
1542
|
+
--review-hint <text> Hint text for review mode
|
|
1543
|
+
|
|
1544
|
+
TUI options:
|
|
1545
|
+
--model <slug> Model slug to use
|
|
1546
|
+
--oss Use the built-in OSS provider
|
|
1547
|
+
--sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
|
|
1548
|
+
--approval <policy> ${APPROVAL_CHOICES.join(" | ")}
|
|
1549
|
+
--resume <id> Resume a saved session by id
|
|
1550
|
+
--resume-last Resume the most recent saved session
|
|
1551
|
+
--resume-picker Show the resume picker on startup
|
|
1552
|
+
--full-auto Enable workspace-write auto approvals
|
|
1553
|
+
--dangerously-bypass-approvals-and-sandbox
|
|
1554
|
+
Disable approvals and sandboxing (unsafe)
|
|
1555
|
+
--cd <path> Working directory for the session
|
|
1556
|
+
--config-profile <name> Config profile to activate
|
|
1557
|
+
--config-overrides <kv> Config overrides (key=value, repeatable)
|
|
1558
|
+
--add-dir <path> Additional writable directory (repeatable)
|
|
1559
|
+
--image <path> Attach an image (repeatable)
|
|
1560
|
+
--web-search Enable web search tool
|
|
1561
|
+
--linux-sandbox-path Path to codex-linux-sandbox binary
|
|
1562
|
+
--base-url <url> Override the Codex API base URL
|
|
1563
|
+
--api-key <key> API key for Codex requests
|
|
1564
|
+
`);
|
|
1565
|
+
}
|
|
1566
|
+
function printCommandHelp(command) {
|
|
1567
|
+
if (command === "reverie-index") {
|
|
1568
|
+
console.log(`codex-native reverie index [options]
|
|
1569
|
+
|
|
1570
|
+
Options:
|
|
1571
|
+
--codex-home <path> Override CODEX_HOME (defaults to ~/.codex)
|
|
1572
|
+
--project-root <path> Project root for scoping + embedding cache (default: cwd)
|
|
1573
|
+
--limit <n> Maximum conversations to index (default: 10)
|
|
1574
|
+
--max-candidates <n> Scan window before filtering (default: 80)
|
|
1575
|
+
--batch-size <n> Batch size forwarded to FastEmbed
|
|
1576
|
+
--normalize Force vector normalization (default: embed config)
|
|
1577
|
+
--no-normalize Disable normalization
|
|
1578
|
+
--cache / --no-cache Override embedding cache behavior
|
|
1579
|
+
--embed-model <name> FastEmbed model (default: BAAI/bge-large-en-v1.5)
|
|
1580
|
+
--embed-cache-dir <dir> Cache directory (defaults to $CODEX_EMBED_CACHE or system tmp)
|
|
1581
|
+
--embed-max-length <n> Override FastEmbed max token length
|
|
1582
|
+
--no-progress Hide FastEmbed download progress
|
|
1583
|
+
--skip-embed-init Assume fastEmbedInit was already called in this process
|
|
1584
|
+
`);
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
if (command === "tui") {
|
|
1588
|
+
console.log(`codex-native tui [options] [prompt]
|
|
1589
|
+
|
|
1590
|
+
Options:
|
|
1591
|
+
--model <slug> Model slug to use
|
|
1592
|
+
--oss Use the built-in OSS provider
|
|
1593
|
+
--sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
|
|
1594
|
+
--approval <policy> ${APPROVAL_CHOICES.join(" | ")}
|
|
1595
|
+
--resume <id> Resume a saved session by id
|
|
1596
|
+
--resume-last Resume the most recent saved session
|
|
1597
|
+
--resume-picker Show the resume picker on startup
|
|
1598
|
+
--full-auto Enable workspace-write auto approvals
|
|
1599
|
+
--dangerously-bypass-approvals-and-sandbox
|
|
1600
|
+
Disable approvals and sandboxing (unsafe)
|
|
1601
|
+
--cd <path> Working directory for the session
|
|
1602
|
+
--config-profile <name> Config profile to activate
|
|
1603
|
+
--config-overrides <kv> Config overrides (key=value, repeatable)
|
|
1604
|
+
--add-dir <path> Additional writable directory (repeatable)
|
|
1605
|
+
--image <path> Attach an image (repeatable)
|
|
1606
|
+
--web-search Enable web search tool
|
|
1607
|
+
--linux-sandbox-path Path to codex-linux-sandbox binary
|
|
1608
|
+
--base-url <url> Override the Codex API base URL
|
|
1609
|
+
--api-key <key> API key for Codex requests
|
|
1610
|
+
`);
|
|
1611
|
+
} else {
|
|
1612
|
+
console.log(`codex-native run [options] [prompt]
|
|
1613
|
+
|
|
1614
|
+
Options:
|
|
1615
|
+
--model <slug> Model slug to use
|
|
1616
|
+
--oss Use the built-in OSS provider
|
|
1617
|
+
--sandbox <mode> ${SANDBOX_CHOICES.join(" | ")}
|
|
1618
|
+
--approval <policy> ${APPROVAL_CHOICES.join(" | ")}
|
|
1619
|
+
--schema <file> Path to final-output JSON schema
|
|
1620
|
+
--thread-id <id> Resume an existing thread
|
|
1621
|
+
--base-url <url> Override the Codex API base URL
|
|
1622
|
+
--api-key <key> API key for Codex requests
|
|
1623
|
+
--linux-sandbox-path Path to codex-linux-sandbox binary
|
|
1624
|
+
--full-auto Enable workspace-write auto approvals
|
|
1625
|
+
--skip-git-repo-check Skip git repository validation
|
|
1626
|
+
--cd <path> Working directory for the run
|
|
1627
|
+
--image <path> Attach an image (repeatable)
|
|
1628
|
+
--review-mode Enable review mode
|
|
1629
|
+
--review-hint <text> Hint text for review mode
|
|
1630
|
+
`);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
function validateOptionChoices(command, options) {
|
|
1634
|
+
const sandbox = options.sandbox;
|
|
1635
|
+
if (sandbox && !SANDBOX_CHOICES.includes(sandbox)) {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
`Invalid sandbox mode "${sandbox}". Valid modes: ${SANDBOX_CHOICES.join(", ")}.`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
const approval = options.approval;
|
|
1641
|
+
if (approval && !APPROVAL_CHOICES.includes(approval)) {
|
|
1642
|
+
throw new Error(
|
|
1643
|
+
`Invalid approval policy "${approval}". Valid policies: ${APPROVAL_CHOICES.join(", ")}.`
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
function logError(error) {
|
|
1648
|
+
if (error instanceof Error) {
|
|
1649
|
+
console.error(error.message);
|
|
1650
|
+
if (process4.env.CODEX_NATIVE_DEBUG) {
|
|
1651
|
+
console.error(error.stack);
|
|
1652
|
+
}
|
|
1653
|
+
} else {
|
|
1654
|
+
console.error(String(error));
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
main().catch((error) => {
|
|
1658
|
+
logError(error);
|
|
1659
|
+
process4.exitCode = 1;
|
|
1660
|
+
});
|
|
1661
|
+
//# sourceMappingURL=cli.mjs.map
|