codex-multi-auth 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +222 -98
- package/assets/readme-hero.svg +2 -2
- package/config/README.md +30 -87
- package/config/{opencode-legacy.json → codex-legacy.json} +571 -571
- package/config/{opencode-modern.json → codex-modern.json} +241 -239
- package/config/{minimal-opencode.json → minimal-codex.json} +15 -13
- package/config/schema/config.schema.json +21 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -20
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts.d.ts +1 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/auth/auth.d.ts +1 -1
- package/dist/lib/auth/auth.js +1 -1
- package/dist/lib/cli.d.ts +1 -1
- package/dist/lib/cli.js +4 -4
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/codex-manager/settings-hub.d.ts +6 -0
- package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub.js +1786 -0
- package/dist/lib/codex-manager/settings-hub.js.map +1 -0
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +5 -1637
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +20 -11
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/context-overflow.d.ts +1 -1
- package/dist/lib/context-overflow.js +1 -1
- package/dist/lib/oauth-success.html +1 -1
- package/dist/lib/prompts/codex-host-bridge.d.ts +19 -0
- package/dist/lib/prompts/codex-host-bridge.d.ts.map +1 -0
- package/dist/lib/prompts/{codex-opencode-bridge.js → codex-host-bridge.js} +143 -143
- package/dist/lib/prompts/codex-host-bridge.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts +2 -2
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +3 -3
- package/dist/lib/prompts/host-codex-prompt.d.ts +25 -0
- package/dist/lib/prompts/host-codex-prompt.d.ts.map +1 -0
- package/dist/lib/prompts/{opencode-codex.js → host-codex-prompt.js} +136 -47
- package/dist/lib/prompts/host-codex-prompt.js.map +1 -0
- package/dist/lib/recovery/storage.d.ts +2 -2
- package/dist/lib/recovery/storage.js +2 -2
- package/dist/lib/recovery/types.d.ts +1 -1
- package/dist/lib/recovery/types.js +1 -1
- package/dist/lib/recovery.d.ts +1 -1
- package/dist/lib/recovery.d.ts.map +1 -1
- package/dist/lib/recovery.js +1 -4
- package/dist/lib/recovery.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +3 -3
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +6 -2
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/input-utils.d.ts +2 -2
- package/dist/lib/request/helpers/input-utils.d.ts.map +1 -1
- package/dist/lib/request/helpers/input-utils.js +14 -14
- package/dist/lib/request/helpers/input-utils.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +2 -2
- package/dist/lib/request/helpers/model-map.js +2 -2
- package/dist/lib/request/request-transformer.d.ts +12 -12
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +23 -24
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/runtime-paths.d.ts +4 -4
- package/dist/lib/runtime-paths.d.ts.map +1 -1
- package/dist/lib/runtime-paths.js +27 -9
- package/dist/lib/runtime-paths.js.map +1 -1
- package/dist/lib/storage/paths.d.ts +11 -0
- package/dist/lib/storage/paths.d.ts.map +1 -1
- package/dist/lib/storage/paths.js +146 -2
- package/dist/lib/storage/paths.js.map +1 -1
- package/dist/lib/storage.d.ts +1 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +106 -32
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/tools/hashline-tools.d.ts +1 -1
- package/dist/lib/tools/hashline-tools.d.ts.map +1 -1
- package/dist/lib/tools/hashline-tools.js +1 -1
- package/dist/lib/tools/hashline-tools.js.map +1 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/ui/copy.d.ts +6 -6
- package/dist/lib/ui/copy.js +6 -6
- package/dist/lib/ui/copy.js.map +1 -1
- package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -0
- package/node_modules/@codex-ai/plugin/dist/index.js +2 -0
- package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -0
- package/node_modules/@codex-ai/plugin/dist/tool.js +29 -0
- package/node_modules/@codex-ai/plugin/package.json +9 -0
- package/package.json +30 -16
- package/scripts/bench-format/{opencode.mjs → codex-host.mjs} +206 -205
- package/scripts/bench-format/models.mjs +111 -105
- package/scripts/benchmark-edit-formats.mjs +1162 -1161
- package/scripts/codex.js +40 -2
- package/scripts/install-codex-auth-utils.js +49 -0
- package/scripts/{install-opencode-codex-auth.js → install-codex-auth.js} +220 -193
- package/scripts/repo-hygiene.js +320 -0
- package/scripts/test-model-matrix.js +475 -423
- package/vendor/codex-ai-plugin/dist/index.d.ts +2 -0
- package/vendor/codex-ai-plugin/dist/index.js +2 -0
- package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -0
- package/vendor/codex-ai-plugin/dist/tool.js +29 -0
- package/vendor/codex-ai-plugin/package.json +9 -0
- package/vendor/codex-ai-sdk/dist/index.d.ts +4 -0
- package/vendor/codex-ai-sdk/dist/index.js +2 -0
- package/vendor/codex-ai-sdk/package.json +8 -0
- package/assets/opencode-logo-ornate-dark.svg +0 -18
- package/dist/lib/prompts/codex-opencode-bridge.d.ts +0 -19
- package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +0 -1
- package/dist/lib/prompts/codex-opencode-bridge.js.map +0 -1
- package/dist/lib/prompts/opencode-codex.d.ts +0 -25
- package/dist/lib/prompts/opencode-codex.d.ts.map +0 -1
- package/dist/lib/prompts/opencode-codex.js.map +0 -1
|
@@ -1,205 +1,206 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const repoRoot = resolve(scriptDir, "..", "..");
|
|
7
|
-
|
|
8
|
-
export function getRepoRoot() {
|
|
9
|
-
return repoRoot;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function
|
|
13
|
-
const envOverride = process.env.
|
|
14
|
-
if (envOverride && envOverride.trim().length > 0) {
|
|
15
|
-
const command = envOverride.trim();
|
|
16
|
-
return { command, shell: /\.cmd$/i.test(command) };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (process.platform !== "win32") {
|
|
20
|
-
return { command: "
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const whereResult = spawnSync("where", ["
|
|
24
|
-
encoding: "utf8",
|
|
25
|
-
windowsHide: true,
|
|
26
|
-
});
|
|
27
|
-
const candidates = `${whereResult.stdout ?? ""}
|
|
28
|
-
.split(/\r?\n/)
|
|
29
|
-
.map((line) => line.trim())
|
|
30
|
-
.filter(
|
|
31
|
-
|
|
32
|
-
if (candidates.length === 0) {
|
|
33
|
-
return { command: "
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const exactExe = candidates.find((candidate) => /npm\\
|
|
37
|
-
if (exactExe) {
|
|
38
|
-
return { command: exactExe, shell: false };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const exactCmd = candidates.find((candidate) => /npm\\
|
|
42
|
-
if (exactCmd) {
|
|
43
|
-
return { command: exactCmd, shell: true };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const anyCmd = candidates.find((candidate) => /\.cmd$/i.test(candidate));
|
|
47
|
-
if (anyCmd) {
|
|
48
|
-
return { command: anyCmd, shell: true };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return { command: candidates[0], shell: false };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function parseNdjson(text) {
|
|
55
|
-
const events = [];
|
|
56
|
-
for (const line of text.split(/\r?\n/)) {
|
|
57
|
-
const trimmed = line.trim();
|
|
58
|
-
if (!trimmed) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
events.push(JSON.parse(trimmed));
|
|
63
|
-
} catch {
|
|
64
|
-
// Ignore non-JSON lines emitted by wrappers (bun install, warnings, etc.).
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return events;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function getToolEvents(events) {
|
|
71
|
-
return events
|
|
72
|
-
.filter((event) => event?.type === "tool_use" && event?.part?.type === "tool")
|
|
73
|
-
.map((event) => ({
|
|
74
|
-
tool: event.part.tool,
|
|
75
|
-
input: event.part.state?.input ?? {},
|
|
76
|
-
output: event.part.state?.output,
|
|
77
|
-
status: event.part.state?.status,
|
|
78
|
-
start: event.part.state?.time?.start,
|
|
79
|
-
end: event.part.state?.time?.end,
|
|
80
|
-
durationMs:
|
|
81
|
-
typeof event.part.state?.time?.start === "number" &&
|
|
82
|
-
typeof event.part.state?.time?.end === "number"
|
|
83
|
-
? event.part.state.time.end - event.part.state.time.start
|
|
84
|
-
: null,
|
|
85
|
-
}));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function getSessionDuration(events) {
|
|
89
|
-
const starts = events
|
|
90
|
-
.filter((event) => event?.type === "step_start" && typeof event.timestamp === "number")
|
|
91
|
-
.map((event) => event.timestamp);
|
|
92
|
-
const finishes = events
|
|
93
|
-
.filter((event) => event?.type === "step_finish" && typeof event.timestamp === "number")
|
|
94
|
-
.map((event) => event.timestamp);
|
|
95
|
-
if (starts.length === 0 || finishes.length === 0) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
return Math.max(...finishes) - Math.min(...starts);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function getTokenTotals(events) {
|
|
102
|
-
const stepFinishes = events.filter((event) => event?.type === "step_finish" && event?.part?.tokens);
|
|
103
|
-
if (stepFinishes.length === 0) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
const total = {
|
|
107
|
-
total: 0,
|
|
108
|
-
input: 0,
|
|
109
|
-
output: 0,
|
|
110
|
-
reasoning: 0,
|
|
111
|
-
cacheRead: 0,
|
|
112
|
-
cacheWrite: 0,
|
|
113
|
-
};
|
|
114
|
-
for (const event of stepFinishes) {
|
|
115
|
-
const tokens = event.part.tokens ?? {};
|
|
116
|
-
const input = Number(tokens.input ?? 0);
|
|
117
|
-
const output = Number(tokens.output ?? 0);
|
|
118
|
-
const reasoning = Number(tokens.reasoning ?? 0);
|
|
119
|
-
const explicitTotal = Number(tokens.total ?? NaN);
|
|
120
|
-
total.total += Number.isFinite(explicitTotal) ? explicitTotal : input + output + reasoning;
|
|
121
|
-
total.input += input;
|
|
122
|
-
total.output += output;
|
|
123
|
-
total.reasoning += reasoning;
|
|
124
|
-
total.cacheRead += Number(tokens.cache?.read ?? 0);
|
|
125
|
-
total.cacheWrite += Number(tokens.cache?.write ?? 0);
|
|
126
|
-
}
|
|
127
|
-
return total;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function getTextOutput(events) {
|
|
131
|
-
return events
|
|
132
|
-
.filter((event) => event?.type === "text" && typeof event?.part?.text === "string")
|
|
133
|
-
.map((event) => event.part.text)
|
|
134
|
-
.join("\n");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function getEventError(events) {
|
|
138
|
-
const errorEvent = events.find((event) => event?.type === "error");
|
|
139
|
-
if (!errorEvent) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
name: errorEvent.error?.name ?? "UnknownError",
|
|
144
|
-
message: errorEvent.error?.data?.message ?? errorEvent.error?.message ?? "Unknown error",
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function
|
|
149
|
-
executable,
|
|
150
|
-
prompt,
|
|
151
|
-
model,
|
|
152
|
-
variant,
|
|
153
|
-
agent,
|
|
154
|
-
cwd,
|
|
155
|
-
homeDir,
|
|
156
|
-
timeoutMs,
|
|
157
|
-
extraEnv,
|
|
158
|
-
}) {
|
|
159
|
-
const startWall = Date.now();
|
|
160
|
-
const args = ["run", "--format", "json", "--agent", agent, "--model", model];
|
|
161
|
-
if (variant) {
|
|
162
|
-
args.push("--variant", variant);
|
|
163
|
-
}
|
|
164
|
-
args.push(prompt);
|
|
165
|
-
|
|
166
|
-
const child = spawnSync(executable.command, args, {
|
|
167
|
-
cwd: cwd ?? repoRoot,
|
|
168
|
-
encoding: "utf8",
|
|
169
|
-
windowsHide: true,
|
|
170
|
-
shell: executable.shell,
|
|
171
|
-
timeout: timeoutMs,
|
|
172
|
-
maxBuffer: 30 * 1024 * 1024,
|
|
173
|
-
env: {
|
|
174
|
-
...process.env,
|
|
175
|
-
...(homeDir ? { HOME: homeDir, USERPROFILE: homeDir } : {}),
|
|
176
|
-
...extraEnv,
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const wallMs = Date.now() - startWall;
|
|
181
|
-
const stdout = child.stdout ?? "";
|
|
182
|
-
const stderr = child.stderr ?? "";
|
|
183
|
-
const events = parseNdjson(stdout);
|
|
184
|
-
const eventError = getEventError(events);
|
|
185
|
-
const timedOut =
|
|
186
|
-
child.error?.code === "ETIMEDOUT" ||
|
|
187
|
-
child.signal === "SIGTERM" ||
|
|
188
|
-
/timed out/i.test(String(child.error?.message ?? ""));
|
|
189
|
-
if (child.error && !timedOut) {
|
|
190
|
-
throw child.error;
|
|
191
|
-
}
|
|
192
|
-
const modelNotFound = /Model not found|ProviderModelNotFoundError/i.test(`${stdout}\n${stderr}`) || /Model not found/i.test(eventError?.message ?? "");
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
status: child.status ?? 1,
|
|
196
|
-
signal: child.signal ?? null,
|
|
197
|
-
stdout,
|
|
198
|
-
stderr,
|
|
199
|
-
wallMs,
|
|
200
|
-
events,
|
|
201
|
-
eventError,
|
|
202
|
-
timedOut,
|
|
203
|
-
modelNotFound,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const repoRoot = resolve(scriptDir, "..", "..");
|
|
7
|
+
|
|
8
|
+
export function getRepoRoot() {
|
|
9
|
+
return repoRoot;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveCodexExecutable() {
|
|
13
|
+
const envOverride = process.env.CODEX_BIN;
|
|
14
|
+
if (envOverride && envOverride.trim().length > 0) {
|
|
15
|
+
const command = envOverride.trim();
|
|
16
|
+
return { command, shell: /\.cmd$/i.test(command) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (process.platform !== "win32") {
|
|
20
|
+
return { command: "codex", shell: false };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const whereResult = spawnSync("where", ["Codex"], {
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
windowsHide: true,
|
|
26
|
+
});
|
|
27
|
+
const candidates = `${whereResult.stdout ?? ""}`
|
|
28
|
+
.split(/\r?\n/)
|
|
29
|
+
.map((line) => line.trim())
|
|
30
|
+
.filter((line) => /^[A-Za-z]:\\.+\.(exe|cmd)$/i.test(line));
|
|
31
|
+
|
|
32
|
+
if (candidates.length === 0) {
|
|
33
|
+
return { command: "codex", shell: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const exactExe = candidates.find((candidate) => /npm\\Codex\.exe$/i.test(candidate));
|
|
37
|
+
if (exactExe) {
|
|
38
|
+
return { command: exactExe, shell: false };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const exactCmd = candidates.find((candidate) => /npm\\Codex\.cmd$/i.test(candidate));
|
|
42
|
+
if (exactCmd) {
|
|
43
|
+
return { command: exactCmd, shell: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const anyCmd = candidates.find((candidate) => /\.cmd$/i.test(candidate));
|
|
47
|
+
if (anyCmd) {
|
|
48
|
+
return { command: anyCmd, shell: true };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { command: candidates[0], shell: false };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseNdjson(text) {
|
|
55
|
+
const events = [];
|
|
56
|
+
for (const line of text.split(/\r?\n/)) {
|
|
57
|
+
const trimmed = line.trim();
|
|
58
|
+
if (!trimmed) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
events.push(JSON.parse(trimmed));
|
|
63
|
+
} catch {
|
|
64
|
+
// Ignore non-JSON lines emitted by wrappers (bun install, warnings, etc.).
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return events;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getToolEvents(events) {
|
|
71
|
+
return events
|
|
72
|
+
.filter((event) => event?.type === "tool_use" && event?.part?.type === "tool")
|
|
73
|
+
.map((event) => ({
|
|
74
|
+
tool: event.part.tool,
|
|
75
|
+
input: event.part.state?.input ?? {},
|
|
76
|
+
output: event.part.state?.output,
|
|
77
|
+
status: event.part.state?.status,
|
|
78
|
+
start: event.part.state?.time?.start,
|
|
79
|
+
end: event.part.state?.time?.end,
|
|
80
|
+
durationMs:
|
|
81
|
+
typeof event.part.state?.time?.start === "number" &&
|
|
82
|
+
typeof event.part.state?.time?.end === "number"
|
|
83
|
+
? event.part.state.time.end - event.part.state.time.start
|
|
84
|
+
: null,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getSessionDuration(events) {
|
|
89
|
+
const starts = events
|
|
90
|
+
.filter((event) => event?.type === "step_start" && typeof event.timestamp === "number")
|
|
91
|
+
.map((event) => event.timestamp);
|
|
92
|
+
const finishes = events
|
|
93
|
+
.filter((event) => event?.type === "step_finish" && typeof event.timestamp === "number")
|
|
94
|
+
.map((event) => event.timestamp);
|
|
95
|
+
if (starts.length === 0 || finishes.length === 0) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return Math.max(...finishes) - Math.min(...starts);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getTokenTotals(events) {
|
|
102
|
+
const stepFinishes = events.filter((event) => event?.type === "step_finish" && event?.part?.tokens);
|
|
103
|
+
if (stepFinishes.length === 0) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const total = {
|
|
107
|
+
total: 0,
|
|
108
|
+
input: 0,
|
|
109
|
+
output: 0,
|
|
110
|
+
reasoning: 0,
|
|
111
|
+
cacheRead: 0,
|
|
112
|
+
cacheWrite: 0,
|
|
113
|
+
};
|
|
114
|
+
for (const event of stepFinishes) {
|
|
115
|
+
const tokens = event.part.tokens ?? {};
|
|
116
|
+
const input = Number(tokens.input ?? 0);
|
|
117
|
+
const output = Number(tokens.output ?? 0);
|
|
118
|
+
const reasoning = Number(tokens.reasoning ?? 0);
|
|
119
|
+
const explicitTotal = Number(tokens.total ?? NaN);
|
|
120
|
+
total.total += Number.isFinite(explicitTotal) ? explicitTotal : input + output + reasoning;
|
|
121
|
+
total.input += input;
|
|
122
|
+
total.output += output;
|
|
123
|
+
total.reasoning += reasoning;
|
|
124
|
+
total.cacheRead += Number(tokens.cache?.read ?? 0);
|
|
125
|
+
total.cacheWrite += Number(tokens.cache?.write ?? 0);
|
|
126
|
+
}
|
|
127
|
+
return total;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getTextOutput(events) {
|
|
131
|
+
return events
|
|
132
|
+
.filter((event) => event?.type === "text" && typeof event?.part?.text === "string")
|
|
133
|
+
.map((event) => event.part.text)
|
|
134
|
+
.join("\n");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function getEventError(events) {
|
|
138
|
+
const errorEvent = events.find((event) => event?.type === "error");
|
|
139
|
+
if (!errorEvent) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
name: errorEvent.error?.name ?? "UnknownError",
|
|
144
|
+
message: errorEvent.error?.data?.message ?? errorEvent.error?.message ?? "Unknown error",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function runCodexJson({
|
|
149
|
+
executable,
|
|
150
|
+
prompt,
|
|
151
|
+
model,
|
|
152
|
+
variant,
|
|
153
|
+
agent,
|
|
154
|
+
cwd,
|
|
155
|
+
homeDir,
|
|
156
|
+
timeoutMs,
|
|
157
|
+
extraEnv,
|
|
158
|
+
}) {
|
|
159
|
+
const startWall = Date.now();
|
|
160
|
+
const args = ["run", "--format", "json", "--agent", agent, "--model", model];
|
|
161
|
+
if (variant) {
|
|
162
|
+
args.push("--variant", variant);
|
|
163
|
+
}
|
|
164
|
+
args.push(prompt);
|
|
165
|
+
|
|
166
|
+
const child = spawnSync(executable.command, args, {
|
|
167
|
+
cwd: cwd ?? repoRoot,
|
|
168
|
+
encoding: "utf8",
|
|
169
|
+
windowsHide: true,
|
|
170
|
+
shell: executable.shell,
|
|
171
|
+
timeout: timeoutMs,
|
|
172
|
+
maxBuffer: 30 * 1024 * 1024,
|
|
173
|
+
env: {
|
|
174
|
+
...process.env,
|
|
175
|
+
...(homeDir ? { HOME: homeDir, USERPROFILE: homeDir } : {}),
|
|
176
|
+
...extraEnv,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const wallMs = Date.now() - startWall;
|
|
181
|
+
const stdout = child.stdout ?? "";
|
|
182
|
+
const stderr = child.stderr ?? "";
|
|
183
|
+
const events = parseNdjson(stdout);
|
|
184
|
+
const eventError = getEventError(events);
|
|
185
|
+
const timedOut =
|
|
186
|
+
child.error?.code === "ETIMEDOUT" ||
|
|
187
|
+
child.signal === "SIGTERM" ||
|
|
188
|
+
/timed out/i.test(String(child.error?.message ?? ""));
|
|
189
|
+
if (child.error && !timedOut) {
|
|
190
|
+
throw child.error;
|
|
191
|
+
}
|
|
192
|
+
const modelNotFound = /Model not found|ProviderModelNotFoundError/i.test(`${stdout}\n${stderr}`) || /Model not found/i.test(eventError?.message ?? "");
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
status: child.status ?? 1,
|
|
196
|
+
signal: child.signal ?? null,
|
|
197
|
+
stdout,
|
|
198
|
+
stderr,
|
|
199
|
+
wallMs,
|
|
200
|
+
events,
|
|
201
|
+
eventError,
|
|
202
|
+
timedOut,
|
|
203
|
+
modelNotFound,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
@@ -1,105 +1,111 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const FALLBACK_OPENAI_CODEX_STABLE = [
|
|
5
|
-
"openai/gpt-5-codex",
|
|
6
|
-
"openai/gpt-5.1-codex",
|
|
7
|
-
"openai/gpt-5.1-codex-mini",
|
|
8
|
-
"openai/gpt-5.1-codex-max",
|
|
9
|
-
"openai/gpt-5.2-codex",
|
|
10
|
-
"openai/gpt-5.3-codex",
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
const OPENAI_CODEX_PREFIXES = ["openai/", "openai-multi/"];
|
|
14
|
-
|
|
15
|
-
export function isOpenAiCodexProviderPrefix(modelId) {
|
|
16
|
-
return OPENAI_CODEX_PREFIXES.some((prefix) => modelId.startsWith(prefix));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function codexTail(modelId) {
|
|
20
|
-
const slash = modelId.indexOf("/");
|
|
21
|
-
return slash >= 0 ? modelId.slice(slash + 1) : modelId;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function canonicalCodexModelId(modelId) {
|
|
25
|
-
return `openai/${codexTail(modelId)}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function aliasCandidatesForCodexModel(modelId) {
|
|
29
|
-
if (!isOpenAiCodexProviderPrefix(modelId)) {
|
|
30
|
-
return [modelId];
|
|
31
|
-
}
|
|
32
|
-
const tail = codexTail(modelId);
|
|
33
|
-
const candidates = [`openai/${tail}`, `openai-multi/${tail}`];
|
|
34
|
-
return [...new Set([modelId, ...candidates])];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function isStableOpenAiCodexModel(modelId) {
|
|
38
|
-
if (!isOpenAiCodexProviderPrefix(modelId)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
if (!modelId.includes("codex")) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
if (modelId.includes("-spark") || modelId.includes("-latest")) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function
|
|
51
|
-
const executable =
|
|
52
|
-
const child = spawnSync(executable.command, ["models"], {
|
|
53
|
-
encoding: "utf8",
|
|
54
|
-
windowsHide: true,
|
|
55
|
-
shell: executable.shell,
|
|
56
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (existing
|
|
80
|
-
byCanonical.set(canonical, modelId);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { resolveCodexExecutable } from "./codex-host.mjs";
|
|
3
|
+
|
|
4
|
+
const FALLBACK_OPENAI_CODEX_STABLE = [
|
|
5
|
+
"openai/gpt-5-codex",
|
|
6
|
+
"openai/gpt-5.1-codex",
|
|
7
|
+
"openai/gpt-5.1-codex-mini",
|
|
8
|
+
"openai/gpt-5.1-codex-max",
|
|
9
|
+
"openai/gpt-5.2-codex",
|
|
10
|
+
"openai/gpt-5.3-codex",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const OPENAI_CODEX_PREFIXES = ["openai/", "openai-multi/"];
|
|
14
|
+
|
|
15
|
+
export function isOpenAiCodexProviderPrefix(modelId) {
|
|
16
|
+
return OPENAI_CODEX_PREFIXES.some((prefix) => modelId.startsWith(prefix));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function codexTail(modelId) {
|
|
20
|
+
const slash = modelId.indexOf("/");
|
|
21
|
+
return slash >= 0 ? modelId.slice(slash + 1) : modelId;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function canonicalCodexModelId(modelId) {
|
|
25
|
+
return `openai/${codexTail(modelId)}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function aliasCandidatesForCodexModel(modelId) {
|
|
29
|
+
if (!isOpenAiCodexProviderPrefix(modelId)) {
|
|
30
|
+
return [modelId];
|
|
31
|
+
}
|
|
32
|
+
const tail = codexTail(modelId);
|
|
33
|
+
const candidates = [`openai/${tail}`, `openai-multi/${tail}`];
|
|
34
|
+
return [...new Set([modelId, ...candidates])];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isStableOpenAiCodexModel(modelId) {
|
|
38
|
+
if (!isOpenAiCodexProviderPrefix(modelId)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (!modelId.includes("codex")) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (modelId.includes("-spark") || modelId.includes("-latest")) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function listCodexModels() {
|
|
51
|
+
const executable = resolveCodexExecutable();
|
|
52
|
+
const child = spawnSync(executable.command, ["models"], {
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
windowsHide: true,
|
|
55
|
+
shell: executable.shell,
|
|
56
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
57
|
+
timeout: Number.parseInt(process.env.CODEX_MODELS_TIMEOUT_MS ?? "30000", 10),
|
|
58
|
+
killSignal: "SIGKILL",
|
|
59
|
+
});
|
|
60
|
+
if (child.error && child.error.code === "ETIMEDOUT") {
|
|
61
|
+
throw new Error(`Timed out while listing Codex models after ${process.env.CODEX_MODELS_TIMEOUT_MS ?? "30000"}ms`);
|
|
62
|
+
}
|
|
63
|
+
const text = `${child.stdout ?? ""}\n${child.stderr ?? ""}`;
|
|
64
|
+
if ((child.status ?? 1) !== 0) {
|
|
65
|
+
throw new Error(`Failed to list Codex models (exit=${child.status ?? 1})`);
|
|
66
|
+
}
|
|
67
|
+
return text
|
|
68
|
+
.split(/\r?\n/)
|
|
69
|
+
.map((line) => line.trim())
|
|
70
|
+
.filter((line) => line.length > 0)
|
|
71
|
+
.filter((line) => !line.startsWith("Using Node "));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function dedupeCodexModels(models) {
|
|
75
|
+
const byCanonical = new Map();
|
|
76
|
+
for (const modelId of models) {
|
|
77
|
+
const canonical = canonicalCodexModelId(modelId);
|
|
78
|
+
const existing = byCanonical.get(canonical);
|
|
79
|
+
if (!existing) {
|
|
80
|
+
byCanonical.set(canonical, modelId);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Prefer the shorter/default provider form when both exist.
|
|
84
|
+
if (existing.startsWith("openai-multi/") && modelId.startsWith("openai/")) {
|
|
85
|
+
byCanonical.set(canonical, modelId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return [...byCanonical.values()].sort((a, b) => canonicalCodexModelId(a).localeCompare(canonicalCodexModelId(b)));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resolveModelPreset(presetName, explicitModels) {
|
|
92
|
+
if (explicitModels && explicitModels.length > 0) {
|
|
93
|
+
return explicitModels;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (presetName !== "codex-core") {
|
|
97
|
+
throw new Error(`Unsupported preset: ${presetName}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const models = dedupeCodexModels(listCodexModels().filter(isStableOpenAiCodexModel));
|
|
102
|
+
if (models.length > 0) {
|
|
103
|
+
return models;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// Fall back to static list.
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [...FALLBACK_OPENAI_CODEX_STABLE];
|
|
110
|
+
}
|
|
111
|
+
|