codex-multi-auth 0.1.3 → 0.1.5
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/LICENSE +1 -1
- package/README.md +7 -10
- package/assets/readme-hero.svg +31 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +6 -2
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +34 -11
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth/server.d.ts +1 -1
- package/dist/lib/auth/server.js +3 -3
- package/dist/lib/codex-cli/state.d.ts +9 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -1
- package/dist/lib/codex-cli/state.js +14 -0
- package/dist/lib/codex-cli/state.js.map +1 -1
- package/dist/lib/codex-cli/writer.d.ts.map +1 -1
- package/dist/lib/codex-cli/writer.js +106 -14
- package/dist/lib/codex-cli/writer.js.map +1 -1
- package/dist/lib/codex-manager/settings-hub.d.ts +16 -1
- package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
- package/dist/lib/codex-manager/settings-hub.js +28 -2
- package/dist/lib/codex-manager/settings-hub.js.map +1 -1
- package/dist/lib/codex-manager.d.ts +1 -0
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +319 -16
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/context-overflow.js +10 -10
- package/dist/lib/oauth-success.html +192 -260
- package/dist/lib/parallel-probe.d.ts +7 -0
- package/dist/lib/parallel-probe.d.ts.map +1 -1
- package/dist/lib/parallel-probe.js +37 -9
- package/dist/lib/parallel-probe.js.map +1 -1
- package/dist/lib/prompts/codex.js +2 -2
- package/dist/lib/refresh-queue.d.ts +4 -5
- package/dist/lib/refresh-queue.d.ts.map +1 -1
- package/dist/lib/refresh-queue.js +99 -21
- package/dist/lib/refresh-queue.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +12 -4
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +53 -25
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
- package/dist/lib/request/helpers/tool-utils.js +36 -6
- package/dist/lib/request/helpers/tool-utils.js.map +1 -1
- package/dist/lib/request/rate-limit-backoff.d.ts +7 -0
- package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -1
- package/dist/lib/request/rate-limit-backoff.js +24 -4
- package/dist/lib/request/rate-limit-backoff.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +10 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +61 -37
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/rotation.d.ts +19 -0
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +60 -39
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/ui/copy.d.ts +1 -1
- package/dist/lib/ui/copy.js +1 -1
- package/dist/lib/utils.d.ts +6 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +11 -0
- package/dist/lib/utils.js.map +1 -1
- package/node_modules/@codex-ai/plugin/dist/index.d.ts +1 -1
- package/node_modules/@codex-ai/plugin/dist/tool.d.ts +41 -41
- package/node_modules/@codex-ai/plugin/dist/tool.js +28 -28
- package/package.json +9 -5
- package/scripts/audit-dev-allowlist.js +186 -94
- package/scripts/bench-format/hashline-v2.mjs +642 -642
- package/scripts/bench-format/render.mjs +496 -496
- package/scripts/bench-format/stats.mjs +54 -54
- package/scripts/bench-format/tasks.mjs +151 -151
- package/scripts/benchmark-render-dashboard.mjs +49 -49
- package/scripts/benchmark-runtime-path.mjs +166 -0
- package/scripts/codex-multi-auth.js +1 -1
- package/scripts/codex.js +86 -26
- package/scripts/copy-oauth-success.js +42 -3
- package/scripts/install-codex-auth-utils.js +69 -0
- package/scripts/install-codex-auth.js +23 -14
- package/scripts/test-all-models.sh +7 -7
- package/scripts/test-model-matrix.js +108 -80
- package/scripts/validate-model-map.sh +7 -7
- package/vendor/codex-ai-plugin/dist/index.d.ts +1 -1
- package/vendor/codex-ai-plugin/dist/tool.d.ts +41 -41
- package/vendor/codex-ai-plugin/dist/tool.js +28 -28
- package/vendor/codex-ai-sdk/dist/index.d.ts +3 -3
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { performance } from "node:perf_hooks";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { filterInput } from "../dist/lib/request/request-transformer.js";
|
|
8
|
+
import { cleanupToolDefinitions } from "../dist/lib/request/helpers/tool-utils.js";
|
|
9
|
+
import { AccountManager } from "../dist/lib/accounts.js";
|
|
10
|
+
|
|
11
|
+
function argValue(args, name) {
|
|
12
|
+
const prefix = `${name}=`;
|
|
13
|
+
const match = args.find((arg) => arg.startsWith(prefix));
|
|
14
|
+
return match ? match.slice(prefix.length) : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parsePositiveInt(value, fallback) {
|
|
18
|
+
if (!value) return fallback;
|
|
19
|
+
const parsed = Number.parseInt(value, 10);
|
|
20
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function benchmarkCase(name, iterations, fn) {
|
|
25
|
+
for (let i = 0; i < 5; i += 1) {
|
|
26
|
+
fn();
|
|
27
|
+
}
|
|
28
|
+
const start = performance.now();
|
|
29
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
30
|
+
fn();
|
|
31
|
+
}
|
|
32
|
+
const end = performance.now();
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
iterations,
|
|
36
|
+
avgMs: Number(((end - start) / iterations).toFixed(6)),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildInputItems(size) {
|
|
41
|
+
const items = [];
|
|
42
|
+
for (let i = 0; i < size; i += 1) {
|
|
43
|
+
items.push({
|
|
44
|
+
type: "message",
|
|
45
|
+
role: i % 2 === 0 ? "user" : "assistant",
|
|
46
|
+
id: `msg_${i}`,
|
|
47
|
+
content: [{ type: "input_text", text: `payload-${i}` }],
|
|
48
|
+
});
|
|
49
|
+
if (i % 40 === 0) {
|
|
50
|
+
items.push({ type: "item_reference", id: `ref_${i}` });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return items;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildTools(toolCount, propertyCount) {
|
|
57
|
+
const tools = [];
|
|
58
|
+
for (let i = 0; i < toolCount; i += 1) {
|
|
59
|
+
const properties = {};
|
|
60
|
+
const required = [];
|
|
61
|
+
for (let j = 0; j < propertyCount; j += 1) {
|
|
62
|
+
const key = `field_${j}`;
|
|
63
|
+
properties[key] = { type: ["string", "null"], description: `property-${j}` };
|
|
64
|
+
required.push(key);
|
|
65
|
+
}
|
|
66
|
+
required.push("ghost_field");
|
|
67
|
+
tools.push({
|
|
68
|
+
type: "function",
|
|
69
|
+
function: {
|
|
70
|
+
name: `tool_${i}`,
|
|
71
|
+
parameters: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties,
|
|
74
|
+
required,
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return tools;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildManager(accountCount) {
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
const accounts = [];
|
|
86
|
+
for (let i = 0; i < accountCount; i += 1) {
|
|
87
|
+
accounts.push({
|
|
88
|
+
refreshToken: `rt_${i}`,
|
|
89
|
+
accessToken: `at_${i}`,
|
|
90
|
+
expiresAt: now + 3_600_000,
|
|
91
|
+
accountId: `acct_${i}`,
|
|
92
|
+
email: `user${i}@example.com`,
|
|
93
|
+
enabled: true,
|
|
94
|
+
addedAt: now,
|
|
95
|
+
lastUsed: 0,
|
|
96
|
+
rateLimitResetTimes: {},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return new AccountManager(undefined, {
|
|
100
|
+
version: 3,
|
|
101
|
+
accounts,
|
|
102
|
+
activeIndex: 0,
|
|
103
|
+
activeIndexByFamily: {},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function run() {
|
|
108
|
+
const args = process.argv.slice(2);
|
|
109
|
+
const iterations = parsePositiveInt(argValue(args, "--iterations"), 30);
|
|
110
|
+
const outputPath = argValue(args, "--output");
|
|
111
|
+
|
|
112
|
+
const inputSmall = buildInputItems(400);
|
|
113
|
+
const inputLarge = buildInputItems(2000);
|
|
114
|
+
const toolsMedium = buildTools(40, 12);
|
|
115
|
+
const toolsLarge = buildTools(140, 25);
|
|
116
|
+
|
|
117
|
+
const results = [
|
|
118
|
+
benchmarkCase("filterInput_small", iterations, () => {
|
|
119
|
+
const out = filterInput(inputSmall);
|
|
120
|
+
if (!Array.isArray(out)) throw new Error("filterInput_small failed");
|
|
121
|
+
}),
|
|
122
|
+
benchmarkCase("filterInput_large", iterations, () => {
|
|
123
|
+
const out = filterInput(inputLarge);
|
|
124
|
+
if (!Array.isArray(out)) throw new Error("filterInput_large failed");
|
|
125
|
+
}),
|
|
126
|
+
benchmarkCase("cleanupToolDefinitions_medium", iterations, () => {
|
|
127
|
+
const out = cleanupToolDefinitions(toolsMedium);
|
|
128
|
+
if (!Array.isArray(out)) throw new Error("cleanupToolDefinitions_medium failed");
|
|
129
|
+
}),
|
|
130
|
+
benchmarkCase("cleanupToolDefinitions_large", iterations, () => {
|
|
131
|
+
const out = cleanupToolDefinitions(toolsLarge);
|
|
132
|
+
if (!Array.isArray(out)) throw new Error("cleanupToolDefinitions_large failed");
|
|
133
|
+
}),
|
|
134
|
+
benchmarkCase("accountHybridSelection_200", iterations, () => {
|
|
135
|
+
const manager = buildManager(200);
|
|
136
|
+
for (let i = 0; i < 200; i += 1) {
|
|
137
|
+
manager.getCurrentOrNextForFamilyHybrid("codex", "gpt-5-codex", { pidOffsetEnabled: false });
|
|
138
|
+
}
|
|
139
|
+
}),
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const payload = {
|
|
143
|
+
generatedAt: new Date().toISOString(),
|
|
144
|
+
node: process.version,
|
|
145
|
+
iterations,
|
|
146
|
+
results,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
if (outputPath) {
|
|
150
|
+
const resolved = resolve(outputPath);
|
|
151
|
+
return mkdir(dirname(resolved), { recursive: true }).then(() =>
|
|
152
|
+
writeFile(resolved, `${JSON.stringify(payload, null, 2)}\n`, "utf8"),
|
|
153
|
+
).then(() => {
|
|
154
|
+
console.log(`Runtime benchmark written: ${resolved}`);
|
|
155
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
160
|
+
return Promise.resolve();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
run().catch((error) => {
|
|
164
|
+
console.error(`Runtime benchmark failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
});
|
package/scripts/codex.js
CHANGED
|
@@ -32,6 +32,25 @@ async function loadRunCodexMultiAuthCli() {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
async function autoSyncManagerActiveSelectionIfEnabled() {
|
|
36
|
+
const enabled = (process.env.CODEX_MULTI_AUTH_AUTO_SYNC_ON_STARTUP ?? "1").trim() !== "0";
|
|
37
|
+
if (!enabled) return;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const mod = await import("../dist/lib/codex-manager.js");
|
|
41
|
+
if (typeof mod.autoSyncActiveAccountToCodex !== "function") {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
await mod.autoSyncActiveAccountToCodex();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
|
|
47
|
+
// Non-auth command path should keep forwarding even if dist is missing.
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Best effort only: never block official Codex startup on sync failure.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
35
54
|
function resolveRealCodexBin() {
|
|
36
55
|
const override = (process.env.CODEX_MULTI_AUTH_REAL_CODEX_BIN ?? "").trim();
|
|
37
56
|
if (override.length > 0) {
|
|
@@ -113,6 +132,41 @@ function forwardToRealCodex(codexBin, args) {
|
|
|
113
132
|
});
|
|
114
133
|
}
|
|
115
134
|
|
|
135
|
+
function hasCliAuthCredentialsStoreOverride(args) {
|
|
136
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
137
|
+
const arg = args[i];
|
|
138
|
+
if (arg === "-c" || arg === "--config") {
|
|
139
|
+
const next = args[i + 1];
|
|
140
|
+
if (!next || !next.includes("=")) continue;
|
|
141
|
+
const [key] = next.split("=", 1);
|
|
142
|
+
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (typeof arg === "string" && arg.startsWith("--config=")) {
|
|
148
|
+
const assignment = arg.slice("--config=".length);
|
|
149
|
+
const [key] = assignment.split("=", 1);
|
|
150
|
+
if ((key ?? "").trim() === "cli_auth_credentials_store") {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function buildForwardArgs(rawArgs) {
|
|
159
|
+
const forceFileAuthStore = (process.env.CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE ?? "1").trim() !== "0";
|
|
160
|
+
if (!forceFileAuthStore) return [...rawArgs];
|
|
161
|
+
if (hasCliAuthCredentialsStoreOverride(rawArgs)) return [...rawArgs];
|
|
162
|
+
|
|
163
|
+
return [
|
|
164
|
+
...rawArgs,
|
|
165
|
+
"-c",
|
|
166
|
+
'cli_auth_credentials_store="file"',
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
116
170
|
function normalizeExitCode(value) {
|
|
117
171
|
if (typeof value === "number" && Number.isInteger(value)) {
|
|
118
172
|
return value;
|
|
@@ -124,37 +178,43 @@ function normalizeExitCode(value) {
|
|
|
124
178
|
return 1;
|
|
125
179
|
}
|
|
126
180
|
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const
|
|
181
|
+
async function main() {
|
|
182
|
+
const rawArgs = process.argv.slice(2);
|
|
183
|
+
const normalizedArgs = normalizeAuthAlias(rawArgs);
|
|
184
|
+
const bypass = (process.env.CODEX_MULTI_AUTH_BYPASS ?? "").trim() === "1";
|
|
130
185
|
|
|
131
|
-
if (!bypass && shouldHandleMultiAuthAuth(normalizedArgs)) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
186
|
+
if (!bypass && shouldHandleMultiAuthAuth(normalizedArgs)) {
|
|
187
|
+
try {
|
|
188
|
+
const runCodexMultiAuthCli = await loadRunCodexMultiAuthCli();
|
|
189
|
+
if (!runCodexMultiAuthCli) {
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
const exitCode = await runCodexMultiAuthCli(normalizedArgs);
|
|
193
|
+
return normalizeExitCode(exitCode);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(
|
|
196
|
+
`codex-multi-auth runner failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
197
|
+
);
|
|
198
|
+
return 1;
|
|
136
199
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const realCodexBin = resolveRealCodexBin();
|
|
203
|
+
if (!realCodexBin) {
|
|
140
204
|
console.error(
|
|
141
|
-
|
|
205
|
+
[
|
|
206
|
+
"Could not locate the official Codex CLI binary (@openai/codex).",
|
|
207
|
+
"Install it globally: npm install -g @openai/codex",
|
|
208
|
+
"Or set CODEX_MULTI_AUTH_REAL_CODEX_BIN to a full bin/codex.js path.",
|
|
209
|
+
].join("\n"),
|
|
142
210
|
);
|
|
143
|
-
|
|
211
|
+
return 1;
|
|
144
212
|
}
|
|
145
|
-
}
|
|
146
213
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
[
|
|
151
|
-
"Could not locate the official Codex CLI binary (@openai/codex).",
|
|
152
|
-
"Install it globally: npm install -g @openai/codex",
|
|
153
|
-
"Or set CODEX_MULTI_AUTH_REAL_CODEX_BIN to a full bin/codex.js path.",
|
|
154
|
-
].join("\n"),
|
|
155
|
-
);
|
|
156
|
-
process.exit(1);
|
|
214
|
+
await autoSyncManagerActiveSelectionIfEnabled();
|
|
215
|
+
const forwardArgs = buildForwardArgs(rawArgs);
|
|
216
|
+
return forwardToRealCodex(realCodexBin, forwardArgs);
|
|
157
217
|
}
|
|
158
218
|
|
|
159
|
-
const
|
|
160
|
-
process.
|
|
219
|
+
const exitCode = await main();
|
|
220
|
+
process.exitCode = normalizeExitCode(exitCode);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
@@ -16,20 +16,59 @@ function getDefaultPaths() {
|
|
|
16
16
|
return { src, dest };
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Copy a file and retry automatically while the destination is temporarily locked.
|
|
21
|
+
* @param {string} src absolute path to the source HTML file
|
|
22
|
+
* @param {string} dest absolute path to the destination HTML file
|
|
23
|
+
* @param {{ maxAttempts?: number, backoffMs?: number }} options retry configuration
|
|
24
|
+
*/
|
|
25
|
+
async function copyWithRetry(
|
|
26
|
+
src,
|
|
27
|
+
dest,
|
|
28
|
+
{ maxAttempts = 3, backoffMs = 50 } = {},
|
|
29
|
+
) {
|
|
30
|
+
const retryableCodes = new Set(["EBUSY", "EPERM", "EACCES"]);
|
|
31
|
+
let attempt = 0;
|
|
32
|
+
for (;;) {
|
|
33
|
+
try {
|
|
34
|
+
await fs.copyFile(src, dest);
|
|
35
|
+
return;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : undefined;
|
|
38
|
+
const isRetryable = typeof code === "string" && retryableCodes.has(code);
|
|
39
|
+
if (!isRetryable || attempt >= maxAttempts - 1) {
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
attempt += 1;
|
|
43
|
+
const delayMs = backoffMs * (2 ** (attempt - 1));
|
|
44
|
+
await new Promise((resolve) => {
|
|
45
|
+
setTimeout(resolve, delayMs);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Copy the OAuth success HTML into dist/, ensuring safe directory creation and retries.
|
|
53
|
+
* @param {{ src?: string, dest?: string }} options optional override paths for testing
|
|
54
|
+
*/
|
|
19
55
|
export async function copyOAuthSuccessHtml(options = {}) {
|
|
20
56
|
const defaults = getDefaultPaths();
|
|
21
57
|
const src = options.src ?? defaults.src;
|
|
22
58
|
const dest = options.dest ?? defaults.dest;
|
|
23
59
|
|
|
24
60
|
await fs.mkdir(dirname(dest), { recursive: true });
|
|
25
|
-
await
|
|
61
|
+
await copyWithRetry(src, dest);
|
|
26
62
|
|
|
27
63
|
return { src, dest };
|
|
28
64
|
}
|
|
29
65
|
|
|
30
66
|
const isDirectRun = (() => {
|
|
31
67
|
if (!process.argv[1]) return false;
|
|
32
|
-
return
|
|
68
|
+
return (
|
|
69
|
+
normalizePathForCompare(process.argv[1]) ===
|
|
70
|
+
normalizePathForCompare(__filename)
|
|
71
|
+
);
|
|
33
72
|
})();
|
|
34
73
|
|
|
35
74
|
if (isDirectRun) {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
+
import { rename as fsRename } from "node:fs/promises";
|
|
3
4
|
|
|
4
5
|
const PLUGIN_NAME = "codex-multi-auth";
|
|
6
|
+
export const FILE_RETRY_CODES = new Set(["EBUSY", "EPERM", "EAGAIN", "ENOTEMPTY", "EACCES"]);
|
|
7
|
+
export const FILE_RETRY_MAX_ATTEMPTS = 6;
|
|
8
|
+
export const FILE_RETRY_BASE_DELAY_MS = 25;
|
|
9
|
+
export const FILE_RETRY_JITTER_MS = 20;
|
|
5
10
|
|
|
6
11
|
export function resolveInstallPaths(
|
|
7
12
|
platform = process.platform,
|
|
@@ -47,3 +52,67 @@ export function normalizePluginList(list) {
|
|
|
47
52
|
return [...deduped, PLUGIN_NAME];
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
function sleep(ms) {
|
|
56
|
+
// Keep this helper local so installer scripts remain standalone and do not depend on lib/.
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
setTimeout(resolve, ms);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function shouldRetryFileOperation(error) {
|
|
63
|
+
return error instanceof Error &&
|
|
64
|
+
typeof error.code === "string" &&
|
|
65
|
+
FILE_RETRY_CODES.has(error.code);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function withFileOperationRetry(operation) {
|
|
69
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
70
|
+
try {
|
|
71
|
+
return await operation();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (!shouldRetryFileOperation(error) || attempt >= FILE_RETRY_MAX_ATTEMPTS) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const jitter = Math.floor(Math.random() * FILE_RETRY_JITTER_MS);
|
|
78
|
+
const delayMs = (FILE_RETRY_BASE_DELAY_MS * (2 ** (attempt - 1))) + jitter;
|
|
79
|
+
await sleep(delayMs);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function renameWithRetry(sourcePath, targetPath, options = {}) {
|
|
85
|
+
const {
|
|
86
|
+
rename = fsRename,
|
|
87
|
+
log = () => {},
|
|
88
|
+
maxRetries = FILE_RETRY_MAX_ATTEMPTS,
|
|
89
|
+
baseDelayMs = FILE_RETRY_BASE_DELAY_MS,
|
|
90
|
+
jitterMs = FILE_RETRY_JITTER_MS,
|
|
91
|
+
random = Math.random,
|
|
92
|
+
sleep: sleepImpl = sleep,
|
|
93
|
+
} = options;
|
|
94
|
+
|
|
95
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 1) {
|
|
96
|
+
throw new RangeError("maxRetries must be an integer >= 1");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (let attempt = 0; attempt < maxRetries; attempt += 1) {
|
|
100
|
+
try {
|
|
101
|
+
await rename(sourcePath, targetPath);
|
|
102
|
+
return;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const code = error && typeof error === "object" && "code" in error
|
|
105
|
+
? error.code
|
|
106
|
+
: undefined;
|
|
107
|
+
const isRetryable = typeof code === "string" && FILE_RETRY_CODES.has(code);
|
|
108
|
+
if (!isRetryable || attempt === maxRetries - 1) {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
const delayMs = baseDelayMs * 2 ** attempt + Math.floor(random() * jitterMs);
|
|
112
|
+
log(
|
|
113
|
+
`Retrying atomic rename (${attempt + 1}/${maxRetries}) code=${code ?? "unknown"} source=${sourcePath} target=${targetPath} delayMs=${delayMs}`,
|
|
114
|
+
);
|
|
115
|
+
await sleepImpl(delayMs);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
-
import { readFile, writeFile, mkdir, copyFile, rm
|
|
4
|
+
import { readFile, writeFile, mkdir, copyFile, rm } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { dirname, join, resolve } from "node:path";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
normalizePluginList,
|
|
9
|
+
renameWithRetry,
|
|
10
|
+
resolveInstallPaths,
|
|
11
|
+
withFileOperationRetry,
|
|
12
|
+
} from "./install-codex-auth-utils.js";
|
|
8
13
|
|
|
9
14
|
const PLUGIN_NAME = "codex-multi-auth";
|
|
10
15
|
|
|
@@ -61,11 +66,15 @@ async function writeJsonAtomic(filePath, value) {
|
|
|
61
66
|
.slice(2, 8)}`;
|
|
62
67
|
const content = formatJson(value);
|
|
63
68
|
try {
|
|
64
|
-
await writeFile(tempPath, content, "utf-8");
|
|
65
|
-
await
|
|
69
|
+
await withFileOperationRetry(() => writeFile(tempPath, content, "utf-8"));
|
|
70
|
+
await renameWithRetry(tempPath, filePath, { log });
|
|
66
71
|
} finally {
|
|
67
72
|
if (existsSync(tempPath)) {
|
|
68
|
-
|
|
73
|
+
try {
|
|
74
|
+
await withFileOperationRetry(() => rm(tempPath, { force: true }));
|
|
75
|
+
} catch (error) {
|
|
76
|
+
log(`Warning: Could not remove temporary file ${tempPath} (${error}).`);
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
}
|
|
71
80
|
}
|
|
@@ -134,15 +143,15 @@ async function clearCache() {
|
|
|
134
143
|
if (dryRun) {
|
|
135
144
|
log(`[dry-run] Would remove ${cacheNodeModules}`);
|
|
136
145
|
log(`[dry-run] Would remove ${cacheBunLock}`);
|
|
137
|
-
} else {
|
|
138
|
-
try {
|
|
139
|
-
await rm(cacheNodeModules, { recursive: true, force: true });
|
|
140
|
-
await rm(cacheBunLock, { force: true });
|
|
141
|
-
} catch (error) {
|
|
142
|
-
log(
|
|
143
|
-
`Warning: Could not fully clear cache (${error instanceof Error ? error.message : String(error)}). You may need to restart Codex.`,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
+
} else {
|
|
147
|
+
try {
|
|
148
|
+
await withFileOperationRetry(() => rm(cacheNodeModules, { recursive: true, force: true }));
|
|
149
|
+
await withFileOperationRetry(() => rm(cacheBunLock, { force: true }));
|
|
150
|
+
} catch (error) {
|
|
151
|
+
log(
|
|
152
|
+
`Warning: Could not fully clear cache (${error instanceof Error ? error.message : String(error)}). You may need to restart Codex.`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
await removePluginFromCachePackage();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
node "${SCRIPT_DIR}/test-model-matrix.js" "$@"
|
|
7
|
-
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
node "${SCRIPT_DIR}/test-model-matrix.js" "$@"
|
|
7
|
+
|