codex-multi-auth 1.3.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -26
- package/config/README.md +3 -3
- package/dist/lib/codex-manager/commands/rotation.d.ts +22 -0
- package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/rotation.js +287 -0
- package/dist/lib/codex-manager/commands/rotation.js.map +1 -0
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/status.js +38 -7
- package/dist/lib/codex-manager/commands/status.js.map +1 -1
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +1 -0
- package/dist/lib/codex-manager/help.js.map +1 -1
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +59 -1
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +9 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +2 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/fs-retry.d.ts +7 -0
- package/dist/lib/fs-retry.d.ts.map +1 -0
- package/dist/lib/fs-retry.js +35 -0
- package/dist/lib/fs-retry.js.map +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/runtime/app-bind.d.ts +74 -0
- package/dist/lib/runtime/app-bind.d.ts.map +1 -0
- package/dist/lib/runtime/app-bind.js +620 -0
- package/dist/lib/runtime/app-bind.js.map +1 -0
- package/dist/lib/runtime/config-toml.d.ts +10 -0
- package/dist/lib/runtime/config-toml.d.ts.map +1 -0
- package/dist/lib/runtime/config-toml.js +128 -0
- package/dist/lib/runtime/config-toml.js.map +1 -0
- package/dist/lib/runtime/runtime-observability.d.ts +5 -0
- package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-observability.js +5 -0
- package/dist/lib/runtime/runtime-observability.js.map +1 -1
- package/dist/lib/runtime-constants.d.ts +3 -0
- package/dist/lib/runtime-constants.d.ts.map +1 -0
- package/dist/lib/runtime-constants.js +3 -0
- package/dist/lib/runtime-constants.js.map +1 -0
- package/dist/lib/runtime-rotation-proxy.d.ts +36 -0
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -0
- package/dist/lib/runtime-rotation-proxy.js +828 -0
- package/dist/lib/runtime-rotation-proxy.js.map +1 -0
- package/dist/lib/schemas.d.ts +1 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +1 -0
- package/dist/lib/schemas.js.map +1 -1
- package/package.json +13 -11
- package/scripts/bench-format/codex-host.mjs +142 -73
- package/scripts/bench-format/models.mjs +17 -16
- package/scripts/benchmark-edit-formats.mjs +8 -6
- package/scripts/check-pack-budget-lib.js +49 -6
- package/scripts/codex-app-launcher.js +653 -0
- package/scripts/codex-app-router.js +322 -0
- package/scripts/codex-multi-auth.js +3 -1
- package/scripts/codex-routing.js +6 -0
- package/scripts/codex.js +3390 -1570
- package/scripts/postinstall.js +237 -0
package/scripts/codex.js
CHANGED
|
@@ -1,255 +1,419 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (!
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
5
|
+
import {
|
|
6
|
+
chmodSync,
|
|
7
|
+
copyFileSync,
|
|
8
|
+
cpSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
linkSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
mkdtempSync,
|
|
13
|
+
readdirSync,
|
|
14
|
+
renameSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
rmSync,
|
|
17
|
+
statSync,
|
|
18
|
+
symlinkSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { createRequire } from "node:module";
|
|
22
|
+
import { homedir, tmpdir } from "node:os";
|
|
23
|
+
import { basename, delimiter, dirname, join, resolve as resolvePath } from "node:path";
|
|
24
|
+
import process from "node:process";
|
|
25
|
+
import { StringDecoder } from "node:string_decoder";
|
|
26
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27
|
+
import {
|
|
28
|
+
resolveRealCodexBin as resolveRealCodexBinFromEnvironment,
|
|
29
|
+
splitPathEntries,
|
|
30
|
+
} from "./codex-bin-resolver.js";
|
|
31
|
+
import { normalizeAuthAlias, shouldHandleMultiAuthAuth } from "./codex-routing.js";
|
|
32
|
+
|
|
33
|
+
const RETRYABLE_SHADOW_HOME_CLEANUP_CODES = new Set(["EBUSY", "EPERM", "ENOTEMPTY"]);
|
|
34
|
+
const SHADOW_HOME_CLEANUP_BACKOFF_MS = [20, 60, 120];
|
|
35
|
+
const SHADOW_HOME_ORPHAN_LOCK_STALE_AGE_MS = 2_000;
|
|
36
|
+
const SHADOW_HOME_SYNC_LOCK_WAIT_TIMEOUT_MS =
|
|
37
|
+
SHADOW_HOME_ORPHAN_LOCK_STALE_AGE_MS +
|
|
38
|
+
SHADOW_HOME_CLEANUP_BACKOFF_MS.reduce((total, value) => total + value, 0);
|
|
39
|
+
const SHADOW_HOME_STATE_FILES = ["auth.json", "accounts.json", ".codex-global-state.json"];
|
|
40
|
+
const RUNTIME_ROTATION_SHADOW_HOME_OMIT_STATE_FILES = new Set([
|
|
41
|
+
"auth.json",
|
|
42
|
+
"accounts.json",
|
|
43
|
+
]);
|
|
44
|
+
const SHADOW_HOME_STATE_FILE_SET = new Set(SHADOW_HOME_STATE_FILES);
|
|
45
|
+
const SHADOW_HOME_CONFIG_FILE = "config.toml";
|
|
46
|
+
const SHADOW_HOME_SYNC_LOCK_DIR = ".codex-multi-auth-shadow-sync.lock";
|
|
47
|
+
const SHADOW_HOME_SYNC_STATE_FILE = ".codex-multi-auth-shadow-sync-state.json";
|
|
48
|
+
const APP_SERVER_ACCOUNT_DISPLAY_NAME = "codex-multi-auth";
|
|
49
|
+
const RUNTIME_CONSTANTS = await loadRuntimeConstants();
|
|
50
|
+
const RUNTIME_ROTATION_PROXY_PROVIDER_ID =
|
|
51
|
+
RUNTIME_CONSTANTS.RUNTIME_ROTATION_PROXY_PROVIDER_ID;
|
|
52
|
+
const APP_SERVER_ACCOUNT_LABEL_ENV = "CODEX_MULTI_AUTH_APP_SERVER_ACCOUNT_LABEL";
|
|
53
|
+
const INTERNAL_RUNTIME_ROTATION_APP_HELPER_ARG =
|
|
54
|
+
"--codex-multi-auth-runtime-app-helper";
|
|
55
|
+
const APP_RUNTIME_HELPER_OWNER_PID_ENV =
|
|
56
|
+
"CODEX_MULTI_AUTH_APP_ROTATION_OWNER_PID";
|
|
57
|
+
const APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV =
|
|
58
|
+
"CODEX_MULTI_AUTH_REAL_CODEX_HOME";
|
|
59
|
+
const APP_RUNTIME_HELPER_STATUS_FILE =
|
|
60
|
+
RUNTIME_CONSTANTS.APP_RUNTIME_HELPER_STATUS_FILE;
|
|
61
|
+
const DEFAULT_APP_RUNTIME_HELPER_IDLE_MS = 12 * 60 * 60 * 1000;
|
|
62
|
+
const DEFAULT_APP_RUNTIME_HELPER_DETACH_GRACE_MS = 5_000;
|
|
63
|
+
const APP_RUNTIME_HELPER_LAUNCH_TIMEOUT_MS = 15_000;
|
|
64
|
+
const APP_SERVER_SHIM_DIR_NAME = "app-server-shims";
|
|
65
|
+
const APP_SERVER_SHIM_HELPER_PREFIX = "helper-";
|
|
66
|
+
let shadowHomeCleanupBusyFailuresRemaining = Number.parseInt(
|
|
67
|
+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_CLEANUP_BUSY_FAILURES ?? "0",
|
|
68
|
+
10,
|
|
69
|
+
);
|
|
70
|
+
let shadowHomeCleanupPreflightReadBusyFailuresRemaining = Number.parseInt(
|
|
71
|
+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_PREFLIGHT_READ_BUSY_FAILURES ?? "0",
|
|
72
|
+
10,
|
|
73
|
+
);
|
|
74
|
+
let shadowHomeSyncLockRecreateStaleCount = Number.parseInt(
|
|
75
|
+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_LOCK_RECREATE_STALE_COUNT ?? "0",
|
|
76
|
+
10,
|
|
77
|
+
);
|
|
78
|
+
let shadowHomeSyncMetadataBusyFailuresRemaining = Number.parseInt(
|
|
79
|
+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_SYNC_METADATA_BUSY_FAILURES ?? "0",
|
|
80
|
+
10,
|
|
81
|
+
);
|
|
82
|
+
let shadowHomeSyncLockOwnerWriteFailuresRemaining = Number.parseInt(
|
|
83
|
+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_LOCK_OWNER_WRITE_FAILURES ?? "0",
|
|
84
|
+
10,
|
|
85
|
+
);
|
|
86
|
+
const shadowHomeCleanupRetryMarkerDir =
|
|
87
|
+
(process.env.CODEX_MULTI_AUTH_TEST_SHADOW_RETRY_MARKER_DIR ?? "").trim();
|
|
88
|
+
let warnedInvalidRuntimeRotationProxyEnv = false;
|
|
89
|
+
let warnedPendingAccountReadIdOverflow = false;
|
|
90
|
+
|
|
91
|
+
async function loadRuntimeConstants() {
|
|
92
|
+
const fallback = {
|
|
93
|
+
RUNTIME_ROTATION_PROXY_PROVIDER_ID: `${APP_SERVER_ACCOUNT_DISPLAY_NAME}-runtime-proxy`,
|
|
94
|
+
APP_RUNTIME_HELPER_STATUS_FILE: "runtime-rotation-app-helper.json",
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
const mod = await import("../dist/lib/runtime-constants.js");
|
|
98
|
+
return {
|
|
99
|
+
RUNTIME_ROTATION_PROXY_PROVIDER_ID:
|
|
100
|
+
typeof mod.RUNTIME_ROTATION_PROXY_PROVIDER_ID === "string"
|
|
101
|
+
? mod.RUNTIME_ROTATION_PROXY_PROVIDER_ID
|
|
102
|
+
: fallback.RUNTIME_ROTATION_PROXY_PROVIDER_ID,
|
|
103
|
+
APP_RUNTIME_HELPER_STATUS_FILE:
|
|
104
|
+
typeof mod.APP_RUNTIME_HELPER_STATUS_FILE === "string"
|
|
105
|
+
? mod.APP_RUNTIME_HELPER_STATUS_FILE
|
|
106
|
+
: fallback.APP_RUNTIME_HELPER_STATUS_FILE,
|
|
107
|
+
};
|
|
108
|
+
} catch {
|
|
109
|
+
// Keep wrapper startup resilient when dist has not been built yet.
|
|
110
|
+
}
|
|
111
|
+
return fallback;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isRetryableShadowHomeCleanupError(error) {
|
|
115
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
116
|
+
return typeof code === "string" && RETRYABLE_SHADOW_HOME_CLEANUP_CODES.has(code);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function sleepSync(ms) {
|
|
120
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function removeDirectoryWithRetry(targetPath) {
|
|
127
|
+
for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
|
|
128
|
+
try {
|
|
129
|
+
maybeThrowSimulatedShadowHomeBusyError();
|
|
130
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
131
|
+
return;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (
|
|
134
|
+
!isRetryableShadowHomeCleanupError(error) ||
|
|
135
|
+
attempt === SHADOW_HOME_CLEANUP_BACKOFF_MS.length
|
|
136
|
+
) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function hydrateCliVersionEnv() {
|
|
145
|
+
try {
|
|
146
|
+
const require = createRequire(import.meta.url);
|
|
147
|
+
const pkg = require("../package.json");
|
|
148
|
+
const version = typeof pkg?.version === "string" ? pkg.version.trim() : "";
|
|
149
|
+
if (version.length > 0) {
|
|
150
|
+
process.env.CODEX_MULTI_AUTH_CLI_VERSION = version;
|
|
151
|
+
}
|
|
152
|
+
} catch {
|
|
153
|
+
// Best effort only.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isRotationEnableCommand(args) {
|
|
158
|
+
return args[0] === "auth" && args[1] === "rotation" && args[2] === "enable";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function shouldAutoInstallCodexAppLauncher(env = process.env) {
|
|
162
|
+
const override = (env.CODEX_MULTI_AUTH_APP_LAUNCHER_INSTALL ?? "1").trim().toLowerCase();
|
|
163
|
+
return !new Set(["0", "false", "no"]).has(override);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function maybeInstallCodexAppLauncherAfterRotationEnable(args, exitCode) {
|
|
167
|
+
if (exitCode !== 0 || !isRotationEnableCommand(args)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (!shouldAutoInstallCodexAppLauncher()) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const mod = await import("./codex-app-launcher.js");
|
|
175
|
+
if (typeof mod.installCodexAppLauncher !== "function") {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
await mod.installCodexAppLauncher({
|
|
179
|
+
log: (message) => console.error(`codex-multi-auth: ${message}`),
|
|
180
|
+
});
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(
|
|
183
|
+
`codex-multi-auth: could not route Codex app launchers: ${error instanceof Error ? error.message : String(error)}`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function loadRunCodexMultiAuthCli() {
|
|
189
|
+
try {
|
|
190
|
+
const mod = await import("../dist/lib/codex-manager.js");
|
|
191
|
+
if (typeof mod.runCodexMultiAuthCli !== "function") {
|
|
192
|
+
console.error(
|
|
193
|
+
"dist/lib/codex-manager.js is missing required export: runCodexMultiAuthCli",
|
|
194
|
+
);
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return mod.runCodexMultiAuthCli;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
200
|
+
console.error(
|
|
201
|
+
[
|
|
202
|
+
"codex-multi-auth auth commands require built runtime files, but dist output is missing.",
|
|
203
|
+
"Run: npm run build",
|
|
204
|
+
].join("\n"),
|
|
205
|
+
);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function loadRuntimeRotationProxyModule() {
|
|
213
|
+
try {
|
|
214
|
+
const mod = await import("../dist/lib/runtime-rotation-proxy.js");
|
|
215
|
+
if (typeof mod.startRuntimeRotationProxy !== "function") {
|
|
216
|
+
console.error(
|
|
217
|
+
"dist/lib/runtime-rotation-proxy.js is missing required export: startRuntimeRotationProxy",
|
|
218
|
+
);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return mod;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
224
|
+
console.error(
|
|
225
|
+
[
|
|
226
|
+
"codex-multi-auth runtime rotation proxy requires built runtime files, but dist output is missing.",
|
|
227
|
+
"Run: npm run build",
|
|
228
|
+
].join("\n"),
|
|
229
|
+
);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function loadRuntimeRotationConfigModule() {
|
|
237
|
+
try {
|
|
238
|
+
const mod = await import("../dist/lib/config.js");
|
|
239
|
+
if (
|
|
240
|
+
typeof mod.loadPluginConfig !== "function" ||
|
|
241
|
+
typeof mod.getCodexRuntimeRotationProxy !== "function"
|
|
242
|
+
) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
return mod;
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function loadRuntimeConfigTomlModule() {
|
|
255
|
+
try {
|
|
256
|
+
const mod = await import("../dist/lib/runtime/config-toml.js");
|
|
257
|
+
if (
|
|
258
|
+
typeof mod.rewriteConfigTomlForRuntimeRotationProvider !== "function" ||
|
|
259
|
+
typeof mod.tomlStringLiteral !== "function"
|
|
260
|
+
) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
return mod;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function autoSyncManagerActiveSelectionIfEnabled() {
|
|
273
|
+
const enabled = (process.env.CODEX_MULTI_AUTH_AUTO_SYNC_ON_STARTUP ?? "1").trim() !== "0";
|
|
274
|
+
if (!enabled) return;
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const mod = await import("../dist/lib/codex-manager.js");
|
|
278
|
+
if (typeof mod.autoSyncActiveAccountToCodex !== "function") {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
await mod.autoSyncActiveAccountToCodex();
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
284
|
+
// Non-auth command path should keep forwarding even if dist is missing.
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// Best effort only: never block official Codex startup on sync failure.
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function resolveRealCodexBin() {
|
|
292
|
+
const override = (process.env.CODEX_MULTI_AUTH_REAL_CODEX_BIN ?? "").trim();
|
|
293
|
+
if (override.length > 0) {
|
|
294
|
+
if (!existsSync(override)) {
|
|
295
|
+
console.error(
|
|
296
|
+
`CODEX_MULTI_AUTH_REAL_CODEX_BIN is set but missing: ${override}`,
|
|
297
|
+
);
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
return resolveRealCodexBinFromEnvironment({
|
|
301
|
+
moduleUrl: import.meta.url,
|
|
302
|
+
env: process.env,
|
|
303
|
+
existsSyncImpl: existsSync,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return resolveRealCodexBinFromEnvironment({ moduleUrl: import.meta.url });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const CHATGPT_CODEX_UNSUPPORTED_MODEL_PATTERN =
|
|
311
|
+
/model is not supported when using codex with a chatgpt account/i;
|
|
312
|
+
const NORMALIZED_UNSUPPORTED_MODEL_PATTERN =
|
|
313
|
+
/model ['"]([^'"]+)['"] is not currently available for this chatgpt account when using codex oauth/i;
|
|
314
|
+
const MODEL_ACCESS_DENIED_PATTERN =
|
|
315
|
+
/the model [`'"]([^`'"]+)[`'"] does not exist or you do not have access to it/i;
|
|
316
|
+
const DIRECT_UNSUPPORTED_MODEL_PATTERN =
|
|
317
|
+
/['"]([^'"]+)['"]\s+model is not supported when using codex with a chatgpt account/i;
|
|
318
|
+
const WRAPPER_UNSUPPORTED_MODEL_FALLBACK_CHAIN = {
|
|
319
|
+
"gpt-5.5": ["gpt-5.4"],
|
|
320
|
+
"gpt-5.5-pro": ["gpt-5.4"],
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
function canonicalizeRequestedModelName(model) {
|
|
324
|
+
if (typeof model !== "string") return "";
|
|
325
|
+
const normalizedModel = normalizeRequestedModel(model);
|
|
326
|
+
if (normalizedModel) {
|
|
327
|
+
return normalizedModel;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const stripped = stripProviderPrefix(model).trim().toLowerCase();
|
|
331
|
+
if (!stripped) {
|
|
332
|
+
return "";
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return stripped.replace(/-(none|minimal|low|medium|high|xhigh)$/i, "");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function extractUnsupportedModelFromOutput(output) {
|
|
339
|
+
if (typeof output !== "string" || output.length === 0) {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const directMatch = output.match(DIRECT_UNSUPPORTED_MODEL_PATTERN);
|
|
344
|
+
if (directMatch?.[1]) {
|
|
345
|
+
return canonicalizeRequestedModelName(directMatch[1]);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const normalizedMatch = output.match(NORMALIZED_UNSUPPORTED_MODEL_PATTERN);
|
|
349
|
+
if (normalizedMatch?.[1]) {
|
|
350
|
+
return canonicalizeRequestedModelName(normalizedMatch[1]);
|
|
351
|
+
}
|
|
352
|
+
const accessDeniedMatch = output.match(MODEL_ACCESS_DENIED_PATTERN);
|
|
353
|
+
if (accessDeniedMatch?.[1]) {
|
|
354
|
+
return canonicalizeRequestedModelName(accessDeniedMatch[1]);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function resolveUnsupportedModelRetryTarget(
|
|
361
|
+
requestedModel,
|
|
362
|
+
output,
|
|
363
|
+
attemptedModels = [],
|
|
364
|
+
) {
|
|
365
|
+
if (
|
|
366
|
+
typeof output !== "string" ||
|
|
367
|
+
output.length === 0 ||
|
|
368
|
+
(!CHATGPT_CODEX_UNSUPPORTED_MODEL_PATTERN.test(output) &&
|
|
369
|
+
!NORMALIZED_UNSUPPORTED_MODEL_PATTERN.test(output) &&
|
|
370
|
+
!MODEL_ACCESS_DENIED_PATTERN.test(output))
|
|
371
|
+
) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const attempted = new Set(
|
|
376
|
+
attemptedModels
|
|
377
|
+
.map((model) => canonicalizeRequestedModelName(model))
|
|
378
|
+
.filter(Boolean),
|
|
379
|
+
);
|
|
380
|
+
const normalizedRequestedModel = canonicalizeRequestedModelName(requestedModel);
|
|
381
|
+
const blockedModel =
|
|
382
|
+
extractUnsupportedModelFromOutput(output) ?? normalizedRequestedModel;
|
|
383
|
+
const fallbackChain =
|
|
384
|
+
WRAPPER_UNSUPPORTED_MODEL_FALLBACK_CHAIN[blockedModel] ??
|
|
385
|
+
(normalizedRequestedModel
|
|
386
|
+
? WRAPPER_UNSUPPORTED_MODEL_FALLBACK_CHAIN[normalizedRequestedModel]
|
|
387
|
+
: undefined) ??
|
|
388
|
+
[];
|
|
389
|
+
|
|
390
|
+
for (const fallbackModel of fallbackChain) {
|
|
391
|
+
if (fallbackModel !== blockedModel && !attempted.has(fallbackModel)) {
|
|
392
|
+
return fallbackModel;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
|
|
235
399
|
function replaceRequestedModel(args, nextModel) {
|
|
236
400
|
if (!nextModel) {
|
|
237
401
|
return [...args];
|
|
238
402
|
}
|
|
239
|
-
|
|
240
|
-
const nextArgs = [...args];
|
|
241
|
-
for (let i = 0; i < nextArgs.length; i += 1) {
|
|
242
|
-
const arg = nextArgs[i];
|
|
243
|
-
if (arg === "--model" && typeof nextArgs[i + 1] === "string") {
|
|
244
|
-
nextArgs[i + 1] = nextModel;
|
|
245
|
-
return nextArgs;
|
|
246
|
-
}
|
|
247
|
-
if (typeof arg === "string" && arg.startsWith("--model=")) {
|
|
248
|
-
nextArgs[i] = `--model=${nextModel}`;
|
|
249
|
-
return nextArgs;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
403
|
+
|
|
404
|
+
const nextArgs = [...args];
|
|
405
|
+
for (let i = 0; i < nextArgs.length; i += 1) {
|
|
406
|
+
const arg = nextArgs[i];
|
|
407
|
+
if (arg === "--model" && typeof nextArgs[i + 1] === "string") {
|
|
408
|
+
nextArgs[i + 1] = nextModel;
|
|
409
|
+
return nextArgs;
|
|
410
|
+
}
|
|
411
|
+
if (typeof arg === "string" && arg.startsWith("--model=")) {
|
|
412
|
+
nextArgs[i] = `--model=${nextModel}`;
|
|
413
|
+
return nextArgs;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
253
417
|
return nextArgs;
|
|
254
418
|
}
|
|
255
419
|
|
|
@@ -265,6 +429,187 @@ function shouldCaptureForwardedCodexOutput(env = process.env) {
|
|
|
265
429
|
return process.stdout.isTTY !== true || process.stderr.isTTY !== true;
|
|
266
430
|
}
|
|
267
431
|
|
|
432
|
+
function jsonRpcIdKey(id) {
|
|
433
|
+
if (
|
|
434
|
+
typeof id === "string" ||
|
|
435
|
+
typeof id === "number" ||
|
|
436
|
+
typeof id === "boolean" ||
|
|
437
|
+
id === null
|
|
438
|
+
) {
|
|
439
|
+
return `${typeof id}:${JSON.stringify(id)}`;
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function parseJsonObjectLine(line) {
|
|
445
|
+
const trimmed = line.trim();
|
|
446
|
+
if (!trimmed.startsWith("{")) {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const parsed = JSON.parse(trimmed);
|
|
451
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
452
|
+
? parsed
|
|
453
|
+
: null;
|
|
454
|
+
} catch {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function splitProtocolLineEnding(line) {
|
|
460
|
+
if (line.endsWith("\r\n")) {
|
|
461
|
+
return { body: line.slice(0, -2), lineEnding: "\r\n" };
|
|
462
|
+
}
|
|
463
|
+
if (line.endsWith("\n")) {
|
|
464
|
+
return { body: line.slice(0, -1), lineEnding: "\n" };
|
|
465
|
+
}
|
|
466
|
+
return { body: line, lineEnding: "" };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function createProtocolLineAccumulator(onLine) {
|
|
470
|
+
const decoder = new StringDecoder("utf8");
|
|
471
|
+
let buffer = "";
|
|
472
|
+
const drain = () => {
|
|
473
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
474
|
+
while (newlineIndex >= 0) {
|
|
475
|
+
const line = buffer.slice(0, newlineIndex + 1);
|
|
476
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
477
|
+
onLine(line);
|
|
478
|
+
newlineIndex = buffer.indexOf("\n");
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
write(chunk) {
|
|
484
|
+
buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);
|
|
485
|
+
drain();
|
|
486
|
+
},
|
|
487
|
+
end() {
|
|
488
|
+
buffer += decoder.end();
|
|
489
|
+
if (buffer.length > 0) {
|
|
490
|
+
onLine(buffer);
|
|
491
|
+
buffer = "";
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function createSyntheticAppServerAccountReadResult() {
|
|
498
|
+
return {
|
|
499
|
+
account: {
|
|
500
|
+
type: "chatgpt",
|
|
501
|
+
email: APP_SERVER_ACCOUNT_DISPLAY_NAME,
|
|
502
|
+
planType: "unknown",
|
|
503
|
+
},
|
|
504
|
+
requiresOpenaiAuth: false,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function createSyntheticAppServerAuthStatusResult() {
|
|
509
|
+
return {
|
|
510
|
+
authMethod: "apikey",
|
|
511
|
+
authToken: null,
|
|
512
|
+
requiresOpenaiAuth: false,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function createSyntheticAppServerRateLimitsResult() {
|
|
517
|
+
return {
|
|
518
|
+
rateLimits: {
|
|
519
|
+
limitId: null,
|
|
520
|
+
limitName: null,
|
|
521
|
+
primary: null,
|
|
522
|
+
secondary: null,
|
|
523
|
+
credits: null,
|
|
524
|
+
planType: null,
|
|
525
|
+
rateLimitReachedType: null,
|
|
526
|
+
},
|
|
527
|
+
rateLimitsByLimitId: null,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function createAppServerAccountReadProtocolProxy() {
|
|
532
|
+
const maxPendingAuthRequestIds = 4096;
|
|
533
|
+
const pendingAuthRequestMethodsById = new Map();
|
|
534
|
+
const inputLines = createProtocolLineAccumulator((line) => {
|
|
535
|
+
const { body } = splitProtocolLineEnding(line);
|
|
536
|
+
const message = parseJsonObjectLine(body);
|
|
537
|
+
if (
|
|
538
|
+
![
|
|
539
|
+
"account/read",
|
|
540
|
+
"account/rateLimits/read",
|
|
541
|
+
"getAuthStatus",
|
|
542
|
+
].includes(message?.method) ||
|
|
543
|
+
!Object.hasOwn(message, "id")
|
|
544
|
+
) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const key = jsonRpcIdKey(message.id);
|
|
548
|
+
if (key) {
|
|
549
|
+
if (pendingAuthRequestMethodsById.size >= maxPendingAuthRequestIds) {
|
|
550
|
+
pendingAuthRequestMethodsById.clear();
|
|
551
|
+
if (!warnedPendingAccountReadIdOverflow) {
|
|
552
|
+
warnedPendingAccountReadIdOverflow = true;
|
|
553
|
+
console.error(
|
|
554
|
+
"codex-multi-auth: cleared pending app-server auth request ids after exceeding the safety cap.",
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
pendingAuthRequestMethodsById.set(key, message.method);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
const outputLines = createProtocolLineAccumulator((line) => {
|
|
562
|
+
process.stdout.write(
|
|
563
|
+
rewriteAppServerAccountReadResponseLine(line, pendingAuthRequestMethodsById),
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
observeInput(chunk) {
|
|
569
|
+
inputLines.write(chunk);
|
|
570
|
+
},
|
|
571
|
+
flushInput() {
|
|
572
|
+
inputLines.end();
|
|
573
|
+
},
|
|
574
|
+
writeOutput(chunk) {
|
|
575
|
+
outputLines.write(chunk);
|
|
576
|
+
},
|
|
577
|
+
flushOutput() {
|
|
578
|
+
outputLines.end();
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function rewriteAppServerAccountReadResponseLine(line, pendingAuthRequestMethodsById) {
|
|
584
|
+
const { body, lineEnding } = splitProtocolLineEnding(line);
|
|
585
|
+
const message = parseJsonObjectLine(body);
|
|
586
|
+
if (!message || !Object.hasOwn(message, "id")) {
|
|
587
|
+
return line;
|
|
588
|
+
}
|
|
589
|
+
const key = jsonRpcIdKey(message.id);
|
|
590
|
+
if (!key || !pendingAuthRequestMethodsById.has(key)) {
|
|
591
|
+
return line;
|
|
592
|
+
}
|
|
593
|
+
const method = pendingAuthRequestMethodsById.get(key);
|
|
594
|
+
pendingAuthRequestMethodsById.delete(key);
|
|
595
|
+
const result =
|
|
596
|
+
method === "account/read"
|
|
597
|
+
? createSyntheticAppServerAccountReadResult()
|
|
598
|
+
: method === "account/rateLimits/read"
|
|
599
|
+
? createSyntheticAppServerRateLimitsResult()
|
|
600
|
+
: method === "getAuthStatus"
|
|
601
|
+
? createSyntheticAppServerAuthStatusResult()
|
|
602
|
+
: null;
|
|
603
|
+
if (!result) {
|
|
604
|
+
return line;
|
|
605
|
+
}
|
|
606
|
+
return `${JSON.stringify({
|
|
607
|
+
jsonrpc: typeof message.jsonrpc === "string" ? message.jsonrpc : "2.0",
|
|
608
|
+
id: message.id,
|
|
609
|
+
result,
|
|
610
|
+
})}${lineEnding}`;
|
|
611
|
+
}
|
|
612
|
+
|
|
268
613
|
function forwardToRealCodexOnce(
|
|
269
614
|
codexBin,
|
|
270
615
|
args,
|
|
@@ -277,15 +622,23 @@ function forwardToRealCodexOnce(
|
|
|
277
622
|
let stdout = "";
|
|
278
623
|
let stderr = "";
|
|
279
624
|
const captureOutput = options.captureOutput === true;
|
|
280
|
-
const
|
|
625
|
+
const proxyAppServerAccountRead =
|
|
626
|
+
options.proxyAppServerAccountRead === true;
|
|
627
|
+
const protocolProxy = proxyAppServerAccountRead
|
|
628
|
+
? createAppServerAccountReadProtocolProxy()
|
|
629
|
+
: null;
|
|
630
|
+
let cleanupProtocolProxy = () => {};
|
|
631
|
+
const finalize = async (exitCode) => {
|
|
281
632
|
if (settled) {
|
|
282
633
|
return;
|
|
283
634
|
}
|
|
284
|
-
settled = true;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
635
|
+
settled = true;
|
|
636
|
+
cleanupProtocolProxy();
|
|
637
|
+
protocolProxy?.flushOutput();
|
|
638
|
+
try {
|
|
639
|
+
await cleanup?.({ exitCode });
|
|
640
|
+
} catch {
|
|
641
|
+
// Best-effort cleanup only.
|
|
289
642
|
}
|
|
290
643
|
resolve({
|
|
291
644
|
exitCode,
|
|
@@ -306,7 +659,11 @@ function forwardToRealCodexOnce(
|
|
|
306
659
|
};
|
|
307
660
|
try {
|
|
308
661
|
child = spawn(command, commandArgs, {
|
|
309
|
-
stdio:
|
|
662
|
+
stdio: proxyAppServerAccountRead
|
|
663
|
+
? ["pipe", "pipe", "pipe"]
|
|
664
|
+
: captureOutput
|
|
665
|
+
? ["inherit", "pipe", "pipe"]
|
|
666
|
+
: "inherit",
|
|
310
667
|
env,
|
|
311
668
|
});
|
|
312
669
|
} catch (error) {
|
|
@@ -314,7 +671,61 @@ function forwardToRealCodexOnce(
|
|
|
314
671
|
return;
|
|
315
672
|
}
|
|
316
673
|
|
|
317
|
-
if (
|
|
674
|
+
if (proxyAppServerAccountRead && protocolProxy) {
|
|
675
|
+
let stdinClosed = false;
|
|
676
|
+
let stdoutClosed = false;
|
|
677
|
+
const closeChildStdin = () => {
|
|
678
|
+
if (stdinClosed) return;
|
|
679
|
+
stdinClosed = true;
|
|
680
|
+
protocolProxy.flushInput();
|
|
681
|
+
child.stdin?.end();
|
|
682
|
+
};
|
|
683
|
+
const onProcessStdinData = (chunk) => {
|
|
684
|
+
protocolProxy.observeInput(chunk);
|
|
685
|
+
if (child.stdin && !child.stdin.destroyed && !child.stdin.write(chunk)) {
|
|
686
|
+
process.stdin.pause();
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
const onProcessStdinEnd = () => {
|
|
690
|
+
closeChildStdin();
|
|
691
|
+
};
|
|
692
|
+
const onProcessStdinError = () => {
|
|
693
|
+
closeChildStdin();
|
|
694
|
+
};
|
|
695
|
+
const onChildStdinDrain = () => {
|
|
696
|
+
process.stdin.resume();
|
|
697
|
+
};
|
|
698
|
+
const onChildStdoutData = (chunk) => {
|
|
699
|
+
protocolProxy.writeOutput(chunk);
|
|
700
|
+
};
|
|
701
|
+
const onChildStdoutEnd = () => {
|
|
702
|
+
if (stdoutClosed) return;
|
|
703
|
+
stdoutClosed = true;
|
|
704
|
+
protocolProxy.flushOutput();
|
|
705
|
+
};
|
|
706
|
+
const onChildStderrData = (chunk) => {
|
|
707
|
+
process.stderr.write(chunk);
|
|
708
|
+
};
|
|
709
|
+
cleanupProtocolProxy = () => {
|
|
710
|
+
process.stdin.removeListener("data", onProcessStdinData);
|
|
711
|
+
process.stdin.removeListener("end", onProcessStdinEnd);
|
|
712
|
+
process.stdin.removeListener("close", onProcessStdinEnd);
|
|
713
|
+
process.stdin.removeListener("error", onProcessStdinError);
|
|
714
|
+
child.stdin?.removeListener("drain", onChildStdinDrain);
|
|
715
|
+
child.stdout?.removeListener("data", onChildStdoutData);
|
|
716
|
+
child.stdout?.removeListener("end", onChildStdoutEnd);
|
|
717
|
+
child.stderr?.removeListener("data", onChildStderrData);
|
|
718
|
+
process.stdin.resume();
|
|
719
|
+
};
|
|
720
|
+
process.stdin.on("data", onProcessStdinData);
|
|
721
|
+
process.stdin.once("end", onProcessStdinEnd);
|
|
722
|
+
process.stdin.once("close", onProcessStdinEnd);
|
|
723
|
+
process.stdin.once("error", onProcessStdinError);
|
|
724
|
+
child.stdin?.on("drain", onChildStdinDrain);
|
|
725
|
+
child.stdout?.on("data", onChildStdoutData);
|
|
726
|
+
child.stdout?.on("end", onChildStdoutEnd);
|
|
727
|
+
child.stderr?.on("data", onChildStderrData);
|
|
728
|
+
} else if (captureOutput) {
|
|
318
729
|
child.stdout?.on("data", (chunk) => {
|
|
319
730
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
320
731
|
stdout += text;
|
|
@@ -332,1322 +743,2731 @@ function forwardToRealCodexOnce(
|
|
|
332
743
|
});
|
|
333
744
|
|
|
334
745
|
child.once("close", (code, signal) => {
|
|
335
|
-
if (signal) {
|
|
336
|
-
const signalNumber = signal === "SIGINT" ? 130 : 1;
|
|
337
|
-
finalize(signalNumber);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
finalize(typeof code === "number" ? code : 1);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async function forwardToRealCodex(codexBin, rawArgs, baseEnv = process.env) {
|
|
346
|
-
let currentArgs = [...rawArgs];
|
|
347
|
-
let lastExitCode = 1;
|
|
348
|
-
const attemptedModels = new Set();
|
|
349
|
-
|
|
350
|
-
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
351
|
-
const requestedModel = extractRequestedModel(currentArgs);
|
|
352
|
-
if (requestedModel) {
|
|
353
|
-
attemptedModels.add(requestedModel);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const { args: forwardArgs, requestedModel: compatibilityRequestedModel } =
|
|
357
|
-
buildForwardArgs(currentArgs);
|
|
358
|
-
const compatibility = createCompatibilityCodexHome(
|
|
359
|
-
forwardArgs,
|
|
360
|
-
compatibilityRequestedModel,
|
|
361
|
-
baseEnv,
|
|
362
|
-
);
|
|
746
|
+
if (signal) {
|
|
747
|
+
const signalNumber = signal === "SIGINT" ? 130 : 1;
|
|
748
|
+
finalize(signalNumber);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
finalize(typeof code === "number" ? code : 1);
|
|
752
|
+
});
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
async function forwardToRealCodex(codexBin, rawArgs, baseEnv = process.env) {
|
|
757
|
+
let currentArgs = [...rawArgs];
|
|
758
|
+
let lastExitCode = 1;
|
|
759
|
+
const attemptedModels = new Set();
|
|
760
|
+
|
|
761
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
762
|
+
const requestedModel = extractRequestedModel(currentArgs);
|
|
763
|
+
if (requestedModel) {
|
|
764
|
+
attemptedModels.add(requestedModel);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const { args: forwardArgs, requestedModel: compatibilityRequestedModel } =
|
|
768
|
+
buildForwardArgs(currentArgs);
|
|
769
|
+
const compatibility = createCompatibilityCodexHome(
|
|
770
|
+
forwardArgs,
|
|
771
|
+
compatibilityRequestedModel,
|
|
772
|
+
baseEnv,
|
|
773
|
+
);
|
|
774
|
+
const runtimeProxyContext = await createRuntimeRotationProxyContextIfEnabled(
|
|
775
|
+
compatibility,
|
|
776
|
+
rawArgs,
|
|
777
|
+
);
|
|
363
778
|
const result = await forwardToRealCodexOnce(
|
|
364
779
|
codexBin,
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
780
|
+
runtimeProxyContext.args,
|
|
781
|
+
runtimeProxyContext.env,
|
|
782
|
+
runtimeProxyContext.cleanup,
|
|
368
783
|
{
|
|
369
|
-
captureOutput:
|
|
784
|
+
captureOutput: shouldCaptureForwardedOutputForArgs(
|
|
785
|
+
rawArgs,
|
|
786
|
+
runtimeProxyContext.env,
|
|
787
|
+
),
|
|
788
|
+
proxyAppServerAccountRead:
|
|
789
|
+
isCodexAppServerCommand(rawArgs) &&
|
|
790
|
+
(runtimeProxyContext.proxyAppServerAccountRead === true ||
|
|
791
|
+
(runtimeProxyContext.env[APP_SERVER_ACCOUNT_LABEL_ENV] ?? "").trim() ===
|
|
792
|
+
"1"),
|
|
370
793
|
},
|
|
371
794
|
);
|
|
372
|
-
lastExitCode = result.exitCode;
|
|
373
|
-
if (result.exitCode === 0) {
|
|
374
|
-
return result.exitCode;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const fallbackModel = resolveUnsupportedModelRetryTarget(
|
|
378
|
-
requestedModel,
|
|
379
|
-
result.output,
|
|
380
|
-
[...attemptedModels],
|
|
381
|
-
);
|
|
382
|
-
if (!fallbackModel) {
|
|
383
|
-
return result.exitCode;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
console.error(
|
|
387
|
-
`codex-multi-auth: model ${requestedModel ?? "requested model"} is unsupported on this ChatGPT Codex surface. Retrying with ${fallbackModel}.`,
|
|
388
|
-
);
|
|
389
|
-
attemptedModels.add(fallbackModel);
|
|
390
|
-
const nextArgs = replaceRequestedModel(currentArgs, fallbackModel);
|
|
391
|
-
if (JSON.stringify(nextArgs) === JSON.stringify(currentArgs)) {
|
|
392
|
-
return result.exitCode;
|
|
393
|
-
}
|
|
394
|
-
currentArgs = nextArgs;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return lastExitCode;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function hasCliAuthCredentialsStoreOverride(args) {
|
|
401
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
402
|
-
const arg = args[i];
|
|
403
|
-
if (arg === "-c" || arg === "--config") {
|
|
404
|
-
const next = args[i + 1];
|
|
405
|
-
if (!next || !next.includes("=")) continue;
|
|
406
|
-
const [key] = next.split("=", 1);
|
|
407
|
-
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
408
|
-
return true;
|
|
409
|
-
}
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
if (typeof arg === "string" && arg.startsWith("--config=")) {
|
|
413
|
-
const assignment = arg.slice("--config=".length);
|
|
414
|
-
const [key] = assignment.split("=", 1);
|
|
415
|
-
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
416
|
-
return true;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return false;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// IMPORTANT: Keep this mapping in sync with
|
|
424
|
-
// `lib/request/helpers/model-map.ts` `MODEL_PROFILES`
|
|
425
|
-
// and its GPT-5 normalization helpers.
|
|
426
|
-
// This wrapper runs before the TypeScript build, so it cannot import that source.
|
|
427
|
-
const SUPPORTED_REASONING_EFFORTS_BY_MODEL = {
|
|
428
|
-
"gpt-5-codex": ["low", "medium", "high", "xhigh"],
|
|
429
|
-
"gpt-5.1-codex-max": ["medium", "high", "xhigh"],
|
|
430
|
-
"gpt-5.1-codex-mini": ["medium", "high"],
|
|
431
|
-
"gpt-5.5": ["none", "low", "medium", "high", "xhigh"],
|
|
432
|
-
"gpt-5.5-pro": ["medium", "high", "xhigh"],
|
|
433
|
-
"gpt-5.4": ["none", "low", "medium", "high", "xhigh"],
|
|
434
|
-
"gpt-5.4-pro": ["medium", "high", "xhigh"],
|
|
435
|
-
"gpt-5.2-pro": ["medium", "high", "xhigh"],
|
|
436
|
-
"gpt-5-pro": ["high"],
|
|
437
|
-
"gpt-5.2": ["none", "low", "medium", "high", "xhigh"],
|
|
438
|
-
"gpt-5.1": ["none", "low", "medium", "high"],
|
|
439
|
-
"gpt-5": ["minimal", "low", "medium", "high"],
|
|
440
|
-
"gpt-5-mini": ["medium"],
|
|
441
|
-
"gpt-5-nano": ["medium"],
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
const REASONING_FALLBACKS = {
|
|
445
|
-
none: ["none", "low", "minimal", "medium", "high", "xhigh"],
|
|
446
|
-
minimal: ["minimal", "low", "none", "medium", "high", "xhigh"],
|
|
447
|
-
low: ["low", "minimal", "none", "medium", "high", "xhigh"],
|
|
448
|
-
medium: ["medium", "low", "high", "minimal", "none", "xhigh"],
|
|
449
|
-
high: ["high", "medium", "xhigh", "low", "minimal", "none"],
|
|
450
|
-
xhigh: ["xhigh", "high", "medium", "low", "minimal", "none"],
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
const KNOWN_REASONING_EFFORTS = new Set(Object.keys(REASONING_FALLBACKS));
|
|
454
|
-
const REQUESTED_MODEL_ALIASES = new Map();
|
|
455
|
-
const DEFAULT_GENERAL_GPT5_MODEL = "gpt-5.4";
|
|
456
|
-
const GPT_5_5_CANONICAL_MODEL = "gpt-5.5";
|
|
457
|
-
const GPT_5_5_PRO_CANONICAL_MODEL = "gpt-5.5-pro";
|
|
458
|
-
const GPT_5_5_RELEASE_MODEL = "gpt-5.5-20260423";
|
|
459
|
-
const GPT_5_5_PRO_RELEASE_MODEL = "gpt-5.5-pro-20260423";
|
|
460
|
-
const GENERAL_GPT5_VERSION_CATALOG = {
|
|
461
|
-
1: {
|
|
462
|
-
base: "gpt-5.1",
|
|
463
|
-
},
|
|
464
|
-
2: {
|
|
465
|
-
base: "gpt-5.2",
|
|
466
|
-
pro: "gpt-5.2-pro",
|
|
467
|
-
},
|
|
468
|
-
4: {
|
|
469
|
-
base: DEFAULT_GENERAL_GPT5_MODEL,
|
|
470
|
-
pro: "gpt-5.4-pro",
|
|
471
|
-
mini: "gpt-5-mini",
|
|
472
|
-
nano: "gpt-5-nano",
|
|
473
|
-
},
|
|
474
|
-
5: {
|
|
475
|
-
base: GPT_5_5_CANONICAL_MODEL,
|
|
476
|
-
pro: GPT_5_5_PRO_CANONICAL_MODEL,
|
|
477
|
-
mini: "gpt-5-mini",
|
|
478
|
-
nano: "gpt-5-nano",
|
|
479
|
-
},
|
|
480
|
-
};
|
|
481
|
-
const GENERAL_GPT5_STABLE_VARIANTS = GENERAL_GPT5_VERSION_CATALOG[4];
|
|
482
|
-
const GENERAL_GPT5_GENERIC_VARIANTS = {
|
|
483
|
-
base: DEFAULT_GENERAL_GPT5_MODEL,
|
|
484
|
-
pro: "gpt-5-pro",
|
|
485
|
-
mini: "gpt-5-mini",
|
|
486
|
-
nano: "gpt-5-nano",
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
function addRequestedModelAlias(alias, normalizedModel) {
|
|
490
|
-
REQUESTED_MODEL_ALIASES.set(alias, normalizedModel);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function addRequestedModelReasoningAliases(alias, normalizedModel) {
|
|
494
|
-
addRequestedModelAlias(alias, normalizedModel);
|
|
495
|
-
for (const effort of KNOWN_REASONING_EFFORTS) {
|
|
496
|
-
addRequestedModelAlias(`${alias}-${effort}`, normalizedModel);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function maybeThrowSimulatedShadowHomeBusyError() {
|
|
501
|
-
if (shadowHomeCleanupBusyFailuresRemaining > 0) {
|
|
502
|
-
shadowHomeCleanupBusyFailuresRemaining -= 1;
|
|
503
|
-
const error = new Error("simulated busy shadow-home operation");
|
|
504
|
-
error.code = "EBUSY";
|
|
505
|
-
throw error;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function maybeThrowSimulatedShadowHomePreflightReadBusyError() {
|
|
510
|
-
if (shadowHomeCleanupPreflightReadBusyFailuresRemaining > 0) {
|
|
511
|
-
shadowHomeCleanupPreflightReadBusyFailuresRemaining -= 1;
|
|
512
|
-
const error = new Error("simulated busy shadow-home preflight read");
|
|
513
|
-
error.code = "EBUSY";
|
|
514
|
-
throw error;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
addRequestedModelReasoningAliases(
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
addRequestedModelReasoningAliases(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
addRequestedModelReasoningAliases(
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
addRequestedModelReasoningAliases(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
addRequestedModelReasoningAliases("gpt-5-
|
|
614
|
-
addRequestedModelReasoningAliases("gpt-5.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
)
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
normalized.includes("gpt
|
|
683
|
-
normalized.includes("gpt
|
|
684
|
-
normalized.includes("
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
return
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
return
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
try {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
"
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
return
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
if (
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
if (
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
)
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
795
|
+
lastExitCode = result.exitCode;
|
|
796
|
+
if (result.exitCode === 0) {
|
|
797
|
+
return result.exitCode;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const fallbackModel = resolveUnsupportedModelRetryTarget(
|
|
801
|
+
requestedModel,
|
|
802
|
+
result.output,
|
|
803
|
+
[...attemptedModels],
|
|
804
|
+
);
|
|
805
|
+
if (!fallbackModel) {
|
|
806
|
+
return result.exitCode;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
console.error(
|
|
810
|
+
`codex-multi-auth: model ${requestedModel ?? "requested model"} is unsupported on this ChatGPT Codex surface. Retrying with ${fallbackModel}.`,
|
|
811
|
+
);
|
|
812
|
+
attemptedModels.add(fallbackModel);
|
|
813
|
+
const nextArgs = replaceRequestedModel(currentArgs, fallbackModel);
|
|
814
|
+
if (JSON.stringify(nextArgs) === JSON.stringify(currentArgs)) {
|
|
815
|
+
return result.exitCode;
|
|
816
|
+
}
|
|
817
|
+
currentArgs = nextArgs;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
return lastExitCode;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function hasCliAuthCredentialsStoreOverride(args) {
|
|
824
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
825
|
+
const arg = args[i];
|
|
826
|
+
if (arg === "-c" || arg === "--config") {
|
|
827
|
+
const next = args[i + 1];
|
|
828
|
+
if (!next || !next.includes("=")) continue;
|
|
829
|
+
const [key] = next.split("=", 1);
|
|
830
|
+
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (typeof arg === "string" && arg.startsWith("--config=")) {
|
|
836
|
+
const assignment = arg.slice("--config=".length);
|
|
837
|
+
const [key] = assignment.split("=", 1);
|
|
838
|
+
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// IMPORTANT: Keep this mapping in sync with
|
|
847
|
+
// `lib/request/helpers/model-map.ts` `MODEL_PROFILES`
|
|
848
|
+
// and its GPT-5 normalization helpers.
|
|
849
|
+
// This wrapper runs before the TypeScript build, so it cannot import that source.
|
|
850
|
+
const SUPPORTED_REASONING_EFFORTS_BY_MODEL = {
|
|
851
|
+
"gpt-5-codex": ["low", "medium", "high", "xhigh"],
|
|
852
|
+
"gpt-5.1-codex-max": ["medium", "high", "xhigh"],
|
|
853
|
+
"gpt-5.1-codex-mini": ["medium", "high"],
|
|
854
|
+
"gpt-5.5": ["none", "low", "medium", "high", "xhigh"],
|
|
855
|
+
"gpt-5.5-pro": ["medium", "high", "xhigh"],
|
|
856
|
+
"gpt-5.4": ["none", "low", "medium", "high", "xhigh"],
|
|
857
|
+
"gpt-5.4-pro": ["medium", "high", "xhigh"],
|
|
858
|
+
"gpt-5.2-pro": ["medium", "high", "xhigh"],
|
|
859
|
+
"gpt-5-pro": ["high"],
|
|
860
|
+
"gpt-5.2": ["none", "low", "medium", "high", "xhigh"],
|
|
861
|
+
"gpt-5.1": ["none", "low", "medium", "high"],
|
|
862
|
+
"gpt-5": ["minimal", "low", "medium", "high"],
|
|
863
|
+
"gpt-5-mini": ["medium"],
|
|
864
|
+
"gpt-5-nano": ["medium"],
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const REASONING_FALLBACKS = {
|
|
868
|
+
none: ["none", "low", "minimal", "medium", "high", "xhigh"],
|
|
869
|
+
minimal: ["minimal", "low", "none", "medium", "high", "xhigh"],
|
|
870
|
+
low: ["low", "minimal", "none", "medium", "high", "xhigh"],
|
|
871
|
+
medium: ["medium", "low", "high", "minimal", "none", "xhigh"],
|
|
872
|
+
high: ["high", "medium", "xhigh", "low", "minimal", "none"],
|
|
873
|
+
xhigh: ["xhigh", "high", "medium", "low", "minimal", "none"],
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const KNOWN_REASONING_EFFORTS = new Set(Object.keys(REASONING_FALLBACKS));
|
|
877
|
+
const REQUESTED_MODEL_ALIASES = new Map();
|
|
878
|
+
const DEFAULT_GENERAL_GPT5_MODEL = "gpt-5.4";
|
|
879
|
+
const GPT_5_5_CANONICAL_MODEL = "gpt-5.5";
|
|
880
|
+
const GPT_5_5_PRO_CANONICAL_MODEL = "gpt-5.5-pro";
|
|
881
|
+
const GPT_5_5_RELEASE_MODEL = "gpt-5.5-20260423";
|
|
882
|
+
const GPT_5_5_PRO_RELEASE_MODEL = "gpt-5.5-pro-20260423";
|
|
883
|
+
const GENERAL_GPT5_VERSION_CATALOG = {
|
|
884
|
+
1: {
|
|
885
|
+
base: "gpt-5.1",
|
|
886
|
+
},
|
|
887
|
+
2: {
|
|
888
|
+
base: "gpt-5.2",
|
|
889
|
+
pro: "gpt-5.2-pro",
|
|
890
|
+
},
|
|
891
|
+
4: {
|
|
892
|
+
base: DEFAULT_GENERAL_GPT5_MODEL,
|
|
893
|
+
pro: "gpt-5.4-pro",
|
|
894
|
+
mini: "gpt-5-mini",
|
|
895
|
+
nano: "gpt-5-nano",
|
|
896
|
+
},
|
|
897
|
+
5: {
|
|
898
|
+
base: GPT_5_5_CANONICAL_MODEL,
|
|
899
|
+
pro: GPT_5_5_PRO_CANONICAL_MODEL,
|
|
900
|
+
mini: "gpt-5-mini",
|
|
901
|
+
nano: "gpt-5-nano",
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
const GENERAL_GPT5_STABLE_VARIANTS = GENERAL_GPT5_VERSION_CATALOG[4];
|
|
905
|
+
const GENERAL_GPT5_GENERIC_VARIANTS = {
|
|
906
|
+
base: DEFAULT_GENERAL_GPT5_MODEL,
|
|
907
|
+
pro: "gpt-5-pro",
|
|
908
|
+
mini: "gpt-5-mini",
|
|
909
|
+
nano: "gpt-5-nano",
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
function addRequestedModelAlias(alias, normalizedModel) {
|
|
913
|
+
REQUESTED_MODEL_ALIASES.set(alias, normalizedModel);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function addRequestedModelReasoningAliases(alias, normalizedModel) {
|
|
917
|
+
addRequestedModelAlias(alias, normalizedModel);
|
|
918
|
+
for (const effort of KNOWN_REASONING_EFFORTS) {
|
|
919
|
+
addRequestedModelAlias(`${alias}-${effort}`, normalizedModel);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function maybeThrowSimulatedShadowHomeBusyError() {
|
|
924
|
+
if (shadowHomeCleanupBusyFailuresRemaining > 0) {
|
|
925
|
+
shadowHomeCleanupBusyFailuresRemaining -= 1;
|
|
926
|
+
const error = new Error("simulated busy shadow-home operation");
|
|
927
|
+
error.code = "EBUSY";
|
|
928
|
+
throw error;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function maybeThrowSimulatedShadowHomePreflightReadBusyError() {
|
|
933
|
+
if (shadowHomeCleanupPreflightReadBusyFailuresRemaining > 0) {
|
|
934
|
+
shadowHomeCleanupPreflightReadBusyFailuresRemaining -= 1;
|
|
935
|
+
const error = new Error("simulated busy shadow-home preflight read");
|
|
936
|
+
error.code = "EBUSY";
|
|
937
|
+
throw error;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function maybeThrowSimulatedShadowHomeSyncMetadataBusyError(targetPath) {
|
|
942
|
+
if (
|
|
943
|
+
basename(targetPath) === SHADOW_HOME_SYNC_STATE_FILE &&
|
|
944
|
+
shadowHomeSyncMetadataBusyFailuresRemaining > 0
|
|
945
|
+
) {
|
|
946
|
+
shadowHomeSyncMetadataBusyFailuresRemaining -= 1;
|
|
947
|
+
const error = new Error("simulated busy shadow-home sync metadata write");
|
|
948
|
+
error.code = "EBUSY";
|
|
949
|
+
throw error;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function maybeThrowSimulatedShadowHomeSyncLockOwnerWriteError() {
|
|
954
|
+
if (shadowHomeSyncLockOwnerWriteFailuresRemaining > 0) {
|
|
955
|
+
shadowHomeSyncLockOwnerWriteFailuresRemaining -= 1;
|
|
956
|
+
const error = new Error("simulated shadow sync lock owner write failure");
|
|
957
|
+
error.code = "EPERM";
|
|
958
|
+
throw error;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function writeShadowHomeCleanupRetryMarker(destinationPath, attempt) {
|
|
963
|
+
if (shadowHomeCleanupRetryMarkerDir.length === 0) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
mkdirSync(shadowHomeCleanupRetryMarkerDir, { recursive: true });
|
|
968
|
+
writeFileSync(
|
|
969
|
+
join(
|
|
970
|
+
shadowHomeCleanupRetryMarkerDir,
|
|
971
|
+
`${basename(destinationPath)}.retry-${attempt + 1}`,
|
|
972
|
+
),
|
|
973
|
+
`${attempt + 1}\n`,
|
|
974
|
+
"utf8",
|
|
975
|
+
);
|
|
976
|
+
} catch {
|
|
977
|
+
// Best-effort test hook only.
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function ensureShadowHomeDestinationMatchesSnapshot(destinationPath, expectedState) {
|
|
982
|
+
if (!expectedState) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const currentState = captureShadowHomeState(destinationPath, {
|
|
986
|
+
rethrowRetryableReadErrors: true,
|
|
987
|
+
});
|
|
988
|
+
if (!shadowHomeStateMatches(currentState, expectedState)) {
|
|
989
|
+
const error = new Error("shadow-home destination changed during sync-back retry");
|
|
990
|
+
error.code = "EEXIST";
|
|
991
|
+
throw error;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function renameFileWithRetry(sourcePath, destinationPath, expectedDestinationState) {
|
|
996
|
+
for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
|
|
997
|
+
try {
|
|
998
|
+
ensureShadowHomeDestinationMatchesSnapshot(
|
|
999
|
+
destinationPath,
|
|
1000
|
+
expectedDestinationState,
|
|
1001
|
+
);
|
|
1002
|
+
maybeThrowSimulatedShadowHomeBusyError();
|
|
1003
|
+
renameSync(sourcePath, destinationPath);
|
|
1004
|
+
return;
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
if (
|
|
1007
|
+
!isRetryableShadowHomeCleanupError(error) ||
|
|
1008
|
+
attempt === SHADOW_HOME_CLEANUP_BACKOFF_MS.length
|
|
1009
|
+
) {
|
|
1010
|
+
throw error;
|
|
1011
|
+
}
|
|
1012
|
+
writeShadowHomeCleanupRetryMarker(destinationPath, attempt);
|
|
1013
|
+
sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function seedRequestedModelAliases() {
|
|
1019
|
+
addRequestedModelReasoningAliases(
|
|
1020
|
+
GPT_5_5_CANONICAL_MODEL,
|
|
1021
|
+
GPT_5_5_CANONICAL_MODEL,
|
|
1022
|
+
);
|
|
1023
|
+
addRequestedModelReasoningAliases(
|
|
1024
|
+
GPT_5_5_RELEASE_MODEL,
|
|
1025
|
+
GPT_5_5_CANONICAL_MODEL,
|
|
1026
|
+
);
|
|
1027
|
+
addRequestedModelReasoningAliases(
|
|
1028
|
+
GPT_5_5_PRO_CANONICAL_MODEL,
|
|
1029
|
+
GPT_5_5_PRO_CANONICAL_MODEL,
|
|
1030
|
+
);
|
|
1031
|
+
addRequestedModelReasoningAliases(
|
|
1032
|
+
GPT_5_5_PRO_RELEASE_MODEL,
|
|
1033
|
+
GPT_5_5_PRO_CANONICAL_MODEL,
|
|
1034
|
+
);
|
|
1035
|
+
addRequestedModelReasoningAliases("gpt-5.4", "gpt-5.4");
|
|
1036
|
+
addRequestedModelReasoningAliases("gpt-5.4-pro", "gpt-5.4-pro");
|
|
1037
|
+
addRequestedModelReasoningAliases("gpt-5.2-pro", "gpt-5.2-pro");
|
|
1038
|
+
addRequestedModelReasoningAliases("gpt-5-pro", "gpt-5-pro");
|
|
1039
|
+
addRequestedModelReasoningAliases("gpt-5.2", "gpt-5.2");
|
|
1040
|
+
addRequestedModelReasoningAliases("gpt-5.1", "gpt-5.1");
|
|
1041
|
+
addRequestedModelReasoningAliases("gpt-5", "gpt-5");
|
|
1042
|
+
addRequestedModelReasoningAliases("gpt-5-mini", "gpt-5-mini");
|
|
1043
|
+
addRequestedModelReasoningAliases("gpt-5-nano", "gpt-5-nano");
|
|
1044
|
+
addRequestedModelReasoningAliases("gpt-5.1-chat-latest", "gpt-5.1");
|
|
1045
|
+
addRequestedModelReasoningAliases("gpt-5-chat-latest", "gpt-5");
|
|
1046
|
+
addRequestedModelReasoningAliases("gpt-5.4-mini", "gpt-5-mini");
|
|
1047
|
+
addRequestedModelReasoningAliases("gpt-5.4-nano", "gpt-5-nano");
|
|
1048
|
+
addRequestedModelReasoningAliases("gpt-5-codex", "gpt-5-codex");
|
|
1049
|
+
addRequestedModelReasoningAliases("gpt-5.3-codex-spark", "gpt-5-codex");
|
|
1050
|
+
addRequestedModelReasoningAliases("gpt-5.3-codex", "gpt-5-codex");
|
|
1051
|
+
addRequestedModelReasoningAliases("gpt-5.2-codex", "gpt-5-codex");
|
|
1052
|
+
addRequestedModelReasoningAliases("gpt-5.1-codex", "gpt-5-codex");
|
|
1053
|
+
addRequestedModelAlias("gpt_5_codex", "gpt-5-codex");
|
|
1054
|
+
addRequestedModelReasoningAliases("codex-max", "gpt-5.1-codex-max");
|
|
1055
|
+
addRequestedModelReasoningAliases("gpt-5.1-codex-max", "gpt-5.1-codex-max");
|
|
1056
|
+
addRequestedModelAlias("codex-mini-latest", "gpt-5.1-codex-mini");
|
|
1057
|
+
addRequestedModelReasoningAliases("gpt-5-codex-mini", "gpt-5.1-codex-mini");
|
|
1058
|
+
addRequestedModelReasoningAliases("gpt-5.1-codex-mini", "gpt-5.1-codex-mini");
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
seedRequestedModelAliases();
|
|
1062
|
+
|
|
1063
|
+
function stripProviderPrefix(model) {
|
|
1064
|
+
return typeof model === "string" && model.includes("/")
|
|
1065
|
+
? model.split("/").pop() ?? model
|
|
1066
|
+
: model;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function tokenizeRequestedModel(model) {
|
|
1070
|
+
return String(model ?? "")
|
|
1071
|
+
.toLowerCase()
|
|
1072
|
+
.split(/[^a-z0-9]+/)
|
|
1073
|
+
.filter(Boolean);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function getGeneralGpt5CatalogForMinor(minor) {
|
|
1077
|
+
switch (minor) {
|
|
1078
|
+
case 1:
|
|
1079
|
+
case 2:
|
|
1080
|
+
case 4:
|
|
1081
|
+
case 5:
|
|
1082
|
+
return GENERAL_GPT5_VERSION_CATALOG[minor];
|
|
1083
|
+
default:
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function resolveGeneralGpt5CatalogVariant(catalog, variant) {
|
|
1089
|
+
return catalog?.[variant] ?? catalog?.base;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function resolveStableGeneralGpt5Variant(variant) {
|
|
1093
|
+
const fallback = (
|
|
1094
|
+
GENERAL_GPT5_STABLE_VARIANTS[variant] ??
|
|
1095
|
+
GENERAL_GPT5_STABLE_VARIANTS.base
|
|
1096
|
+
);
|
|
1097
|
+
if (!fallback) {
|
|
1098
|
+
throw new Error(`Stable GPT-5 fallback is missing for variant ${variant}`);
|
|
1099
|
+
}
|
|
1100
|
+
return fallback;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function resolveCodexRequestedModel(normalized) {
|
|
1104
|
+
if (
|
|
1105
|
+
normalized.includes("gpt-5.1-codex-max") ||
|
|
1106
|
+
normalized.includes("gpt 5.1 codex max") ||
|
|
1107
|
+
normalized.includes("codex-max")
|
|
1108
|
+
) {
|
|
1109
|
+
return "gpt-5.1-codex-max";
|
|
1110
|
+
}
|
|
1111
|
+
if (
|
|
1112
|
+
normalized.includes("gpt-5.1-codex-mini") ||
|
|
1113
|
+
normalized.includes("gpt 5.1 codex mini") ||
|
|
1114
|
+
normalized.includes("gpt-5-codex-mini") ||
|
|
1115
|
+
normalized.includes("gpt 5 codex mini") ||
|
|
1116
|
+
normalized.includes("codex-mini-latest")
|
|
1117
|
+
) {
|
|
1118
|
+
return "gpt-5.1-codex-mini";
|
|
1119
|
+
}
|
|
1120
|
+
if (
|
|
1121
|
+
normalized.includes("gpt-5.3-codex-spark") ||
|
|
1122
|
+
normalized.includes("gpt 5.3 codex spark") ||
|
|
1123
|
+
normalized.includes("gpt-5.3-codex") ||
|
|
1124
|
+
normalized.includes("gpt 5.3 codex") ||
|
|
1125
|
+
normalized.includes("gpt-5.2-codex") ||
|
|
1126
|
+
normalized.includes("gpt 5.2 codex") ||
|
|
1127
|
+
normalized.includes("gpt-5.1-codex") ||
|
|
1128
|
+
normalized.includes("gpt 5.1 codex") ||
|
|
1129
|
+
normalized.includes("gpt-5-codex") ||
|
|
1130
|
+
normalized.includes("gpt 5 codex") ||
|
|
1131
|
+
normalized === "codex"
|
|
1132
|
+
) {
|
|
1133
|
+
return "gpt-5-codex";
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
return "";
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
function resolveGeneralGpt5RequestedModel(stripped) {
|
|
1140
|
+
const tokens = tokenizeRequestedModel(stripped);
|
|
1141
|
+
const gptIndex = tokens.indexOf("gpt");
|
|
1142
|
+
const isGpt5 = gptIndex !== -1 && tokens[gptIndex + 1] === "5";
|
|
1143
|
+
if (!isGpt5 || tokens.includes("codex")) {
|
|
1144
|
+
return "";
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const rawMinor = tokens[gptIndex + 2];
|
|
1148
|
+
const minor =
|
|
1149
|
+
typeof rawMinor === "string" && /^\d+$/.test(rawMinor)
|
|
1150
|
+
? Number(rawMinor)
|
|
1151
|
+
: undefined;
|
|
1152
|
+
const variant = tokens.includes("mini")
|
|
1153
|
+
? "mini"
|
|
1154
|
+
: tokens.includes("nano")
|
|
1155
|
+
? "nano"
|
|
1156
|
+
: tokens.includes("pro")
|
|
1157
|
+
? "pro"
|
|
1158
|
+
: "base";
|
|
1159
|
+
|
|
1160
|
+
if (minor === undefined) {
|
|
1161
|
+
return GENERAL_GPT5_GENERIC_VARIANTS[variant];
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const exactCatalog = getGeneralGpt5CatalogForMinor(minor);
|
|
1165
|
+
const exactMatch = resolveGeneralGpt5CatalogVariant(exactCatalog, variant);
|
|
1166
|
+
if (exactMatch) {
|
|
1167
|
+
return exactMatch;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
return resolveStableGeneralGpt5Variant(variant);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function normalizeRequestedModel(model) {
|
|
1174
|
+
const stripped = stripProviderPrefix(model ?? "");
|
|
1175
|
+
const normalized = stripped.trim().toLowerCase();
|
|
1176
|
+
if (normalized.length === 0) return "";
|
|
1177
|
+
const exactMatch = REQUESTED_MODEL_ALIASES.get(normalized);
|
|
1178
|
+
if (exactMatch) {
|
|
1179
|
+
return exactMatch;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const codexModel = resolveCodexRequestedModel(normalized);
|
|
1183
|
+
if (codexModel) {
|
|
1184
|
+
return codexModel;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const generalModel = resolveGeneralGpt5RequestedModel(stripped);
|
|
1188
|
+
if (generalModel) {
|
|
1189
|
+
return generalModel;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
return "";
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function coerceReasoningEffortForModel(model, effort) {
|
|
1196
|
+
if (typeof effort !== "string") return effort;
|
|
1197
|
+
const normalizedEffort = effort.trim().toLowerCase();
|
|
1198
|
+
if (!KNOWN_REASONING_EFFORTS.has(normalizedEffort)) {
|
|
1199
|
+
return effort;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const normalizedModel = normalizeRequestedModel(model);
|
|
1203
|
+
const supportedEfforts =
|
|
1204
|
+
SUPPORTED_REASONING_EFFORTS_BY_MODEL[normalizedModel] ?? null;
|
|
1205
|
+
if (!supportedEfforts || supportedEfforts.includes(normalizedEffort)) {
|
|
1206
|
+
return normalizedEffort;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
const fallbackOrder = REASONING_FALLBACKS[normalizedEffort] ?? [normalizedEffort];
|
|
1210
|
+
for (const candidate of fallbackOrder) {
|
|
1211
|
+
if (supportedEfforts.includes(candidate)) {
|
|
1212
|
+
return candidate;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return normalizedEffort;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function extractRequestedModel(args) {
|
|
1220
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1221
|
+
const arg = args[i];
|
|
1222
|
+
if (arg === "--model") {
|
|
1223
|
+
const next = args[i + 1];
|
|
1224
|
+
if (typeof next === "string" && next.trim().length > 0) {
|
|
1225
|
+
return next.trim();
|
|
1226
|
+
}
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
if (typeof arg === "string" && arg.startsWith("--model=")) {
|
|
1230
|
+
const value = arg.slice("--model=".length).trim();
|
|
1231
|
+
if (value.length > 0) {
|
|
1232
|
+
return value;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function parseConfigAssignment(value) {
|
|
1240
|
+
if (typeof value !== "string") return null;
|
|
1241
|
+
const separatorIndex = value.indexOf("=");
|
|
1242
|
+
if (separatorIndex < 0) return null;
|
|
1243
|
+
return {
|
|
1244
|
+
key: value.slice(0, separatorIndex).trim(),
|
|
1245
|
+
value: value.slice(separatorIndex + 1).trim(),
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function parseQuotedValue(value) {
|
|
1250
|
+
if (typeof value !== "string") {
|
|
1251
|
+
return { quote: '"', inner: "" };
|
|
1252
|
+
}
|
|
1253
|
+
const trimmed = value.trim();
|
|
1254
|
+
const first = trimmed[0];
|
|
1255
|
+
const last = trimmed.at(-1);
|
|
1256
|
+
if ((first === '"' || first === "'") && last === first) {
|
|
1257
|
+
return {
|
|
1258
|
+
quote: first,
|
|
1259
|
+
inner: trimmed.slice(1, -1),
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
return { quote: '"', inner: trimmed };
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function rewriteReasoningConfigAssignment(assignment, requestedModel) {
|
|
1266
|
+
const parsed = parseConfigAssignment(assignment);
|
|
1267
|
+
if (!parsed || parsed.key !== "model_reasoning_effort") {
|
|
1268
|
+
return assignment;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const { quote, inner } = parseQuotedValue(parsed.value);
|
|
1272
|
+
const coercedEffort = coerceReasoningEffortForModel(requestedModel, inner);
|
|
1273
|
+
if (coercedEffort === inner) {
|
|
1274
|
+
return assignment;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
return `${parsed.key}=${quote}${coercedEffort}${quote}`;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function rewriteReasoningConfigArgs(rawArgs) {
|
|
1281
|
+
const requestedModel = extractRequestedModel(rawArgs);
|
|
1282
|
+
if (!requestedModel) {
|
|
1283
|
+
return {
|
|
1284
|
+
args: [...rawArgs],
|
|
1285
|
+
requestedModel: null,
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
const nextArgs = [...rawArgs];
|
|
1290
|
+
for (let i = 0; i < nextArgs.length; i += 1) {
|
|
1291
|
+
const arg = nextArgs[i];
|
|
1292
|
+
if ((arg === "-c" || arg === "--config") && typeof nextArgs[i + 1] === "string") {
|
|
1293
|
+
nextArgs[i + 1] = rewriteReasoningConfigAssignment(
|
|
1294
|
+
nextArgs[i + 1],
|
|
1295
|
+
requestedModel,
|
|
1296
|
+
);
|
|
1297
|
+
i += 1;
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
if (typeof arg === "string" && arg.startsWith("--config=")) {
|
|
1301
|
+
const assignment = arg.slice("--config=".length);
|
|
1302
|
+
nextArgs[i] = `--config=${rewriteReasoningConfigAssignment(
|
|
1303
|
+
assignment,
|
|
1304
|
+
requestedModel,
|
|
1305
|
+
)}`;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
return {
|
|
1310
|
+
args: nextArgs,
|
|
1311
|
+
requestedModel,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function resolveCodexHomeDir(env = process.env) {
|
|
1316
|
+
const override = (env.CODEX_HOME ?? "").trim();
|
|
1317
|
+
if (override.length > 0) return override;
|
|
1318
|
+
if (process.platform === "win32") {
|
|
1319
|
+
const homeDir = resolveWindowsUserHomeDir();
|
|
1320
|
+
if (homeDir) {
|
|
1321
|
+
return join(homeDir, ".codex");
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return join(env.HOME ?? homedir(), ".codex");
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function ensureTrailingNewline(value) {
|
|
1328
|
+
return value.endsWith("\n") ? value : `${value}\n`;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
function captureShadowHomeState(filePath, options = {}) {
|
|
1332
|
+
try {
|
|
1333
|
+
if (!existsSync(filePath)) {
|
|
1334
|
+
return { exists: false, content: null };
|
|
1335
|
+
}
|
|
1336
|
+
if (options.rethrowRetryableReadErrors) {
|
|
1337
|
+
maybeThrowSimulatedShadowHomePreflightReadBusyError();
|
|
1338
|
+
}
|
|
1339
|
+
return {
|
|
1340
|
+
exists: true,
|
|
1341
|
+
content: readFileSync(filePath, "utf8"),
|
|
1342
|
+
};
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if (options.rethrowRetryableReadErrors && isRetryableShadowHomeCleanupError(error)) {
|
|
1345
|
+
throw error;
|
|
1346
|
+
}
|
|
1347
|
+
return { exists: true, content: null, unreadable: true };
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function shadowHomeStateMatches(left, right) {
|
|
1352
|
+
return (
|
|
1353
|
+
left.exists === right.exists &&
|
|
1354
|
+
left.content === right.content &&
|
|
1355
|
+
Boolean(left.unreadable) === Boolean(right.unreadable)
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
function hashShadowHomeState(state) {
|
|
1360
|
+
if (state.unreadable) {
|
|
1361
|
+
return null;
|
|
1362
|
+
}
|
|
1363
|
+
if (!state.exists) {
|
|
1364
|
+
return "missing";
|
|
1365
|
+
}
|
|
1366
|
+
if (typeof state.content !== "string") {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
return `sha256:${createHash("sha256").update(state.content).digest("hex")}`;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
function readShadowHomeSyncState(originalCodexHome) {
|
|
1373
|
+
try {
|
|
1374
|
+
const parsed = JSON.parse(
|
|
1375
|
+
readFileSync(join(originalCodexHome, SHADOW_HOME_SYNC_STATE_FILE), "utf8"),
|
|
1376
|
+
);
|
|
1377
|
+
if (
|
|
1378
|
+
!parsed ||
|
|
1379
|
+
typeof parsed !== "object" ||
|
|
1380
|
+
parsed.version !== 1 ||
|
|
1381
|
+
!parsed.files ||
|
|
1382
|
+
typeof parsed.files !== "object"
|
|
1383
|
+
) {
|
|
1384
|
+
return { version: 1, files: {} };
|
|
1385
|
+
}
|
|
1386
|
+
return parsed;
|
|
1387
|
+
} catch {
|
|
1388
|
+
return { version: 1, files: {} };
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
function rememberShadowHomeSyncState(
|
|
1393
|
+
originalCodexHome,
|
|
1394
|
+
syncState,
|
|
1395
|
+
name,
|
|
1396
|
+
baseState,
|
|
1397
|
+
syncedState,
|
|
1398
|
+
) {
|
|
1399
|
+
const baseHash = hashShadowHomeState(baseState);
|
|
1400
|
+
const syncedHash = hashShadowHomeState(syncedState);
|
|
1401
|
+
if (!baseHash || !syncedHash) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
syncState.files[name] = {
|
|
1405
|
+
baseHash,
|
|
1406
|
+
syncedHash,
|
|
1407
|
+
updatedAt: Date.now(),
|
|
1408
|
+
};
|
|
1409
|
+
try {
|
|
1410
|
+
writeOwnerOnlyJsonFileAtomicSync(
|
|
1411
|
+
join(originalCodexHome, SHADOW_HOME_SYNC_STATE_FILE),
|
|
1412
|
+
syncState,
|
|
1413
|
+
);
|
|
1414
|
+
} catch {
|
|
1415
|
+
// Best-effort metadata; failed metadata must not fail auth cleanup.
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function canRebaseShadowHomeSyncState(syncState, name, baseState, currentState) {
|
|
1420
|
+
const entry = syncState.files?.[name];
|
|
1421
|
+
if (!entry || typeof entry !== "object") {
|
|
1422
|
+
return false;
|
|
1423
|
+
}
|
|
1424
|
+
// Permit a later shadow session to write over an earlier shadow sync from
|
|
1425
|
+
// the same launch snapshot, while still refusing unrelated external edits.
|
|
1426
|
+
return (
|
|
1427
|
+
entry.baseHash === hashShadowHomeState(baseState) &&
|
|
1428
|
+
entry.syncedHash === hashShadowHomeState(currentState)
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function readShadowHomeSyncLockOwnerPid(lockPath) {
|
|
1433
|
+
try {
|
|
1434
|
+
const rawOwner = JSON.parse(readFileSync(join(lockPath, "owner.json"), "utf8"));
|
|
1435
|
+
const pid = Number(rawOwner?.pid);
|
|
1436
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
1437
|
+
} catch {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function isShadowHomeSyncLockOldEnoughToSteal(lockPath) {
|
|
1443
|
+
try {
|
|
1444
|
+
const stats = statSync(lockPath);
|
|
1445
|
+
const newestTimestamp = Math.max(stats.mtimeMs, stats.ctimeMs);
|
|
1446
|
+
return Date.now() - newestTimestamp >= SHADOW_HOME_ORPHAN_LOCK_STALE_AGE_MS;
|
|
1447
|
+
} catch {
|
|
1448
|
+
return true;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
function removeStaleShadowHomeSyncLock(lockPath) {
|
|
1453
|
+
const ownerPid = readShadowHomeSyncLockOwnerPid(lockPath);
|
|
1454
|
+
if (ownerPid !== null && isProcessAlive(ownerPid)) {
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
if (ownerPid === null && !isShadowHomeSyncLockOldEnoughToSteal(lockPath)) {
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
try {
|
|
1461
|
+
removeDirectoryWithRetry(lockPath);
|
|
1462
|
+
if (shadowHomeSyncLockRecreateStaleCount > 0) {
|
|
1463
|
+
shadowHomeSyncLockRecreateStaleCount -= 1;
|
|
1464
|
+
mkdirSync(lockPath, { recursive: true });
|
|
1465
|
+
writeShadowHomeSyncLockOwner(lockPath, {
|
|
1466
|
+
pid: 2_147_483_647,
|
|
1467
|
+
createdAt: 1,
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
return true;
|
|
1471
|
+
} catch {
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
function writeShadowHomeSyncLockOwner(lockPath, owner) {
|
|
1477
|
+
const ownerPath = join(lockPath, "owner.json");
|
|
1478
|
+
maybeThrowSimulatedShadowHomeSyncLockOwnerWriteError();
|
|
1479
|
+
writeFileSync(ownerPath, `${JSON.stringify(owner)}\n`, {
|
|
1480
|
+
encoding: "utf8",
|
|
1481
|
+
mode: 0o600,
|
|
1482
|
+
});
|
|
1483
|
+
try {
|
|
1484
|
+
chmodSync(ownerPath, 0o600);
|
|
1485
|
+
} catch {
|
|
1486
|
+
// Best-effort only; permission semantics vary by platform.
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function writeShadowHomeSyncLockOwnerWithRetry(lockPath, owner) {
|
|
1491
|
+
for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
|
|
1492
|
+
try {
|
|
1493
|
+
writeShadowHomeSyncLockOwner(lockPath, owner);
|
|
1494
|
+
return;
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
if (
|
|
1497
|
+
!isRetryableShadowHomeCleanupError(error) ||
|
|
1498
|
+
attempt === SHADOW_HOME_CLEANUP_BACKOFF_MS.length
|
|
1499
|
+
) {
|
|
1500
|
+
throw error;
|
|
1501
|
+
}
|
|
1502
|
+
sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
function acquireShadowHomeSyncLock(originalCodexHome) {
|
|
1508
|
+
const lockPath = join(originalCodexHome, SHADOW_HOME_SYNC_LOCK_DIR);
|
|
1509
|
+
mkdirSync(originalCodexHome, { recursive: true });
|
|
1510
|
+
const maxStaleRecoveries = SHADOW_HOME_CLEANUP_BACKOFF_MS.length + 1;
|
|
1511
|
+
let staleRecoveries = 0;
|
|
1512
|
+
let attempt = 0;
|
|
1513
|
+
const deadline = Date.now() + SHADOW_HOME_SYNC_LOCK_WAIT_TIMEOUT_MS;
|
|
1514
|
+
while (true) {
|
|
1515
|
+
try {
|
|
1516
|
+
mkdirSync(lockPath);
|
|
1517
|
+
try {
|
|
1518
|
+
writeShadowHomeSyncLockOwnerWithRetry(lockPath, {
|
|
1519
|
+
pid: process.pid,
|
|
1520
|
+
createdAt: Date.now(),
|
|
1521
|
+
});
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
try {
|
|
1524
|
+
removeDirectoryWithRetry(lockPath);
|
|
1525
|
+
} catch {
|
|
1526
|
+
// Preserve the owner write failure while avoiding orphaned locks when possible.
|
|
1527
|
+
}
|
|
1528
|
+
throw error;
|
|
1529
|
+
}
|
|
1530
|
+
return () => {
|
|
1531
|
+
try {
|
|
1532
|
+
removeDirectoryWithRetry(lockPath);
|
|
1533
|
+
} catch {
|
|
1534
|
+
// Best-effort lock cleanup only.
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
const code =
|
|
1539
|
+
error && typeof error === "object" && "code" in error
|
|
1540
|
+
? error.code
|
|
1541
|
+
: undefined;
|
|
1542
|
+
if (code !== "EEXIST") {
|
|
1543
|
+
throw error;
|
|
1544
|
+
}
|
|
1545
|
+
if (
|
|
1546
|
+
staleRecoveries < maxStaleRecoveries &&
|
|
1547
|
+
removeStaleShadowHomeSyncLock(lockPath)
|
|
1548
|
+
) {
|
|
1549
|
+
staleRecoveries += 1;
|
|
1550
|
+
attempt = 0;
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1553
|
+
const remainingMs = deadline - Date.now();
|
|
1554
|
+
if (remainingMs <= 0) {
|
|
1555
|
+
throw error;
|
|
1556
|
+
}
|
|
1557
|
+
const backoffMs =
|
|
1558
|
+
SHADOW_HOME_CLEANUP_BACKOFF_MS[
|
|
1559
|
+
Math.min(attempt, SHADOW_HOME_CLEANUP_BACKOFF_MS.length - 1)
|
|
1560
|
+
] ?? SHADOW_HOME_CLEANUP_BACKOFF_MS[0];
|
|
1561
|
+
sleepSync(Math.min(backoffMs, remainingMs));
|
|
1562
|
+
attempt += 1;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
function syncShadowHomeStateFile(
|
|
1568
|
+
sourcePath,
|
|
1569
|
+
destinationPath,
|
|
1570
|
+
expectedDestinationState,
|
|
1571
|
+
) {
|
|
1572
|
+
const tempPath = join(
|
|
1573
|
+
dirname(destinationPath),
|
|
1574
|
+
`.${basename(destinationPath)}.codex-multi-auth-sync-${process.pid}.tmp`,
|
|
1575
|
+
);
|
|
1576
|
+
try {
|
|
1577
|
+
mkdirSync(dirname(destinationPath), { recursive: true });
|
|
1578
|
+
copyFileSync(sourcePath, tempPath);
|
|
1579
|
+
renameFileWithRetry(tempPath, destinationPath, expectedDestinationState);
|
|
1580
|
+
} catch (error) {
|
|
1581
|
+
try {
|
|
1582
|
+
rmSync(tempPath, { force: true });
|
|
1583
|
+
} catch {
|
|
1584
|
+
// Best-effort cleanup only.
|
|
1585
|
+
}
|
|
1586
|
+
throw error;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
function syncShadowHomeStateFileBestEffort(
|
|
1591
|
+
sourcePath,
|
|
1592
|
+
destinationPath,
|
|
1593
|
+
expectedDestinationState,
|
|
1594
|
+
tightenFile,
|
|
1595
|
+
) {
|
|
1596
|
+
try {
|
|
1597
|
+
syncShadowHomeStateFile(
|
|
1598
|
+
sourcePath,
|
|
1599
|
+
destinationPath,
|
|
1600
|
+
expectedDestinationState,
|
|
1601
|
+
);
|
|
1602
|
+
tightenFile(destinationPath);
|
|
1603
|
+
return true;
|
|
1604
|
+
} catch {
|
|
1605
|
+
// Best-effort sync-back: keep attempting sibling files after Windows locks.
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function isDirectoryLike(path) {
|
|
1611
|
+
try {
|
|
1612
|
+
return statSync(path).isDirectory();
|
|
1613
|
+
} catch {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
function isFileLike(path) {
|
|
1619
|
+
try {
|
|
1620
|
+
return statSync(path).isFile();
|
|
1621
|
+
} catch {
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
function mirrorDirectoryIntoShadowHome(sourcePath, destinationPath) {
|
|
1627
|
+
try {
|
|
1628
|
+
if ((process.env.CODEX_MULTI_AUTH_TEST_FORCE_SHADOW_DIR_COPY ?? "").trim() === "1") {
|
|
1629
|
+
throw new Error("simulated directory link failure");
|
|
1630
|
+
}
|
|
1631
|
+
symlinkSync(
|
|
1632
|
+
sourcePath,
|
|
1633
|
+
destinationPath,
|
|
1634
|
+
process.platform === "win32" ? "junction" : "dir",
|
|
1635
|
+
);
|
|
1636
|
+
return "linked";
|
|
1637
|
+
} catch {
|
|
1638
|
+
// Fall back to a copy when links are unavailable. Directory links are
|
|
1639
|
+
// preferred because they keep sessions, plugins, and skills live.
|
|
1640
|
+
}
|
|
1641
|
+
cpSync(sourcePath, destinationPath, {
|
|
1642
|
+
recursive: true,
|
|
1643
|
+
dereference: false,
|
|
1644
|
+
});
|
|
1645
|
+
return "copied";
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
function linkFileIntoShadowHome(sourcePath, destinationPath) {
|
|
1649
|
+
try {
|
|
1650
|
+
symlinkSync(sourcePath, destinationPath, "file");
|
|
1651
|
+
return true;
|
|
1652
|
+
} catch {
|
|
1653
|
+
// File symlinks keep SQLite/cache-style root files realtime when allowed.
|
|
1654
|
+
}
|
|
1655
|
+
try {
|
|
1656
|
+
linkSync(sourcePath, destinationPath);
|
|
1657
|
+
return true;
|
|
1658
|
+
} catch {
|
|
1659
|
+
// Hard links cover platforms where file symlinks require extra privileges.
|
|
1660
|
+
}
|
|
1661
|
+
return false;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function mirrorFileIntoShadowHome(sourcePath, destinationPath, tightenFile) {
|
|
1665
|
+
if (linkFileIntoShadowHome(sourcePath, destinationPath)) {
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
copyFileSync(sourcePath, destinationPath);
|
|
1669
|
+
tightenFile(destinationPath);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
function collectShadowHomeSyncFileNames(shadowCodexHome, syncFileNames) {
|
|
1673
|
+
try {
|
|
1674
|
+
for (const entry of readdirSync(shadowCodexHome, { withFileTypes: true })) {
|
|
1675
|
+
const name = entry.name;
|
|
1676
|
+
if (
|
|
1677
|
+
name === SHADOW_HOME_CONFIG_FILE ||
|
|
1678
|
+
name === SHADOW_HOME_SYNC_STATE_FILE ||
|
|
1679
|
+
syncFileNames.has(name)
|
|
1680
|
+
) {
|
|
1681
|
+
continue;
|
|
1682
|
+
}
|
|
1683
|
+
const shadowPath = join(shadowCodexHome, name);
|
|
1684
|
+
let fileLike = entry.isFile();
|
|
1685
|
+
if (entry.isSymbolicLink()) {
|
|
1686
|
+
fileLike = isFileLike(shadowPath);
|
|
1687
|
+
}
|
|
1688
|
+
if (fileLike) {
|
|
1689
|
+
syncFileNames.add(name);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
} catch {
|
|
1693
|
+
// Best-effort; cleanup still syncs the known state files.
|
|
1694
|
+
}
|
|
1695
|
+
return syncFileNames;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
function syncCopiedShadowHomeDirectories(originalCodexHome, shadowCodexHome, names) {
|
|
1699
|
+
for (const name of names) {
|
|
1700
|
+
const shadowPath = join(shadowCodexHome, name);
|
|
1701
|
+
if (!isDirectoryLike(shadowPath)) {
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
try {
|
|
1705
|
+
cpSync(shadowPath, join(originalCodexHome, name), {
|
|
1706
|
+
recursive: true,
|
|
1707
|
+
dereference: false,
|
|
1708
|
+
force: true,
|
|
1709
|
+
});
|
|
1710
|
+
} catch {
|
|
1711
|
+
// Best-effort sync-back; sibling directories and state files still run.
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
function syncShadowHomeAuthBundle(
|
|
1717
|
+
originalCodexHome,
|
|
1718
|
+
shadowCodexHome,
|
|
1719
|
+
originalFileStates,
|
|
1720
|
+
tightenFile,
|
|
1721
|
+
skipSyncBackNames = new Set(),
|
|
1722
|
+
) {
|
|
1723
|
+
const syncState = readShadowHomeSyncState(originalCodexHome);
|
|
1724
|
+
for (const name of SHADOW_HOME_STATE_FILES) {
|
|
1725
|
+
if (skipSyncBackNames.has(name)) {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
const shadowPath = join(shadowCodexHome, name);
|
|
1729
|
+
const shadowState = captureShadowHomeState(shadowPath);
|
|
1730
|
+
if (!shadowState.exists || shadowState.unreadable) {
|
|
1731
|
+
continue;
|
|
1732
|
+
}
|
|
1733
|
+
const originalPath = join(originalCodexHome, name);
|
|
1734
|
+
const originalSnapshot =
|
|
1735
|
+
originalFileStates.get(name) ?? { exists: false, content: null };
|
|
1736
|
+
const currentOriginalState = captureShadowHomeState(originalPath);
|
|
1737
|
+
let expectedDestinationState = originalSnapshot;
|
|
1738
|
+
if (!shadowHomeStateMatches(currentOriginalState, originalSnapshot)) {
|
|
1739
|
+
if (
|
|
1740
|
+
!canRebaseShadowHomeSyncState(
|
|
1741
|
+
syncState,
|
|
1742
|
+
name,
|
|
1743
|
+
originalSnapshot,
|
|
1744
|
+
currentOriginalState,
|
|
1745
|
+
)
|
|
1746
|
+
) {
|
|
1747
|
+
continue;
|
|
1748
|
+
}
|
|
1749
|
+
expectedDestinationState = currentOriginalState;
|
|
1750
|
+
}
|
|
1751
|
+
if (
|
|
1752
|
+
expectedDestinationState.unreadable ||
|
|
1753
|
+
shadowHomeStateMatches(shadowState, expectedDestinationState)
|
|
1754
|
+
) {
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (syncShadowHomeStateFileBestEffort(
|
|
1758
|
+
shadowPath,
|
|
1759
|
+
originalPath,
|
|
1760
|
+
expectedDestinationState,
|
|
1761
|
+
tightenFile,
|
|
1762
|
+
)) {
|
|
1763
|
+
rememberShadowHomeSyncState(
|
|
1764
|
+
originalCodexHome,
|
|
1765
|
+
syncState,
|
|
1766
|
+
name,
|
|
1767
|
+
originalSnapshot,
|
|
1768
|
+
shadowState,
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
function syncAdditionalShadowHomeFiles(
|
|
1775
|
+
originalCodexHome,
|
|
1776
|
+
shadowCodexHome,
|
|
1777
|
+
names,
|
|
1778
|
+
originalFileStates,
|
|
1779
|
+
tightenFile,
|
|
1780
|
+
skipSyncBackNames = new Set(),
|
|
1781
|
+
) {
|
|
1782
|
+
for (const name of names) {
|
|
1783
|
+
if (SHADOW_HOME_STATE_FILE_SET.has(name) || skipSyncBackNames.has(name)) {
|
|
1784
|
+
continue;
|
|
1785
|
+
}
|
|
1786
|
+
const shadowPath = join(shadowCodexHome, name);
|
|
1787
|
+
const shadowState = captureShadowHomeState(shadowPath);
|
|
1788
|
+
if (!shadowState.exists || shadowState.unreadable) {
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
const originalPath = join(originalCodexHome, name);
|
|
1793
|
+
const originalSnapshot =
|
|
1794
|
+
originalFileStates.get(name) ?? { exists: false, content: null };
|
|
1795
|
+
const currentOriginalState = captureShadowHomeState(originalPath);
|
|
1796
|
+
if (!shadowHomeStateMatches(currentOriginalState, originalSnapshot)) {
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
if (shadowHomeStateMatches(shadowState, originalSnapshot)) {
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
syncShadowHomeStateFileBestEffort(
|
|
1803
|
+
shadowPath,
|
|
1804
|
+
originalPath,
|
|
1805
|
+
originalSnapshot,
|
|
1806
|
+
tightenFile,
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
function createShadowHomeMirror(
|
|
1812
|
+
originalCodexHome,
|
|
1813
|
+
shadowCodexHome,
|
|
1814
|
+
tightenFile,
|
|
1815
|
+
options = {},
|
|
1816
|
+
) {
|
|
1817
|
+
const syncFileNames = new Set(SHADOW_HOME_STATE_FILES);
|
|
1818
|
+
const skipSyncBackNames = new Set(options.skipSyncBackNames ?? []);
|
|
1819
|
+
const originalFileStates = new Map();
|
|
1820
|
+
const copiedDirectoryNames = new Set();
|
|
1821
|
+
const rememberSyncFile = (name) => {
|
|
1822
|
+
if (!originalFileStates.has(name)) {
|
|
1823
|
+
originalFileStates.set(
|
|
1824
|
+
name,
|
|
1825
|
+
captureShadowHomeState(join(originalCodexHome, name)),
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
syncFileNames.add(name);
|
|
1829
|
+
};
|
|
1830
|
+
for (const name of SHADOW_HOME_STATE_FILES) {
|
|
1831
|
+
rememberSyncFile(name);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
if (existsSync(originalCodexHome)) {
|
|
1835
|
+
for (const entry of readdirSync(originalCodexHome, { withFileTypes: true })) {
|
|
1836
|
+
const name = entry.name;
|
|
1837
|
+
if (
|
|
1838
|
+
name === SHADOW_HOME_CONFIG_FILE ||
|
|
1839
|
+
name === SHADOW_HOME_SYNC_STATE_FILE ||
|
|
1840
|
+
name === SHADOW_HOME_SYNC_LOCK_DIR
|
|
1841
|
+
) {
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
const isKnownStateFile = SHADOW_HOME_STATE_FILE_SET.has(name);
|
|
1845
|
+
const sourcePath = join(originalCodexHome, name);
|
|
1846
|
+
const destinationPath = join(shadowCodexHome, name);
|
|
1847
|
+
if (existsSync(destinationPath)) {
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
let directoryLike = entry.isDirectory();
|
|
1852
|
+
let fileLike = entry.isFile();
|
|
1853
|
+
if (entry.isSymbolicLink()) {
|
|
1854
|
+
directoryLike = isDirectoryLike(sourcePath);
|
|
1855
|
+
fileLike = !directoryLike && isFileLike(sourcePath);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
try {
|
|
1859
|
+
if (isKnownStateFile && !fileLike) {
|
|
1860
|
+
throw new Error(`Expected ${name} to be a file`);
|
|
1861
|
+
}
|
|
1862
|
+
if (directoryLike) {
|
|
1863
|
+
if (mirrorDirectoryIntoShadowHome(sourcePath, destinationPath) === "copied") {
|
|
1864
|
+
copiedDirectoryNames.add(name);
|
|
1865
|
+
}
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
if (fileLike) {
|
|
1869
|
+
rememberSyncFile(name);
|
|
1870
|
+
if (isKnownStateFile) {
|
|
1871
|
+
copyFileSync(sourcePath, destinationPath);
|
|
1872
|
+
tightenFile(destinationPath);
|
|
1873
|
+
} else {
|
|
1874
|
+
mirrorFileIntoShadowHome(sourcePath, destinationPath, tightenFile);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
} catch (error) {
|
|
1878
|
+
if (isKnownStateFile) {
|
|
1879
|
+
throw error;
|
|
1880
|
+
}
|
|
1881
|
+
// A missing or locked optional home entry should not block runtime
|
|
1882
|
+
// launch; auth/config files still get handled explicitly.
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
return () => {
|
|
1888
|
+
let releaseLock = () => {};
|
|
1889
|
+
try {
|
|
1890
|
+
const names = collectShadowHomeSyncFileNames(shadowCodexHome, syncFileNames);
|
|
1891
|
+
releaseLock = acquireShadowHomeSyncLock(originalCodexHome);
|
|
1892
|
+
syncShadowHomeAuthBundle(
|
|
1893
|
+
originalCodexHome,
|
|
1894
|
+
shadowCodexHome,
|
|
1895
|
+
originalFileStates,
|
|
1896
|
+
tightenFile,
|
|
1897
|
+
skipSyncBackNames,
|
|
1898
|
+
);
|
|
1899
|
+
syncCopiedShadowHomeDirectories(
|
|
1900
|
+
originalCodexHome,
|
|
1901
|
+
shadowCodexHome,
|
|
1902
|
+
copiedDirectoryNames,
|
|
1903
|
+
);
|
|
1904
|
+
syncAdditionalShadowHomeFiles(
|
|
1905
|
+
originalCodexHome,
|
|
1906
|
+
shadowCodexHome,
|
|
1907
|
+
names,
|
|
1908
|
+
originalFileStates,
|
|
1909
|
+
tightenFile,
|
|
1910
|
+
skipSyncBackNames,
|
|
1911
|
+
);
|
|
1912
|
+
} catch {
|
|
1913
|
+
// Best-effort only; runtime auth refreshes should not fail cleanup.
|
|
1914
|
+
} finally {
|
|
1915
|
+
releaseLock();
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
function rewriteConfigTomlReasoningEffort(rawConfig, requestedModel) {
|
|
1921
|
+
const lineEnding = rawConfig.includes("\r\n") ? "\r\n" : "\n";
|
|
1922
|
+
let changed = false;
|
|
1923
|
+
const nextLines = rawConfig.split(/\r?\n/).map((line) => {
|
|
1924
|
+
const quotedMatch = line.match(
|
|
1925
|
+
/^(\s*model_reasoning_effort\s*=\s*)(["'])([^"']+)(\2.*)$/,
|
|
1926
|
+
);
|
|
1927
|
+
const bareMatch = quotedMatch
|
|
1928
|
+
? null
|
|
1929
|
+
: line.match(
|
|
1930
|
+
/^(\s*model_reasoning_effort\s*=\s*)([^\s#]+)(\s*(?:#.*)?)$/,
|
|
1931
|
+
);
|
|
1932
|
+
if (!quotedMatch && !bareMatch) return line;
|
|
1933
|
+
|
|
1934
|
+
const prefix = quotedMatch?.[1] ?? bareMatch?.[1] ?? "";
|
|
1935
|
+
const openingQuote = quotedMatch?.[2] ?? "";
|
|
1936
|
+
const currentEffort = quotedMatch?.[3] ?? bareMatch?.[2] ?? "";
|
|
1937
|
+
const suffix = quotedMatch?.[4] ?? bareMatch?.[3] ?? "";
|
|
1938
|
+
const coercedEffort = coerceReasoningEffortForModel(
|
|
1939
|
+
requestedModel,
|
|
1940
|
+
currentEffort,
|
|
1941
|
+
);
|
|
1942
|
+
if (coercedEffort === currentEffort) {
|
|
1943
|
+
return line;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
changed = true;
|
|
1947
|
+
return quotedMatch
|
|
1948
|
+
? `${prefix}${openingQuote}${coercedEffort}${suffix}`
|
|
1949
|
+
: `${prefix}${coercedEffort}${suffix}`;
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
if (!changed) {
|
|
1953
|
+
return rawConfig;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
return ensureTrailingNewline(nextLines.join(lineEnding));
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function resolveOriginalMultiAuthDir(env) {
|
|
1960
|
+
const explicit = (env.CODEX_MULTI_AUTH_DIR ?? "").trim();
|
|
1961
|
+
if (explicit.length > 0) {
|
|
1962
|
+
return explicit;
|
|
1963
|
+
}
|
|
1964
|
+
return undefined;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function parseRuntimeRotationProxyEnv(value) {
|
|
1968
|
+
if (value === undefined) return undefined;
|
|
1969
|
+
const normalized = value.trim().toLowerCase();
|
|
1970
|
+
if (normalized.length === 0) return undefined;
|
|
1971
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes") {
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
if (normalized === "0" || normalized === "false" || normalized === "no") {
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
if (!warnedInvalidRuntimeRotationProxyEnv) {
|
|
1978
|
+
warnedInvalidRuntimeRotationProxyEnv = true;
|
|
1979
|
+
console.error(
|
|
1980
|
+
"codex-multi-auth: ignoring invalid CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY value. Expected 0/1, true/false, or yes/no.",
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
return undefined;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
async function isRuntimeRotationProxyEnabled(rawArgs, baseEnv = process.env) {
|
|
1987
|
+
if ((baseEnv.CODEX_MULTI_AUTH_BYPASS ?? "").trim() === "1") {
|
|
1988
|
+
return false;
|
|
1989
|
+
}
|
|
1990
|
+
if (!shouldUseRuntimeRoutingForForwardedArgs(rawArgs)) {
|
|
1991
|
+
return false;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
const envOverride = parseRuntimeRotationProxyEnv(
|
|
1995
|
+
baseEnv.CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY,
|
|
1996
|
+
);
|
|
1997
|
+
if (envOverride !== undefined) {
|
|
1998
|
+
return envOverride;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
const configModule = await loadRuntimeRotationConfigModule();
|
|
2002
|
+
if (!configModule) {
|
|
2003
|
+
return false;
|
|
2004
|
+
}
|
|
2005
|
+
const pluginConfig = configModule.loadPluginConfig();
|
|
2006
|
+
return configModule.getCodexRuntimeRotationProxy(pluginConfig) === true;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
function createRuntimeRotationProxyClientApiKey() {
|
|
2010
|
+
return randomBytes(32).toString("hex");
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
function omitRuntimeRotationShadowHomeStateFiles(shadowCodexHome) {
|
|
2014
|
+
for (const name of RUNTIME_ROTATION_SHADOW_HOME_OMIT_STATE_FILES) {
|
|
2015
|
+
const targetPath = join(shadowCodexHome, name);
|
|
2016
|
+
try {
|
|
2017
|
+
if (!existsSync(targetPath)) {
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
if (isDirectoryLike(targetPath)) {
|
|
2021
|
+
removeDirectoryWithRetry(targetPath);
|
|
2022
|
+
} else {
|
|
2023
|
+
rmSync(targetPath, { force: true });
|
|
2024
|
+
}
|
|
2025
|
+
} catch {
|
|
2026
|
+
// Best-effort: stale official auth state should not block runtime rotation.
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
function resolveRuntimeRotationProxyOriginalCodexHome(baseEnv) {
|
|
2032
|
+
const override = (baseEnv[APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV] ?? "").trim();
|
|
2033
|
+
return override || resolveCodexHomeDir(baseEnv);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function createRuntimeRotationProxyCodexHome(
|
|
2037
|
+
baseEnv,
|
|
2038
|
+
proxyBaseUrl,
|
|
2039
|
+
clientApiKey,
|
|
2040
|
+
configTomlModule,
|
|
2041
|
+
) {
|
|
2042
|
+
const originalCodexHome = resolveRuntimeRotationProxyOriginalCodexHome(baseEnv);
|
|
2043
|
+
const shadowCodexHome = mkdtempSync(join(tmpdir(), "codex-multi-auth-runtime-home-"));
|
|
2044
|
+
let syncShadowHomeStateBack = () => {};
|
|
2045
|
+
const cleanup = () => {
|
|
2046
|
+
try {
|
|
2047
|
+
removeDirectoryWithRetry(shadowCodexHome);
|
|
2048
|
+
} catch {
|
|
2049
|
+
// Best-effort cleanup only.
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
const tightenShadowHomePermissions = (path) => {
|
|
2053
|
+
try {
|
|
2054
|
+
chmodSync(path, 0o600);
|
|
2055
|
+
} catch {
|
|
2056
|
+
// Best-effort only; permission semantics vary by platform.
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
try {
|
|
2061
|
+
syncShadowHomeStateBack = createShadowHomeMirror(
|
|
2062
|
+
originalCodexHome,
|
|
2063
|
+
shadowCodexHome,
|
|
2064
|
+
tightenShadowHomePermissions,
|
|
2065
|
+
{
|
|
2066
|
+
skipSyncBackNames: RUNTIME_ROTATION_SHADOW_HOME_OMIT_STATE_FILES,
|
|
2067
|
+
},
|
|
2068
|
+
);
|
|
2069
|
+
omitRuntimeRotationShadowHomeStateFiles(shadowCodexHome);
|
|
2070
|
+
const originalConfigPath = join(originalCodexHome, "config.toml");
|
|
2071
|
+
const rawConfig = existsSync(originalConfigPath)
|
|
2072
|
+
? readFileSync(originalConfigPath, "utf8")
|
|
2073
|
+
: "";
|
|
2074
|
+
const runtimeConfig = configTomlModule.rewriteConfigTomlForRuntimeRotationProvider(
|
|
2075
|
+
rawConfig,
|
|
2076
|
+
proxyBaseUrl,
|
|
2077
|
+
clientApiKey,
|
|
2078
|
+
);
|
|
2079
|
+
const runtimeConfigPath = join(shadowCodexHome, "config.toml");
|
|
2080
|
+
writeFileSync(runtimeConfigPath, runtimeConfig, "utf8");
|
|
2081
|
+
tightenShadowHomePermissions(runtimeConfigPath);
|
|
2082
|
+
} catch (error) {
|
|
2083
|
+
cleanup();
|
|
2084
|
+
throw error;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
const forwardedEnv = {
|
|
2088
|
+
...baseEnv,
|
|
2089
|
+
CODEX_HOME: shadowCodexHome,
|
|
2090
|
+
OPENAI_API_KEY: clientApiKey,
|
|
2091
|
+
};
|
|
2092
|
+
const originalMultiAuthDir = resolveOriginalMultiAuthDir(baseEnv);
|
|
2093
|
+
if (originalMultiAuthDir) {
|
|
2094
|
+
forwardedEnv.CODEX_MULTI_AUTH_DIR = originalMultiAuthDir;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
return {
|
|
2098
|
+
env: forwardedEnv,
|
|
2099
|
+
cleanup: () => {
|
|
2100
|
+
syncShadowHomeStateBack();
|
|
2101
|
+
cleanup();
|
|
2102
|
+
},
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
function appendNodeImportOption(nodeOptions, preloadPath) {
|
|
2107
|
+
const importOption = `--import=${pathToFileURL(preloadPath).href}`;
|
|
2108
|
+
const trimmed = (nodeOptions ?? "").trim();
|
|
2109
|
+
return trimmed.length > 0 ? `${trimmed} ${importOption}` : importOption;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function createRuntimeRotationAppServerPreloadSource(wrapperScriptPath) {
|
|
2113
|
+
return [
|
|
2114
|
+
'import { spawn } from "node:child_process";',
|
|
2115
|
+
'import { basename } from "node:path";',
|
|
2116
|
+
'import process from "node:process";',
|
|
2117
|
+
"",
|
|
2118
|
+
`const wrapperScriptPath = ${JSON.stringify(wrapperScriptPath)};`,
|
|
2119
|
+
`const accountLabelEnv = ${JSON.stringify(APP_SERVER_ACCOUNT_LABEL_ENV)};`,
|
|
2120
|
+
"const rawArgs = process.argv.slice(1);",
|
|
2121
|
+
"const firstArg = rawArgs[0] ?? \"\";",
|
|
2122
|
+
'if (basename(firstArg).toLowerCase() === "app-server") {',
|
|
2123
|
+
' const args = ["app-server", ...rawArgs.slice(1)];',
|
|
2124
|
+
" const env = {",
|
|
2125
|
+
" ...process.env,",
|
|
2126
|
+
' CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY: "0",',
|
|
2127
|
+
" [accountLabelEnv]: \"1\",",
|
|
2128
|
+
" };",
|
|
2129
|
+
" const child = spawn(process.execPath, [wrapperScriptPath, ...args], {",
|
|
2130
|
+
" stdio: \"inherit\",",
|
|
2131
|
+
" env,",
|
|
2132
|
+
" });",
|
|
2133
|
+
" child.once(\"error\", (error) => {",
|
|
2134
|
+
' console.error(`codex-multi-auth app-server shim failed: ${error instanceof Error ? error.message : String(error)}`);',
|
|
2135
|
+
" process.exit(1);",
|
|
2136
|
+
" });",
|
|
2137
|
+
" child.once(\"exit\", (code, signal) => {",
|
|
2138
|
+
" if (signal) {",
|
|
2139
|
+
' process.exit(signal === "SIGINT" ? 130 : 1);',
|
|
2140
|
+
" return;",
|
|
2141
|
+
" }",
|
|
2142
|
+
" process.exit(typeof code === \"number\" ? code : 1);",
|
|
2143
|
+
" });",
|
|
2144
|
+
" await new Promise(() => undefined);",
|
|
2145
|
+
"}",
|
|
2146
|
+
"",
|
|
2147
|
+
].join("\n");
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
function sweepStaleRuntimeRotationAppServerShimDirs(shimRootDir) {
|
|
2151
|
+
let entries = [];
|
|
2152
|
+
try {
|
|
2153
|
+
entries = readdirSync(shimRootDir, { withFileTypes: true });
|
|
2154
|
+
} catch {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
for (const entry of entries) {
|
|
2158
|
+
if (!entry.isDirectory() || !entry.name.startsWith(APP_SERVER_SHIM_HELPER_PREFIX)) {
|
|
2159
|
+
continue;
|
|
2160
|
+
}
|
|
2161
|
+
const pidText = entry.name.slice(APP_SERVER_SHIM_HELPER_PREFIX.length);
|
|
2162
|
+
const pid = Number.parseInt(pidText, 10);
|
|
2163
|
+
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid || isProcessAlive(pid)) {
|
|
2164
|
+
continue;
|
|
2165
|
+
}
|
|
2166
|
+
try {
|
|
2167
|
+
removeDirectoryWithRetry(join(shimRootDir, entry.name));
|
|
2168
|
+
} catch {
|
|
2169
|
+
// Best-effort stale shim cleanup only.
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
function installRuntimeRotationAppServerCliShim(forwardedEnv) {
|
|
2175
|
+
const shadowCodexHome = forwardedEnv.CODEX_HOME;
|
|
2176
|
+
if (!shadowCodexHome) {
|
|
2177
|
+
throw new Error("runtime app-server shim requires CODEX_HOME");
|
|
2178
|
+
}
|
|
2179
|
+
const multiAuthDir =
|
|
2180
|
+
resolveOriginalMultiAuthDir(forwardedEnv) ??
|
|
2181
|
+
join(resolveRuntimeRotationProxyOriginalCodexHome(forwardedEnv), "multi-auth");
|
|
2182
|
+
const shimRootDir = join(multiAuthDir, APP_SERVER_SHIM_DIR_NAME);
|
|
2183
|
+
sweepStaleRuntimeRotationAppServerShimDirs(shimRootDir);
|
|
2184
|
+
const shimDir = join(
|
|
2185
|
+
shimRootDir,
|
|
2186
|
+
`${APP_SERVER_SHIM_HELPER_PREFIX}${process.pid}`,
|
|
2187
|
+
);
|
|
2188
|
+
mkdirSync(shimDir, { recursive: true });
|
|
2189
|
+
const executableName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
2190
|
+
const executablePath = join(shimDir, executableName);
|
|
2191
|
+
const preloadPath = join(shimDir, "codex-multi-auth-app-server-preload.mjs");
|
|
2192
|
+
try {
|
|
2193
|
+
try {
|
|
2194
|
+
rmSync(executablePath, { force: true });
|
|
2195
|
+
} catch {
|
|
2196
|
+
// Best-effort stale shim cleanup only.
|
|
2197
|
+
}
|
|
2198
|
+
try {
|
|
2199
|
+
linkSync(process.execPath, executablePath);
|
|
2200
|
+
} catch {
|
|
2201
|
+
copyFileSync(process.execPath, executablePath);
|
|
2202
|
+
}
|
|
2203
|
+
if (process.platform !== "win32") {
|
|
2204
|
+
chmodSync(executablePath, 0o755);
|
|
2205
|
+
}
|
|
2206
|
+
writeFileSync(
|
|
2207
|
+
preloadPath,
|
|
2208
|
+
createRuntimeRotationAppServerPreloadSource(fileURLToPath(import.meta.url)),
|
|
2209
|
+
{ encoding: "utf8", mode: 0o600 },
|
|
2210
|
+
);
|
|
2211
|
+
try {
|
|
2212
|
+
chmodSync(preloadPath, 0o600);
|
|
2213
|
+
} catch {
|
|
2214
|
+
// Best-effort only; permission semantics vary by platform.
|
|
2215
|
+
}
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
try {
|
|
2218
|
+
removeDirectoryWithRetry(shimDir);
|
|
2219
|
+
} catch {
|
|
2220
|
+
// Preserve the original installation failure.
|
|
2221
|
+
}
|
|
2222
|
+
throw error;
|
|
2223
|
+
}
|
|
2224
|
+
forwardedEnv.CODEX_CLI_PATH = shimDir;
|
|
2225
|
+
forwardedEnv.NODE_OPTIONS = appendNodeImportOption(
|
|
2226
|
+
forwardedEnv.NODE_OPTIONS,
|
|
2227
|
+
preloadPath,
|
|
2228
|
+
);
|
|
2229
|
+
forwardedEnv.CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY = "0";
|
|
2230
|
+
forwardedEnv[APP_SERVER_ACCOUNT_LABEL_ENV] = "1";
|
|
2231
|
+
return shimDir;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
function resolveRuntimeRotationAppHelperStatusPath(env = process.env) {
|
|
2235
|
+
const multiAuthDir =
|
|
2236
|
+
resolveOriginalMultiAuthDir(env) ?? join(resolveCodexHomeDir(env), "multi-auth");
|
|
2237
|
+
return join(multiAuthDir, APP_RUNTIME_HELPER_STATUS_FILE);
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
function writeOwnerOnlyJsonFileAtomicSync(targetPath, payload) {
|
|
2241
|
+
const targetDir = dirname(targetPath);
|
|
2242
|
+
mkdirSync(targetDir, { recursive: true });
|
|
2243
|
+
for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
|
|
2244
|
+
const tempPath = join(
|
|
2245
|
+
targetDir,
|
|
2246
|
+
[
|
|
2247
|
+
`.${basename(targetPath)}`,
|
|
2248
|
+
String(process.pid),
|
|
2249
|
+
String(Date.now()),
|
|
2250
|
+
randomBytes(4).toString("hex"),
|
|
2251
|
+
"tmp",
|
|
2252
|
+
].join("."),
|
|
2253
|
+
);
|
|
2254
|
+
try {
|
|
2255
|
+
writeFileSync(tempPath, `${JSON.stringify(payload, null, 2)}\n`, {
|
|
2256
|
+
encoding: "utf8",
|
|
2257
|
+
mode: 0o600,
|
|
2258
|
+
});
|
|
2259
|
+
chmodSync(tempPath, 0o600);
|
|
2260
|
+
maybeThrowSimulatedShadowHomeSyncMetadataBusyError(targetPath);
|
|
2261
|
+
renameSync(tempPath, targetPath);
|
|
2262
|
+
chmodSync(targetPath, 0o600);
|
|
2263
|
+
return;
|
|
2264
|
+
} catch (error) {
|
|
2265
|
+
try {
|
|
2266
|
+
rmSync(tempPath, { force: true });
|
|
2267
|
+
} catch {
|
|
2268
|
+
// Preserve the original write failure.
|
|
2269
|
+
}
|
|
2270
|
+
if (
|
|
2271
|
+
isRetryableShadowHomeCleanupError(error) &&
|
|
2272
|
+
attempt < SHADOW_HOME_CLEANUP_BACKOFF_MS.length
|
|
2273
|
+
) {
|
|
2274
|
+
sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
|
|
2275
|
+
continue;
|
|
2276
|
+
}
|
|
2277
|
+
throw error;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
function resolveRuntimeRotationAppHelperIdleMs(env = process.env) {
|
|
2283
|
+
const parsed = Number.parseInt(
|
|
2284
|
+
env.CODEX_MULTI_AUTH_APP_ROTATION_IDLE_MS ?? "",
|
|
2285
|
+
10,
|
|
2286
|
+
);
|
|
2287
|
+
return Number.isFinite(parsed) && parsed > 0
|
|
2288
|
+
? Math.max(50, parsed)
|
|
2289
|
+
: DEFAULT_APP_RUNTIME_HELPER_IDLE_MS;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
function resolveRuntimeRotationAppHelperOwnerPid(env = process.env) {
|
|
2293
|
+
const parsed = Number.parseInt(env[APP_RUNTIME_HELPER_OWNER_PID_ENV] ?? "", 10);
|
|
2294
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
function isProcessAlive(pid) {
|
|
2298
|
+
try {
|
|
2299
|
+
process.kill(pid, 0);
|
|
2300
|
+
return true;
|
|
2301
|
+
} catch (error) {
|
|
2302
|
+
return error && typeof error === "object" && error.code === "EPERM";
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
function isRuntimeRotationAppHelperOwnerAlive(pid) {
|
|
2307
|
+
return isProcessAlive(pid);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
function resolveRuntimeRotationAppHelperDetachGraceMs(env = process.env) {
|
|
2311
|
+
const parsed = Number.parseInt(
|
|
2312
|
+
env.CODEX_MULTI_AUTH_APP_ROTATION_DETACH_GRACE_MS ?? "",
|
|
2313
|
+
10,
|
|
2314
|
+
);
|
|
2315
|
+
return Number.isFinite(parsed) && parsed >= 0
|
|
2316
|
+
? parsed
|
|
2317
|
+
: DEFAULT_APP_RUNTIME_HELPER_DETACH_GRACE_MS;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
function pickRuntimeRotationAppHelperEnv(env) {
|
|
2321
|
+
const picked = {
|
|
2322
|
+
CODEX_HOME: env.CODEX_HOME,
|
|
2323
|
+
OPENAI_API_KEY: env.OPENAI_API_KEY,
|
|
2324
|
+
};
|
|
2325
|
+
for (const name of [
|
|
2326
|
+
"CODEX_CLI_PATH",
|
|
2327
|
+
"NODE_OPTIONS",
|
|
2328
|
+
"CODEX_MULTI_AUTH_REAL_CODEX_BIN",
|
|
2329
|
+
"CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY",
|
|
2330
|
+
APP_SERVER_ACCOUNT_LABEL_ENV,
|
|
2331
|
+
]) {
|
|
2332
|
+
if (env[name]) {
|
|
2333
|
+
picked[name] = env[name];
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
if (env.CODEX_MULTI_AUTH_DIR) {
|
|
2337
|
+
picked.CODEX_MULTI_AUTH_DIR = env.CODEX_MULTI_AUTH_DIR;
|
|
2338
|
+
}
|
|
2339
|
+
return picked;
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
function writeRuntimeRotationAppHelperStatus(payload, env = process.env) {
|
|
2343
|
+
try {
|
|
2344
|
+
const statusPath = resolveRuntimeRotationAppHelperStatusPath(env);
|
|
2345
|
+
writeOwnerOnlyJsonFileAtomicSync(statusPath, payload);
|
|
2346
|
+
} catch {
|
|
2347
|
+
// Best-effort status only; the helper must not fail because telemetry is unavailable.
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
function createRuntimeRotationAppHelperStatus({
|
|
2352
|
+
proxyServer,
|
|
2353
|
+
startedAt,
|
|
2354
|
+
idleTimeoutMs,
|
|
2355
|
+
lastActivityAt,
|
|
2356
|
+
state,
|
|
2357
|
+
}) {
|
|
2358
|
+
const proxyStatus =
|
|
2359
|
+
typeof proxyServer?.getStatus === "function" ? proxyServer.getStatus() : {};
|
|
2360
|
+
const lastAccountIndex = proxyStatus.lastAccountIndex ?? null;
|
|
2361
|
+
const lastAccountLabel =
|
|
2362
|
+
typeof proxyStatus.lastAccountLabel === "string" &&
|
|
2363
|
+
!proxyStatus.lastAccountLabel.includes("@")
|
|
2364
|
+
? proxyStatus.lastAccountLabel
|
|
2365
|
+
: typeof lastAccountIndex === "number"
|
|
2366
|
+
? `Account ${lastAccountIndex + 1}`
|
|
2367
|
+
: null;
|
|
2368
|
+
return {
|
|
2369
|
+
version: 1,
|
|
2370
|
+
kind: "codex-app-runtime-rotation-helper",
|
|
2371
|
+
state,
|
|
2372
|
+
pid: process.pid,
|
|
2373
|
+
startedAt,
|
|
2374
|
+
updatedAt: Date.now(),
|
|
2375
|
+
baseUrl: proxyServer?.baseUrl ?? null,
|
|
2376
|
+
idleTimeoutMs,
|
|
2377
|
+
idleExpiresAt: lastActivityAt + idleTimeoutMs,
|
|
2378
|
+
totalRequests: proxyStatus.totalRequests ?? 0,
|
|
2379
|
+
upstreamRequests: proxyStatus.upstreamRequests ?? 0,
|
|
2380
|
+
retries: proxyStatus.retries ?? 0,
|
|
2381
|
+
rotations: proxyStatus.rotations ?? 0,
|
|
2382
|
+
lastAccountIndex,
|
|
2383
|
+
lastAccountLabel,
|
|
2384
|
+
lastAccountId: proxyStatus.lastAccountId ?? null,
|
|
2385
|
+
lastAccountUpdatedAt: proxyStatus.lastAccountUpdatedAt ?? null,
|
|
2386
|
+
lastError: proxyStatus.lastError ?? null,
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
async function runRuntimeRotationAppHelper() {
|
|
2391
|
+
let proxyServer = null;
|
|
2392
|
+
let shadowContext = null;
|
|
2393
|
+
let appServerShimDir = null;
|
|
2394
|
+
let statusTimer = null;
|
|
2395
|
+
let closing = false;
|
|
2396
|
+
const startedAt = Date.now();
|
|
2397
|
+
const idleTimeoutMs = resolveRuntimeRotationAppHelperIdleMs();
|
|
2398
|
+
const ownerPid = resolveRuntimeRotationAppHelperOwnerPid();
|
|
2399
|
+
let lastActivityAt = startedAt;
|
|
2400
|
+
let lastRequestCount = 0;
|
|
2401
|
+
|
|
2402
|
+
const publishStatus = (state) => {
|
|
2403
|
+
writeRuntimeRotationAppHelperStatus(
|
|
2404
|
+
createRuntimeRotationAppHelperStatus({
|
|
2405
|
+
proxyServer,
|
|
2406
|
+
startedAt,
|
|
2407
|
+
idleTimeoutMs,
|
|
2408
|
+
lastActivityAt,
|
|
2409
|
+
state,
|
|
2410
|
+
}),
|
|
2411
|
+
);
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
const cleanup = async (state = "stopped") => {
|
|
2415
|
+
if (closing) return;
|
|
2416
|
+
closing = true;
|
|
2417
|
+
if (statusTimer) {
|
|
2418
|
+
clearInterval(statusTimer);
|
|
2419
|
+
}
|
|
2420
|
+
try {
|
|
2421
|
+
if (appServerShimDir) {
|
|
2422
|
+
try {
|
|
2423
|
+
removeDirectoryWithRetry(appServerShimDir);
|
|
2424
|
+
} catch {
|
|
2425
|
+
// Best-effort shim cleanup only.
|
|
2426
|
+
}
|
|
2427
|
+
appServerShimDir = null;
|
|
2428
|
+
}
|
|
2429
|
+
shadowContext?.cleanup?.();
|
|
2430
|
+
} finally {
|
|
2431
|
+
try {
|
|
2432
|
+
await proxyServer?.close?.();
|
|
2433
|
+
} finally {
|
|
2434
|
+
publishStatus(state);
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2439
|
+
const exitAfterCleanup = (state, exitCode) => {
|
|
2440
|
+
void cleanup(state).finally(() => {
|
|
2441
|
+
process.exit(exitCode);
|
|
2442
|
+
});
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2445
|
+
process.once("SIGINT", () => exitAfterCleanup("stopped", 130));
|
|
2446
|
+
process.once("SIGTERM", () => exitAfterCleanup("stopped", 0));
|
|
2447
|
+
process.once("SIGHUP", () => exitAfterCleanup("stopped", 0));
|
|
2448
|
+
|
|
2449
|
+
try {
|
|
2450
|
+
const proxyModule = await loadRuntimeRotationProxyModule();
|
|
2451
|
+
if (!proxyModule) {
|
|
2452
|
+
throw new Error("runtime rotation proxy module is unavailable");
|
|
2453
|
+
}
|
|
2454
|
+
const configTomlModule = await loadRuntimeConfigTomlModule();
|
|
2455
|
+
if (!configTomlModule) {
|
|
2456
|
+
throw new Error("runtime rotation config helpers are unavailable");
|
|
2457
|
+
}
|
|
2458
|
+
const clientApiKey = createRuntimeRotationProxyClientApiKey();
|
|
2459
|
+
proxyServer = await proxyModule.startRuntimeRotationProxy({ clientApiKey });
|
|
2460
|
+
shadowContext = createRuntimeRotationProxyCodexHome(
|
|
2461
|
+
process.env,
|
|
2462
|
+
proxyServer.baseUrl,
|
|
2463
|
+
clientApiKey,
|
|
2464
|
+
configTomlModule,
|
|
2465
|
+
);
|
|
2466
|
+
appServerShimDir = installRuntimeRotationAppServerCliShim(shadowContext.env);
|
|
2467
|
+
lastRequestCount = proxyServer.getStatus?.().totalRequests ?? 0;
|
|
2468
|
+
publishStatus("running");
|
|
2469
|
+
process.stdout.write(
|
|
2470
|
+
`${JSON.stringify({
|
|
2471
|
+
type: "ready",
|
|
2472
|
+
pid: process.pid,
|
|
2473
|
+
baseUrl: proxyServer.baseUrl,
|
|
2474
|
+
statusPath: resolveRuntimeRotationAppHelperStatusPath(),
|
|
2475
|
+
env: pickRuntimeRotationAppHelperEnv(shadowContext.env),
|
|
2476
|
+
})}\n`,
|
|
2477
|
+
);
|
|
2478
|
+
|
|
2479
|
+
statusTimer = setInterval(() => {
|
|
2480
|
+
const currentTime = Date.now();
|
|
2481
|
+
const requestCount = proxyServer?.getStatus?.().totalRequests ?? 0;
|
|
2482
|
+
if (requestCount !== lastRequestCount) {
|
|
2483
|
+
lastRequestCount = requestCount;
|
|
2484
|
+
lastActivityAt = currentTime;
|
|
2485
|
+
}
|
|
2486
|
+
if (ownerPid && isRuntimeRotationAppHelperOwnerAlive(ownerPid)) {
|
|
2487
|
+
lastActivityAt = currentTime;
|
|
2488
|
+
}
|
|
2489
|
+
publishStatus("running");
|
|
2490
|
+
if (currentTime - lastActivityAt >= idleTimeoutMs) {
|
|
2491
|
+
exitAfterCleanup("idle-timeout", 0);
|
|
2492
|
+
}
|
|
2493
|
+
}, Math.min(1_000, Math.max(50, Math.floor(idleTimeoutMs / 2))));
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
process.stdout.write(
|
|
2496
|
+
`${JSON.stringify({
|
|
2497
|
+
type: "error",
|
|
2498
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2499
|
+
})}\n`,
|
|
2500
|
+
);
|
|
2501
|
+
await cleanup("error");
|
|
2502
|
+
return 1;
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
await new Promise(() => undefined);
|
|
2506
|
+
return 0;
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
function waitForRuntimeRotationAppHelperExit(helper, timeoutMs = 2_000) {
|
|
2510
|
+
return new Promise((resolve) => {
|
|
2511
|
+
let settled = false;
|
|
2512
|
+
let timer = null;
|
|
2513
|
+
const finish = () => {
|
|
2514
|
+
if (settled) return;
|
|
2515
|
+
settled = true;
|
|
2516
|
+
if (timer) clearTimeout(timer);
|
|
2517
|
+
resolve();
|
|
2518
|
+
};
|
|
2519
|
+
timer = setTimeout(finish, timeoutMs);
|
|
2520
|
+
helper.once("close", finish);
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
function stopRuntimeRotationAppHelper(helper) {
|
|
2525
|
+
if (!helper || helper.killed) {
|
|
2526
|
+
return Promise.resolve();
|
|
2527
|
+
}
|
|
2528
|
+
try {
|
|
2529
|
+
helper.kill("SIGTERM");
|
|
2530
|
+
} catch {
|
|
2531
|
+
return Promise.resolve();
|
|
2532
|
+
}
|
|
2533
|
+
return waitForRuntimeRotationAppHelperExit(helper);
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
function startRuntimeRotationAppHelper(baseContext) {
|
|
2537
|
+
const realCodexHome =
|
|
2538
|
+
baseContext.originalCodexHome ??
|
|
2539
|
+
resolveRuntimeRotationProxyOriginalCodexHome(baseContext.env);
|
|
2540
|
+
return new Promise((resolve, reject) => {
|
|
2541
|
+
let stdoutBuffer = "";
|
|
2542
|
+
let stderrBuffer = "";
|
|
2543
|
+
let settled = false;
|
|
2544
|
+
const helper = spawn(
|
|
2545
|
+
process.execPath,
|
|
2546
|
+
[fileURLToPath(import.meta.url), INTERNAL_RUNTIME_ROTATION_APP_HELPER_ARG],
|
|
2547
|
+
{
|
|
2548
|
+
env: {
|
|
2549
|
+
...baseContext.env,
|
|
2550
|
+
[APP_RUNTIME_HELPER_OWNER_PID_ENV]: String(process.pid),
|
|
2551
|
+
[APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV]: realCodexHome,
|
|
2552
|
+
},
|
|
2553
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2554
|
+
detached: true,
|
|
2555
|
+
},
|
|
2556
|
+
);
|
|
2557
|
+
let timeout = null;
|
|
2558
|
+
const finish = (result) => {
|
|
2559
|
+
if (settled) return;
|
|
2560
|
+
settled = true;
|
|
2561
|
+
if (timeout) clearTimeout(timeout);
|
|
2562
|
+
resolve(result);
|
|
2563
|
+
};
|
|
2564
|
+
const fail = (error) => {
|
|
2565
|
+
if (settled) return;
|
|
2566
|
+
settled = true;
|
|
2567
|
+
if (timeout) clearTimeout(timeout);
|
|
2568
|
+
void stopRuntimeRotationAppHelper(helper).finally(() => reject(error));
|
|
2569
|
+
};
|
|
2570
|
+
timeout = setTimeout(() => {
|
|
2571
|
+
fail(new Error("timed out waiting for runtime rotation app helper"));
|
|
2572
|
+
}, APP_RUNTIME_HELPER_LAUNCH_TIMEOUT_MS);
|
|
2573
|
+
helper.stdout?.setEncoding("utf8");
|
|
2574
|
+
helper.stdout?.on("data", (chunk) => {
|
|
2575
|
+
stdoutBuffer += chunk;
|
|
2576
|
+
const newlineIndex = stdoutBuffer.indexOf("\n");
|
|
2577
|
+
if (newlineIndex < 0) return;
|
|
2578
|
+
const line = stdoutBuffer.slice(0, newlineIndex).trim();
|
|
2579
|
+
try {
|
|
2580
|
+
const message = JSON.parse(line);
|
|
2581
|
+
if (message?.type === "ready" && message.env && message.pid) {
|
|
2582
|
+
finish({ helper, message });
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
fail(
|
|
2586
|
+
new Error(
|
|
2587
|
+
message?.message ??
|
|
2588
|
+
"runtime rotation app helper returned an invalid startup response",
|
|
2589
|
+
),
|
|
2590
|
+
);
|
|
2591
|
+
} catch (error) {
|
|
2592
|
+
fail(error);
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
helper.stderr?.setEncoding("utf8");
|
|
2596
|
+
helper.stderr?.on("data", (chunk) => {
|
|
2597
|
+
stderrBuffer += chunk;
|
|
2598
|
+
});
|
|
2599
|
+
helper.once("error", fail);
|
|
2600
|
+
helper.once("close", (code) => {
|
|
2601
|
+
if (settled) return;
|
|
2602
|
+
fail(
|
|
2603
|
+
new Error(
|
|
2604
|
+
`runtime rotation app helper exited before startup (code ${code ?? "unknown"}): ${stderrBuffer.trim()}`,
|
|
2605
|
+
),
|
|
2606
|
+
);
|
|
2607
|
+
});
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
async function createRuntimeRotationAppHelperContext(baseContext, configTomlModule) {
|
|
2612
|
+
const startedAt = Date.now();
|
|
2613
|
+
const { helper, message } = await startRuntimeRotationAppHelper(baseContext);
|
|
2614
|
+
const helperEnv = message.env ?? {};
|
|
2615
|
+
const detachGraceMs = resolveRuntimeRotationAppHelperDetachGraceMs(baseContext.env);
|
|
2616
|
+
|
|
2617
|
+
const cleanup = async ({ exitCode } = {}) => {
|
|
2618
|
+
const livedMs = Date.now() - startedAt;
|
|
2619
|
+
if (exitCode === 0 && livedMs < detachGraceMs) {
|
|
2620
|
+
helper.stdout?.destroy();
|
|
2621
|
+
helper.stderr?.destroy();
|
|
2622
|
+
helper.unref();
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
await stopRuntimeRotationAppHelper(helper);
|
|
2626
|
+
};
|
|
2627
|
+
|
|
2628
|
+
return {
|
|
2629
|
+
args: [
|
|
2630
|
+
...baseContext.args,
|
|
2631
|
+
"-c",
|
|
2632
|
+
`model_provider=${configTomlModule.tomlStringLiteral(RUNTIME_ROTATION_PROXY_PROVIDER_ID)}`,
|
|
2633
|
+
],
|
|
2634
|
+
env: {
|
|
2635
|
+
...baseContext.env,
|
|
2636
|
+
...helperEnv,
|
|
2637
|
+
},
|
|
2638
|
+
cleanup: async (details) => {
|
|
2639
|
+
try {
|
|
2640
|
+
await cleanup(details);
|
|
2641
|
+
} finally {
|
|
2642
|
+
baseContext.cleanup?.();
|
|
2643
|
+
}
|
|
2644
|
+
},
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
async function createRuntimeRotationProxyContextIfEnabled(
|
|
2649
|
+
baseContext,
|
|
2650
|
+
rawArgs,
|
|
2651
|
+
) {
|
|
2652
|
+
const enabled = await isRuntimeRotationProxyEnabled(rawArgs, baseContext.env);
|
|
2653
|
+
if (!enabled) {
|
|
2654
|
+
return baseContext;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
const configTomlModule = await loadRuntimeConfigTomlModule();
|
|
2658
|
+
if (!configTomlModule) {
|
|
2659
|
+
console.error(
|
|
2660
|
+
"codex-multi-auth runtime rotation config helpers are unavailable; continuing without runtime rotation.",
|
|
2661
|
+
);
|
|
2662
|
+
return baseContext;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
if (isCodexAppCommand(rawArgs)) {
|
|
2666
|
+
return createRuntimeRotationAppHelperContext(baseContext, configTomlModule);
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
const proxyModule = await loadRuntimeRotationProxyModule();
|
|
2670
|
+
if (!proxyModule) {
|
|
2671
|
+
console.error(
|
|
2672
|
+
"codex-multi-auth runtime rotation proxy is unavailable; continuing without runtime rotation.",
|
|
2673
|
+
);
|
|
2674
|
+
return baseContext;
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
let proxyServer;
|
|
2678
|
+
let shadowContext;
|
|
2679
|
+
try {
|
|
2680
|
+
const clientApiKey = createRuntimeRotationProxyClientApiKey();
|
|
2681
|
+
proxyServer = await proxyModule.startRuntimeRotationProxy({ clientApiKey });
|
|
2682
|
+
shadowContext = createRuntimeRotationProxyCodexHome(
|
|
2683
|
+
baseContext.env,
|
|
2684
|
+
proxyServer.baseUrl,
|
|
2685
|
+
clientApiKey,
|
|
2686
|
+
configTomlModule,
|
|
2687
|
+
);
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
try {
|
|
2690
|
+
await proxyServer?.close?.();
|
|
2691
|
+
} catch {
|
|
2692
|
+
// Best-effort cleanup only.
|
|
2693
|
+
}
|
|
2694
|
+
console.error(
|
|
2695
|
+
`codex-multi-auth runtime rotation proxy failed to start; continuing without runtime rotation: ${error instanceof Error ? error.message : String(error)}`,
|
|
2696
|
+
);
|
|
2697
|
+
return baseContext;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
const cleanup = async () => {
|
|
2701
|
+
try {
|
|
2702
|
+
shadowContext.cleanup?.();
|
|
2703
|
+
} finally {
|
|
2704
|
+
try {
|
|
2705
|
+
await proxyServer.close();
|
|
2706
|
+
} finally {
|
|
2707
|
+
baseContext.cleanup?.();
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
};
|
|
2711
|
+
|
|
2712
|
+
return {
|
|
2713
|
+
args: [
|
|
2714
|
+
...baseContext.args,
|
|
2715
|
+
"-c",
|
|
2716
|
+
`model_provider=${configTomlModule.tomlStringLiteral(RUNTIME_ROTATION_PROXY_PROVIDER_ID)}`,
|
|
2717
|
+
],
|
|
2718
|
+
env: shadowContext.env,
|
|
2719
|
+
cleanup,
|
|
2720
|
+
proxyAppServerAccountRead: isCodexAppServerCommand(rawArgs),
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
async function loadRuntimeObservabilityModule() {
|
|
2725
|
+
try {
|
|
2726
|
+
const mod = await import("../dist/lib/runtime/runtime-observability.js");
|
|
2727
|
+
if (
|
|
2728
|
+
typeof mod.loadPersistedRuntimeObservabilitySnapshot !== "function" ||
|
|
2729
|
+
typeof mod.mutateRuntimeObservabilitySnapshot !== "function"
|
|
2730
|
+
) {
|
|
2731
|
+
return null;
|
|
2732
|
+
}
|
|
2733
|
+
return mod;
|
|
2734
|
+
} catch (error) {
|
|
2735
|
+
if (
|
|
2736
|
+
error &&
|
|
2737
|
+
typeof error === "object" &&
|
|
2738
|
+
"code" in error &&
|
|
2739
|
+
error.code === "ERR_MODULE_NOT_FOUND"
|
|
2740
|
+
) {
|
|
2741
|
+
return null;
|
|
2742
|
+
}
|
|
2743
|
+
throw error;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
function isPureHelpOrVersionArgs(rawArgs) {
|
|
2748
|
+
if (!Array.isArray(rawArgs) || rawArgs.length === 0) {
|
|
2749
|
+
return false;
|
|
2750
|
+
}
|
|
2751
|
+
return rawArgs.every((arg) =>
|
|
2752
|
+
typeof arg === "string" && ["--help", "-h", "--version", "-V"].includes(arg),
|
|
2753
|
+
);
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
function consumesNextArg(arg) {
|
|
2757
|
+
return new Set([
|
|
2758
|
+
"-c",
|
|
2759
|
+
"--config",
|
|
2760
|
+
"--enable",
|
|
2761
|
+
"--disable",
|
|
2762
|
+
"--listen",
|
|
2763
|
+
"--remote",
|
|
2764
|
+
"--remote-auth-token-env",
|
|
2765
|
+
"--ws-auth",
|
|
2766
|
+
"--ws-token-file",
|
|
2767
|
+
"--ws-token-sha256",
|
|
2768
|
+
"--ws-shared-secret-file",
|
|
2769
|
+
"--ws-issuer",
|
|
2770
|
+
"--ws-audience",
|
|
2771
|
+
"--ws-max-clock-skew-seconds",
|
|
2772
|
+
"--download-url",
|
|
2773
|
+
"-i",
|
|
2774
|
+
"--image",
|
|
2775
|
+
"-m",
|
|
2776
|
+
"--model",
|
|
2777
|
+
"--local-provider",
|
|
2778
|
+
"-p",
|
|
2779
|
+
"--profile",
|
|
2780
|
+
"-s",
|
|
2781
|
+
"--sandbox",
|
|
2782
|
+
"-a",
|
|
2783
|
+
"--ask-for-approval",
|
|
2784
|
+
"-C",
|
|
2785
|
+
"--cd",
|
|
2786
|
+
"--add-dir",
|
|
2787
|
+
"--output-schema",
|
|
2788
|
+
"--color",
|
|
2789
|
+
"-o",
|
|
2790
|
+
"--output-last-message",
|
|
2791
|
+
]).has(arg);
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
function findForwardedCommand(rawArgs) {
|
|
2795
|
+
if (!Array.isArray(rawArgs) || rawArgs.length === 0) {
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
2799
|
+
const arg = rawArgs[i];
|
|
2800
|
+
if (typeof arg !== "string" || arg.length === 0) continue;
|
|
2801
|
+
if (arg === "--") {
|
|
2802
|
+
return i + 1 < rawArgs.length
|
|
2803
|
+
? { command: rawArgs[i + 1], index: i + 1 }
|
|
2804
|
+
: null;
|
|
2805
|
+
}
|
|
2806
|
+
if (arg.startsWith("--config=")) {
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
if (arg.startsWith("--") || (arg.startsWith("-") && arg !== "-")) {
|
|
2810
|
+
if (consumesNextArg(arg)) {
|
|
2811
|
+
i += 1;
|
|
2812
|
+
}
|
|
2813
|
+
continue;
|
|
2814
|
+
}
|
|
2815
|
+
return { command: arg, index: i };
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
return null;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
function findForwardedSubcommand(rawArgs, commandIndex) {
|
|
2822
|
+
for (let i = commandIndex + 1; i < rawArgs.length; i += 1) {
|
|
2823
|
+
const arg = rawArgs[i];
|
|
2824
|
+
if (typeof arg !== "string" || arg.length === 0) continue;
|
|
2825
|
+
if (arg === "--") {
|
|
2826
|
+
return i + 1 < rawArgs.length ? rawArgs[i + 1] : null;
|
|
2827
|
+
}
|
|
2828
|
+
if (arg.startsWith("--config=")) {
|
|
2829
|
+
continue;
|
|
2830
|
+
}
|
|
2831
|
+
if (arg.startsWith("--") || (arg.startsWith("-") && arg !== "-")) {
|
|
2832
|
+
if (consumesNextArg(arg)) {
|
|
2833
|
+
i += 1;
|
|
2834
|
+
}
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
return arg;
|
|
2838
|
+
}
|
|
2839
|
+
return null;
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
function hasHelpFlagAfterCommand(rawArgs, commandIndex) {
|
|
2843
|
+
for (let i = commandIndex + 1; i < rawArgs.length; i += 1) {
|
|
2844
|
+
const arg = rawArgs[i];
|
|
2845
|
+
if (arg === "--") return false;
|
|
2846
|
+
if (arg === "--help" || arg === "-h" || arg === "help") return true;
|
|
2847
|
+
if (typeof arg === "string" && consumesNextArg(arg)) {
|
|
2848
|
+
i += 1;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
return false;
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
function isCodexAppCommand(rawArgs) {
|
|
2855
|
+
return findForwardedCommand(rawArgs)?.command === "app";
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
function isCodexAppServerCommand(rawArgs) {
|
|
2859
|
+
return findForwardedCommand(rawArgs)?.command === "app-server";
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
function shouldUseRuntimeRoutingForForwardedArgs(rawArgs) {
|
|
2863
|
+
if (!Array.isArray(rawArgs) || rawArgs.length === 0) {
|
|
2864
|
+
return true;
|
|
2865
|
+
}
|
|
2866
|
+
if (isPureHelpOrVersionArgs(rawArgs)) {
|
|
2867
|
+
return false;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
const command = findForwardedCommand(rawArgs);
|
|
2871
|
+
if (!command) {
|
|
2872
|
+
return true;
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
const requestCommands = new Set(["exec", "review", "resume", "fork", "app"]);
|
|
2876
|
+
const nonRequestCommands = new Set([
|
|
2877
|
+
"help",
|
|
2878
|
+
"completion",
|
|
2879
|
+
"login",
|
|
2880
|
+
"logout",
|
|
2881
|
+
"mcp",
|
|
2882
|
+
"mcp-server",
|
|
2883
|
+
"sandbox",
|
|
2884
|
+
"debug",
|
|
2885
|
+
"apply",
|
|
2886
|
+
"cloud",
|
|
2887
|
+
"features",
|
|
2888
|
+
"auth",
|
|
2889
|
+
]);
|
|
2890
|
+
|
|
2891
|
+
if (command.command === "app-server") {
|
|
2892
|
+
if (hasHelpFlagAfterCommand(rawArgs, command.index)) {
|
|
2893
|
+
return false;
|
|
2894
|
+
}
|
|
2895
|
+
const subcommand = findForwardedSubcommand(rawArgs, command.index);
|
|
2896
|
+
return !new Set(["help", "generate-ts", "generate-json-schema"]).has(
|
|
2897
|
+
subcommand ?? "",
|
|
2898
|
+
);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
if (command.command === "app" && hasHelpFlagAfterCommand(rawArgs, command.index)) {
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2904
|
+
if (requestCommands.has(command.command)) {
|
|
2905
|
+
return true;
|
|
2906
|
+
}
|
|
2907
|
+
if (nonRequestCommands.has(command.command)) {
|
|
2908
|
+
return false;
|
|
2909
|
+
}
|
|
2910
|
+
return true;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
function shouldTrackForwardedRuntimeObservability(rawArgs) {
|
|
2914
|
+
return shouldUseRuntimeRoutingForForwardedArgs(rawArgs);
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
function shouldCaptureForwardedOutputForArgs(rawArgs, env) {
|
|
2918
|
+
if (isCodexAppServerCommand(rawArgs)) {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
return shouldCaptureForwardedCodexOutput(env);
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
function createRuntimeSnapshotChangeToken(snapshot) {
|
|
2925
|
+
return JSON.stringify({
|
|
2926
|
+
responsesRequests: snapshot?.responsesRequests ?? null,
|
|
2927
|
+
authRefreshRequests: snapshot?.authRefreshRequests ?? null,
|
|
2928
|
+
diagnosticProbeRequests: snapshot?.diagnosticProbeRequests ?? null,
|
|
2929
|
+
totalRequests: snapshot?.runtimeMetrics?.totalRequests ?? null,
|
|
2930
|
+
successfulRequests: snapshot?.runtimeMetrics?.successfulRequests ?? null,
|
|
2931
|
+
failedRequests: snapshot?.runtimeMetrics?.failedRequests ?? null,
|
|
2932
|
+
lastRequestAt: snapshot?.runtimeMetrics?.lastRequestAt ?? null,
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
async function loadRuntimeSnapshotWithRetry(runtimeObservabilityModule) {
|
|
2937
|
+
let snapshot = null;
|
|
2938
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
2939
|
+
snapshot =
|
|
2940
|
+
(await runtimeObservabilityModule.loadPersistedRuntimeObservabilitySnapshot()) ??
|
|
2941
|
+
null;
|
|
2942
|
+
if (snapshot) {
|
|
2943
|
+
return snapshot;
|
|
2944
|
+
}
|
|
2945
|
+
if (attempt < 4) {
|
|
2946
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
return snapshot;
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
async function withForwardedRuntimeObservability(rawArgs, runForwardedCommand) {
|
|
2953
|
+
if (!shouldTrackForwardedRuntimeObservability(rawArgs)) {
|
|
2954
|
+
return runForwardedCommand();
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
const runtimeObservabilityModule = await loadRuntimeObservabilityModule();
|
|
2958
|
+
if (!runtimeObservabilityModule) {
|
|
2959
|
+
return runForwardedCommand();
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
const beforeSnapshot = await loadRuntimeSnapshotWithRetry(runtimeObservabilityModule);
|
|
2963
|
+
const beforeToken = createRuntimeSnapshotChangeToken(beforeSnapshot);
|
|
2964
|
+
const startedAt = Date.now();
|
|
2965
|
+
const exitCode = await runForwardedCommand();
|
|
2966
|
+
const afterSnapshot = await loadRuntimeSnapshotWithRetry(runtimeObservabilityModule);
|
|
2967
|
+
const afterToken = createRuntimeSnapshotChangeToken(afterSnapshot);
|
|
2968
|
+
if (afterToken !== beforeToken) {
|
|
2969
|
+
return exitCode;
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
runtimeObservabilityModule.mutateRuntimeObservabilitySnapshot((snapshot) => {
|
|
2973
|
+
snapshot.currentRequestId = null;
|
|
2974
|
+
snapshot.responsesRequests += 1;
|
|
2975
|
+
snapshot.runtimeMetrics.totalRequests += 1;
|
|
2976
|
+
snapshot.runtimeMetrics.responsesRequests += 1;
|
|
2977
|
+
snapshot.runtimeMetrics.cumulativeLatencyMs += Math.max(0, Date.now() - startedAt);
|
|
2978
|
+
snapshot.runtimeMetrics.lastRequestAt = Date.now();
|
|
2979
|
+
if (!snapshot.runtimeMetrics.startedAt) {
|
|
2980
|
+
snapshot.runtimeMetrics.startedAt = startedAt;
|
|
2981
|
+
}
|
|
2982
|
+
if (exitCode === 0) {
|
|
2983
|
+
snapshot.runtimeMetrics.successfulRequests += 1;
|
|
2984
|
+
snapshot.runtimeMetrics.lastError = null;
|
|
2985
|
+
} else {
|
|
2986
|
+
snapshot.runtimeMetrics.failedRequests += 1;
|
|
2987
|
+
snapshot.runtimeMetrics.lastError = `forwarded-codex-exit:${exitCode}`;
|
|
2988
|
+
}
|
|
2989
|
+
});
|
|
2990
|
+
return exitCode;
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
function createCompatibilityCodexHome(
|
|
2994
|
+
processedArgs,
|
|
2995
|
+
requestedModel,
|
|
2996
|
+
baseEnv = process.env,
|
|
2997
|
+
) {
|
|
2998
|
+
const originalCodexHome = resolveCodexHomeDir(baseEnv);
|
|
2999
|
+
if (!requestedModel) {
|
|
3000
|
+
return {
|
|
3001
|
+
args: processedArgs,
|
|
3002
|
+
env: baseEnv,
|
|
3003
|
+
cleanup: undefined,
|
|
3004
|
+
originalCodexHome,
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
const configPath = join(originalCodexHome, "config.toml");
|
|
3009
|
+
if (!existsSync(configPath)) {
|
|
3010
|
+
return {
|
|
3011
|
+
args: processedArgs,
|
|
3012
|
+
env: baseEnv,
|
|
3013
|
+
cleanup: undefined,
|
|
3014
|
+
originalCodexHome,
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
const rawConfig = readFileSync(configPath, "utf8");
|
|
3019
|
+
const compatConfig = rewriteConfigTomlReasoningEffort(
|
|
3020
|
+
rawConfig,
|
|
3021
|
+
requestedModel,
|
|
3022
|
+
);
|
|
3023
|
+
if (compatConfig === rawConfig) {
|
|
3024
|
+
return {
|
|
3025
|
+
args: processedArgs,
|
|
3026
|
+
env: baseEnv,
|
|
3027
|
+
cleanup: undefined,
|
|
3028
|
+
originalCodexHome,
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
const shadowCodexHome = mkdtempSync(join(tmpdir(), "codex-multi-auth-home-"));
|
|
3033
|
+
let syncShadowHomeStateBack = () => {};
|
|
3034
|
+
const cleanup = () => {
|
|
3035
|
+
try {
|
|
3036
|
+
removeDirectoryWithRetry(shadowCodexHome);
|
|
3037
|
+
} catch {
|
|
3038
|
+
// Best-effort cleanup only.
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
3041
|
+
const tightenShadowHomePermissions = (path) => {
|
|
3042
|
+
try {
|
|
3043
|
+
chmodSync(path, 0o600);
|
|
3044
|
+
} catch {
|
|
3045
|
+
// Best-effort only; permission semantics vary by platform.
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
try {
|
|
3049
|
+
syncShadowHomeStateBack = createShadowHomeMirror(
|
|
3050
|
+
originalCodexHome,
|
|
3051
|
+
shadowCodexHome,
|
|
3052
|
+
tightenShadowHomePermissions,
|
|
3053
|
+
);
|
|
3054
|
+
const compatConfigPath = join(shadowCodexHome, "config.toml");
|
|
3055
|
+
writeFileSync(compatConfigPath, compatConfig, "utf8");
|
|
3056
|
+
tightenShadowHomePermissions(compatConfigPath);
|
|
3057
|
+
} catch (error) {
|
|
3058
|
+
cleanup();
|
|
3059
|
+
throw error;
|
|
3060
|
+
}
|
|
3061
|
+
const cleanupWithSync = () => {
|
|
3062
|
+
syncShadowHomeStateBack();
|
|
3063
|
+
cleanup();
|
|
3064
|
+
};
|
|
3065
|
+
|
|
3066
|
+
const forwardedEnv = {
|
|
3067
|
+
...baseEnv,
|
|
3068
|
+
CODEX_HOME: shadowCodexHome,
|
|
3069
|
+
};
|
|
3070
|
+
const originalMultiAuthDir = resolveOriginalMultiAuthDir(baseEnv);
|
|
3071
|
+
if (originalMultiAuthDir) {
|
|
3072
|
+
forwardedEnv.CODEX_MULTI_AUTH_DIR = originalMultiAuthDir;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
return {
|
|
3076
|
+
args: processedArgs,
|
|
3077
|
+
env: forwardedEnv,
|
|
3078
|
+
cleanup: cleanupWithSync,
|
|
3079
|
+
originalCodexHome,
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
function buildForwardArgs(rawArgs) {
|
|
3084
|
+
const { args: compatibilityArgs, requestedModel } = rewriteReasoningConfigArgs(rawArgs);
|
|
3085
|
+
const forceFileAuthStore = (process.env.CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE ?? "1").trim() !== "0";
|
|
3086
|
+
if (!forceFileAuthStore) {
|
|
3087
|
+
return { args: compatibilityArgs, requestedModel };
|
|
3088
|
+
}
|
|
3089
|
+
if (hasCliAuthCredentialsStoreOverride(compatibilityArgs)) {
|
|
3090
|
+
return { args: compatibilityArgs, requestedModel };
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
return {
|
|
3094
|
+
args: [
|
|
3095
|
+
...compatibilityArgs,
|
|
3096
|
+
"-c",
|
|
3097
|
+
'cli_auth_credentials_store="file"',
|
|
3098
|
+
],
|
|
3099
|
+
requestedModel,
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
function normalizeExitCode(value) {
|
|
3104
|
+
if (typeof value === "number" && Number.isInteger(value)) {
|
|
3105
|
+
return value;
|
|
3106
|
+
}
|
|
3107
|
+
const parsed = Number(value);
|
|
3108
|
+
if (Number.isInteger(parsed)) {
|
|
3109
|
+
return parsed;
|
|
3110
|
+
}
|
|
3111
|
+
return 1;
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
const WINDOWS_SHIM_MARKER = "codex-multi-auth windows shim guardian v1";
|
|
3115
|
+
const POWERSHELL_PROFILE_MARKER_START = "# >>> codex-multi-auth shell guard >>>";
|
|
3116
|
+
const POWERSHELL_PROFILE_MARKER_END = "# <<< codex-multi-auth shell guard <<<";
|
|
3117
|
+
|
|
3118
|
+
function shouldInstallWindowsBatchShimGuard() {
|
|
3119
|
+
if (process.platform !== "win32") return false;
|
|
3120
|
+
const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "0").trim();
|
|
3121
|
+
return override !== "0";
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
function resolveWindowsShimDirectoryFromInvocation() {
|
|
3125
|
+
const invokedScript = (process.argv[1] ?? "").trim();
|
|
3126
|
+
if (invokedScript.length === 0) return null;
|
|
3127
|
+
const resolvedScript = resolvePath(invokedScript);
|
|
3128
|
+
const scriptDir = dirname(resolvedScript);
|
|
3129
|
+
const packageRoot = dirname(scriptDir);
|
|
3130
|
+
const nodeModulesDir = dirname(packageRoot);
|
|
3131
|
+
if (basename(nodeModulesDir).toLowerCase() !== "node_modules") {
|
|
3132
|
+
return null;
|
|
3133
|
+
}
|
|
3134
|
+
const shimDir = dirname(nodeModulesDir);
|
|
3135
|
+
if (existsSync(join(shimDir, "codex-multi-auth.cmd"))) {
|
|
3136
|
+
return shimDir;
|
|
3137
|
+
}
|
|
3138
|
+
return null;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
function resolveWindowsShimDirectoryFromPath() {
|
|
3142
|
+
const fromInvocation = resolveWindowsShimDirectoryFromInvocation();
|
|
3143
|
+
if (fromInvocation) {
|
|
3144
|
+
return fromInvocation;
|
|
3145
|
+
}
|
|
3146
|
+
const pathEntries = splitPathEntries(process.env.PATH ?? process.env.Path ?? "");
|
|
3147
|
+
for (const entry of pathEntries) {
|
|
3148
|
+
if (existsSync(join(entry, "codex-multi-auth.cmd"))) {
|
|
3149
|
+
return entry;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
return null;
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
function buildWindowsBatchShimContent() {
|
|
3156
|
+
return [
|
|
3157
|
+
"@ECHO off",
|
|
3158
|
+
`:: ${WINDOWS_SHIM_MARKER}`,
|
|
3159
|
+
"GOTO start",
|
|
3160
|
+
":find_dp0",
|
|
3161
|
+
"SET dp0=%~dp0",
|
|
3162
|
+
"EXIT /b",
|
|
3163
|
+
":start",
|
|
3164
|
+
"SETLOCAL",
|
|
3165
|
+
"CALL :find_dp0",
|
|
3166
|
+
"",
|
|
3167
|
+
'IF EXIST "%dp0%\\node.exe" (',
|
|
3168
|
+
' SET "_prog=%dp0%\\node.exe"',
|
|
3169
|
+
") ELSE (",
|
|
3170
|
+
' SET "_prog=node"',
|
|
3171
|
+
' SET PATHEXT=%PATHEXT:;.JS;=%',
|
|
3172
|
+
")",
|
|
3173
|
+
"",
|
|
3174
|
+
'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\node_modules\\codex-multi-auth\\scripts\\codex.js" %*',
|
|
3175
|
+
].join("\r\n");
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
function buildWindowsCmdShimContent() {
|
|
3179
|
+
return [
|
|
3180
|
+
"@ECHO off",
|
|
3181
|
+
`:: ${WINDOWS_SHIM_MARKER}`,
|
|
3182
|
+
"GOTO start",
|
|
3183
|
+
":find_dp0",
|
|
3184
|
+
"SET dp0=%~dp0",
|
|
3185
|
+
"EXIT /b",
|
|
3186
|
+
":start",
|
|
3187
|
+
"SETLOCAL",
|
|
3188
|
+
"CALL :find_dp0",
|
|
3189
|
+
"",
|
|
3190
|
+
'IF EXIST "%dp0%\\node.exe" (',
|
|
3191
|
+
' SET "_prog=%dp0%\\node.exe"',
|
|
3192
|
+
") ELSE (",
|
|
3193
|
+
' SET "_prog=node"',
|
|
3194
|
+
' SET PATHEXT=%PATHEXT:;.JS;=%',
|
|
3195
|
+
")",
|
|
3196
|
+
"",
|
|
3197
|
+
'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\node_modules\\codex-multi-auth\\scripts\\codex.js" %*',
|
|
3198
|
+
].join("\r\n");
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
function buildWindowsPowerShellShimContent() {
|
|
3202
|
+
return [
|
|
3203
|
+
`# ${WINDOWS_SHIM_MARKER}`,
|
|
3204
|
+
"$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent",
|
|
3205
|
+
"",
|
|
3206
|
+
'$exe=""',
|
|
3207
|
+
'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {',
|
|
3208
|
+
' $exe=".exe"',
|
|
3209
|
+
"}",
|
|
3210
|
+
"$ret=0",
|
|
3211
|
+
'if (Test-Path "$basedir/node$exe") {',
|
|
3212
|
+
" if ($MyInvocation.ExpectingInput) {",
|
|
3213
|
+
' $input | & "$basedir/node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
|
|
3214
|
+
" } else {",
|
|
3215
|
+
' & "$basedir/node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
|
|
3216
|
+
" }",
|
|
3217
|
+
" $ret=$LASTEXITCODE",
|
|
3218
|
+
"} else {",
|
|
3219
|
+
" if ($MyInvocation.ExpectingInput) {",
|
|
3220
|
+
' $input | & "node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
|
|
3221
|
+
" } else {",
|
|
3222
|
+
' & "node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
|
|
3223
|
+
" }",
|
|
3224
|
+
" $ret=$LASTEXITCODE",
|
|
3225
|
+
"}",
|
|
3226
|
+
"if ($null -eq $ret) {",
|
|
3227
|
+
" exit 0",
|
|
3228
|
+
"}",
|
|
3229
|
+
"exit $ret",
|
|
3230
|
+
].join("\r\n");
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
function ensureWindowsShellShim(filePath, desiredContent, options = {}) {
|
|
3234
|
+
const {
|
|
3235
|
+
overwriteCustomShim = false,
|
|
3236
|
+
shimMarker = WINDOWS_SHIM_MARKER,
|
|
3237
|
+
} = options;
|
|
3238
|
+
|
|
3239
|
+
let currentContent = "";
|
|
3240
|
+
if (existsSync(filePath)) {
|
|
3241
|
+
try {
|
|
3242
|
+
currentContent = readFileSync(filePath, "utf8");
|
|
3243
|
+
} catch {
|
|
3244
|
+
return false;
|
|
3245
|
+
}
|
|
3246
|
+
if (currentContent === desiredContent || currentContent.includes(shimMarker)) {
|
|
3247
|
+
if (currentContent !== desiredContent) {
|
|
3248
|
+
try {
|
|
3249
|
+
writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
|
|
3250
|
+
return true;
|
|
3251
|
+
} catch {
|
|
3252
|
+
return false;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return false;
|
|
3256
|
+
}
|
|
3257
|
+
const looksLikeStockOpenAiShim =
|
|
3258
|
+
currentContent.includes("node_modules\\@openai\\codex\\bin\\codex.js") ||
|
|
3259
|
+
currentContent.includes("node_modules/@openai/codex/bin/codex.js") ||
|
|
3260
|
+
currentContent.includes("@openai\\codex-win32-") ||
|
|
3261
|
+
currentContent.includes("@openai/codex-win32-") ||
|
|
3262
|
+
currentContent.includes("vendor\\x86_64-pc-windows-msvc\\codex\\codex.exe") ||
|
|
3263
|
+
currentContent.includes("vendor\\aarch64-pc-windows-msvc\\codex\\codex.exe") ||
|
|
3264
|
+
currentContent.includes("vendor/x86_64-pc-windows-msvc/codex/codex.exe") ||
|
|
3265
|
+
currentContent.includes("vendor/aarch64-pc-windows-msvc/codex/codex.exe");
|
|
3266
|
+
if (looksLikeStockOpenAiShim) {
|
|
3267
|
+
try {
|
|
3268
|
+
writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
|
|
3269
|
+
return true;
|
|
3270
|
+
} catch {
|
|
3271
|
+
return false;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
if (!overwriteCustomShim) {
|
|
3275
|
+
return false;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
try {
|
|
3280
|
+
writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
|
|
3281
|
+
return true;
|
|
3282
|
+
} catch {
|
|
3283
|
+
return false;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
function shouldInstallPowerShellProfileGuard() {
|
|
3288
|
+
if (process.platform !== "win32") return false;
|
|
3289
|
+
const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "0").trim();
|
|
3290
|
+
return override !== "0";
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
function resolveWindowsUserHomeDir() {
|
|
3294
|
+
const userProfile = (process.env.USERPROFILE ?? "").trim();
|
|
3295
|
+
if (userProfile.length > 0) return userProfile;
|
|
3296
|
+
const homeDrive = (process.env.HOMEDRIVE ?? "").trim();
|
|
3297
|
+
const homePath = (process.env.HOMEPATH ?? "").trim();
|
|
3298
|
+
if (homeDrive.length > 0 && homePath.length > 0) {
|
|
3299
|
+
return `${homeDrive}${homePath}`;
|
|
3300
|
+
}
|
|
3301
|
+
const home = (process.env.HOME ?? "").trim();
|
|
3302
|
+
return home;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
function buildPowerShellProfileGuardBlock(shimDirectory) {
|
|
3306
|
+
const codexBatchPath = join(shimDirectory, "codex.bat").replace(/\\/g, "\\\\");
|
|
3307
|
+
return [
|
|
3308
|
+
POWERSHELL_PROFILE_MARKER_START,
|
|
3309
|
+
`$CodexMultiAuthShim = "${codexBatchPath}"`,
|
|
3310
|
+
"if (Test-Path $CodexMultiAuthShim) {",
|
|
3311
|
+
" function global:codex {",
|
|
3312
|
+
" & $CodexMultiAuthShim @args",
|
|
3313
|
+
" }",
|
|
3314
|
+
"}",
|
|
3315
|
+
POWERSHELL_PROFILE_MARKER_END,
|
|
3316
|
+
].join("\r\n");
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
function upsertPowerShellProfileGuard(profilePath, guardBlock) {
|
|
3320
|
+
let content = "";
|
|
3321
|
+
if (existsSync(profilePath)) {
|
|
3322
|
+
try {
|
|
3323
|
+
content = readFileSync(profilePath, "utf8");
|
|
3324
|
+
} catch {
|
|
3325
|
+
return false;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
const normalizedCurrentContent = content.replace(/\r?\n$/, "");
|
|
3329
|
+
|
|
3330
|
+
const startIndex = content.indexOf(POWERSHELL_PROFILE_MARKER_START);
|
|
3331
|
+
const endIndex = content.indexOf(POWERSHELL_PROFILE_MARKER_END);
|
|
3332
|
+
let nextContent;
|
|
3333
|
+
if (startIndex >= 0 && endIndex >= startIndex) {
|
|
3334
|
+
const endWithMarker = endIndex + POWERSHELL_PROFILE_MARKER_END.length;
|
|
3335
|
+
const prefix = content.slice(0, startIndex).replace(/\s*$/, "");
|
|
3336
|
+
const suffix = content.slice(endWithMarker).replace(/^\s*/, "");
|
|
3337
|
+
nextContent = `${prefix}\r\n\r\n${guardBlock}\r\n\r\n${suffix}`.trimEnd();
|
|
3338
|
+
} else if (normalizedCurrentContent.trim().length === 0) {
|
|
3339
|
+
nextContent = guardBlock;
|
|
3340
|
+
} else {
|
|
3341
|
+
nextContent = `${normalizedCurrentContent.replace(/\s*$/, "")}\r\n\r\n${guardBlock}`;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
if (nextContent === normalizedCurrentContent) {
|
|
3345
|
+
return false;
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
try {
|
|
3349
|
+
mkdirSync(dirname(profilePath), { recursive: true });
|
|
3350
|
+
writeFileSync(profilePath, `${nextContent}\r\n`, { encoding: "utf8", mode: 0o644 });
|
|
3351
|
+
return true;
|
|
3352
|
+
} catch {
|
|
3353
|
+
return false;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
function ensurePowerShellProfileGuard(shimDirectory) {
|
|
3358
|
+
if (!shouldInstallPowerShellProfileGuard()) return false;
|
|
3359
|
+
const homeDir = resolveWindowsUserHomeDir();
|
|
3360
|
+
if (!homeDir) return false;
|
|
3361
|
+
const guardBlock = buildPowerShellProfileGuardBlock(shimDirectory);
|
|
3362
|
+
const profilePaths = [
|
|
3363
|
+
join(homeDir, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"),
|
|
3364
|
+
join(homeDir, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"),
|
|
3365
|
+
];
|
|
3366
|
+
let changed = false;
|
|
3367
|
+
for (const profilePath of profilePaths) {
|
|
3368
|
+
changed = upsertPowerShellProfileGuard(profilePath, guardBlock) || changed;
|
|
3369
|
+
}
|
|
3370
|
+
return changed;
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
function ensureWindowsShellShimGuards() {
|
|
3374
|
+
const shouldInstallBatchGuard = shouldInstallWindowsBatchShimGuard();
|
|
3375
|
+
const shouldInstallProfileGuard = shouldInstallPowerShellProfileGuard();
|
|
3376
|
+
if (!shouldInstallBatchGuard && !shouldInstallProfileGuard) return;
|
|
3377
|
+
const shimDirectory = resolveWindowsShimDirectoryFromPath();
|
|
3378
|
+
if (!shimDirectory) return;
|
|
3379
|
+
|
|
3380
|
+
const codexMultiAuthShimPath = join(shimDirectory, "codex-multi-auth.cmd");
|
|
3381
|
+
if (!existsSync(codexMultiAuthShimPath)) return;
|
|
3382
|
+
|
|
3383
|
+
const overwriteCustomShim =
|
|
3384
|
+
(process.env.CODEX_MULTI_AUTH_OVERWRITE_CUSTOM_BATCH_SHIM ?? "0").trim() === "1";
|
|
3385
|
+
const installedBatch = shouldInstallBatchGuard
|
|
3386
|
+
? ensureWindowsShellShim(
|
|
3387
|
+
join(shimDirectory, "codex.bat"),
|
|
3388
|
+
buildWindowsBatchShimContent(),
|
|
3389
|
+
{ overwriteCustomShim },
|
|
3390
|
+
)
|
|
3391
|
+
: false;
|
|
3392
|
+
const installedCmd = shouldInstallBatchGuard
|
|
3393
|
+
? ensureWindowsShellShim(
|
|
3394
|
+
join(shimDirectory, "codex.cmd"),
|
|
3395
|
+
buildWindowsCmdShimContent(),
|
|
3396
|
+
{ overwriteCustomShim },
|
|
3397
|
+
)
|
|
3398
|
+
: false;
|
|
3399
|
+
const installedPs1 = shouldInstallBatchGuard
|
|
3400
|
+
? ensureWindowsShellShim(
|
|
3401
|
+
join(shimDirectory, "codex.ps1"),
|
|
3402
|
+
buildWindowsPowerShellShimContent(),
|
|
3403
|
+
{ overwriteCustomShim },
|
|
3404
|
+
)
|
|
3405
|
+
: false;
|
|
3406
|
+
const installedAny = installedBatch || installedCmd || installedPs1;
|
|
3407
|
+
const installedProfileGuard = shouldInstallProfileGuard
|
|
3408
|
+
? ensurePowerShellProfileGuard(shimDirectory)
|
|
3409
|
+
: false;
|
|
3410
|
+
if (installedAny || installedProfileGuard) {
|
|
3411
|
+
console.error(
|
|
3412
|
+
"codex-multi-auth: installed Windows shell guards to keep multi-auth routing after codex npm updates.",
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
async function main() {
|
|
3418
|
+
hydrateCliVersionEnv();
|
|
3419
|
+
|
|
3420
|
+
const rawArgs = process.argv.slice(2);
|
|
3421
|
+
if (rawArgs[0] === INTERNAL_RUNTIME_ROTATION_APP_HELPER_ARG) {
|
|
3422
|
+
return runRuntimeRotationAppHelper();
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3425
|
+
ensureWindowsShellShimGuards();
|
|
3426
|
+
|
|
3427
|
+
const normalizedArgs = normalizeAuthAlias(rawArgs);
|
|
3428
|
+
const bypass = (process.env.CODEX_MULTI_AUTH_BYPASS ?? "").trim() === "1";
|
|
3429
|
+
|
|
3430
|
+
if (!bypass && shouldHandleMultiAuthAuth(normalizedArgs)) {
|
|
3431
|
+
try {
|
|
3432
|
+
const runCodexMultiAuthCli = await loadRunCodexMultiAuthCli();
|
|
3433
|
+
if (!runCodexMultiAuthCli) {
|
|
3434
|
+
return 1;
|
|
3435
|
+
}
|
|
3436
|
+
const exitCode = await runCodexMultiAuthCli(normalizedArgs);
|
|
3437
|
+
await maybeInstallCodexAppLauncherAfterRotationEnable(
|
|
3438
|
+
normalizedArgs,
|
|
3439
|
+
normalizeExitCode(exitCode),
|
|
3440
|
+
);
|
|
3441
|
+
return normalizeExitCode(exitCode);
|
|
3442
|
+
} catch (error) {
|
|
3443
|
+
console.error(
|
|
3444
|
+
`codex-multi-auth runner failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3445
|
+
);
|
|
3446
|
+
return 1;
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
const realCodexBin = resolveRealCodexBin();
|
|
3451
|
+
if (!realCodexBin) {
|
|
3452
|
+
console.error(
|
|
3453
|
+
[
|
|
3454
|
+
"Could not locate the official Codex CLI.",
|
|
3455
|
+
"Install it with npm, Homebrew, or an official native release so `codex` is on PATH.",
|
|
3456
|
+
"Or set CODEX_MULTI_AUTH_REAL_CODEX_BIN to the full path of either codex or @openai/codex/bin/codex.js.",
|
|
3457
|
+
].join("\n"),
|
|
3458
|
+
);
|
|
3459
|
+
return 1;
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
await autoSyncManagerActiveSelectionIfEnabled();
|
|
3463
|
+
try {
|
|
3464
|
+
return await withForwardedRuntimeObservability(rawArgs, () =>
|
|
3465
|
+
forwardToRealCodex(realCodexBin, rawArgs),
|
|
3466
|
+
);
|
|
3467
|
+
} finally {
|
|
3468
|
+
await autoSyncManagerActiveSelectionIfEnabled();
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
const exitCode = await main();
|
|
3473
|
+
process.exitCode = normalizeExitCode(exitCode);
|