codex-multi-auth 2.1.8 → 2.1.10

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.
Files changed (60) hide show
  1. package/README.md +413 -411
  2. package/dist/lib/accounts.d.ts +2 -0
  3. package/dist/lib/accounts.d.ts.map +1 -1
  4. package/dist/lib/accounts.js +26 -2
  5. package/dist/lib/accounts.js.map +1 -1
  6. package/dist/lib/auth/device-auth.d.ts +1 -0
  7. package/dist/lib/auth/device-auth.d.ts.map +1 -1
  8. package/dist/lib/auth/device-auth.js +6 -2
  9. package/dist/lib/auth/device-auth.js.map +1 -1
  10. package/dist/lib/codex-manager/commands/forecast.d.ts +5 -1
  11. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  12. package/dist/lib/codex-manager/commands/forecast.js +14 -1
  13. package/dist/lib/codex-manager/commands/forecast.js.map +1 -1
  14. package/dist/lib/codex-manager/commands/report.d.ts +2 -0
  15. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  16. package/dist/lib/codex-manager/commands/report.js +17 -1
  17. package/dist/lib/codex-manager/commands/report.js.map +1 -1
  18. package/dist/lib/codex-manager/commands/rotation.d.ts +1 -1
  19. package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
  20. package/dist/lib/codex-manager/commands/rotation.js +71 -1
  21. package/dist/lib/codex-manager/commands/rotation.js.map +1 -1
  22. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  23. package/dist/lib/codex-manager/repair-commands.js +35 -0
  24. package/dist/lib/codex-manager/repair-commands.js.map +1 -1
  25. package/dist/lib/codex-manager.d.ts.map +1 -1
  26. package/dist/lib/codex-manager.js +36 -2
  27. package/dist/lib/codex-manager.js.map +1 -1
  28. package/dist/lib/forecast.d.ts +9 -0
  29. package/dist/lib/forecast.d.ts.map +1 -1
  30. package/dist/lib/forecast.js +36 -0
  31. package/dist/lib/forecast.js.map +1 -1
  32. package/dist/lib/runtime/runtime-observability.d.ts +17 -0
  33. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  34. package/dist/lib/runtime/runtime-observability.js +48 -0
  35. package/dist/lib/runtime/runtime-observability.js.map +1 -1
  36. package/dist/lib/runtime-rotation-proxy.d.ts +1 -0
  37. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  38. package/dist/lib/runtime-rotation-proxy.js +119 -20
  39. package/dist/lib/runtime-rotation-proxy.js.map +1 -1
  40. package/package.json +193 -185
  41. package/scripts/audit-dev-allowlist.js +0 -220
  42. package/scripts/bench-format/codex-host.mjs +0 -275
  43. package/scripts/bench-format/hashline-v2.mjs +0 -642
  44. package/scripts/bench-format/models.mjs +0 -110
  45. package/scripts/bench-format/render.mjs +0 -496
  46. package/scripts/bench-format/stats.mjs +0 -54
  47. package/scripts/bench-format/tasks.mjs +0 -151
  48. package/scripts/benchmark-edit-formats.mjs +0 -1164
  49. package/scripts/benchmark-render-dashboard.mjs +0 -49
  50. package/scripts/benchmark-runtime-path.mjs +0 -166
  51. package/scripts/check-pack-budget-lib.js +0 -157
  52. package/scripts/check-pack-budget.mjs +0 -10
  53. package/scripts/copy-oauth-success.js +0 -76
  54. package/scripts/install-codex-auth.js +0 -258
  55. package/scripts/repo-hygiene.js +0 -320
  56. package/scripts/test-all-models.sh +0 -7
  57. package/scripts/test-model-matrix.js +0 -687
  58. package/scripts/update-vendor-provenance.mjs +0 -96
  59. package/scripts/validate-model-map.sh +0 -7
  60. package/scripts/verify-vendor-provenance.mjs +0 -53
@@ -1,258 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // @ts-check
4
-
5
- import { existsSync } from "node:fs";
6
- import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
7
- import { dirname, join, resolve } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
- import {
10
- normalizePluginList,
11
- renameWithRetry,
12
- resolveInstallPaths,
13
- withFileOperationRetry,
14
- } from "./install-codex-auth-utils.js";
15
-
16
- const PLUGIN_NAME = "codex-multi-auth";
17
-
18
- const args = new Set(process.argv.slice(2));
19
-
20
- if (args.has("--help") || args.has("-h")) {
21
- console.log(
22
- `Usage: ${PLUGIN_NAME} [--modern|--legacy] [--dry-run] [--no-cache-clear]\n\n` +
23
- "Default behavior:\n" +
24
- " - Installs/updates global config at ~/.config/Codex/Codex.json\n" +
25
- " - Uses modern config (variants) by default\n" +
26
- " - Ensures plugin is unpinned (latest)\n" +
27
- " - Clears Codex plugin cache\n\n" +
28
- "Options:\n" +
29
- " --modern Force modern config (default)\n" +
30
- " --legacy Use legacy config (older Codex versions)\n" +
31
- " --dry-run Show actions without writing\n" +
32
- " --no-cache-clear Skip clearing Codex cache\n",
33
- );
34
- process.exit(0);
35
- }
36
-
37
- const useLegacy = args.has("--legacy");
38
- const useModern = args.has("--modern") || !useLegacy;
39
- const dryRun = args.has("--dry-run");
40
- const skipCacheClear = args.has("--no-cache-clear");
41
-
42
- const scriptDir = dirname(fileURLToPath(import.meta.url));
43
- const repoRoot = resolve(scriptDir, "..");
44
- const templatePath = join(
45
- repoRoot,
46
- "config",
47
- useLegacy ? "codex-legacy.json" : "codex-modern.json",
48
- );
49
-
50
- const installPaths = resolveInstallPaths();
51
- const {
52
- configDir,
53
- configPath,
54
- cacheNodeModules,
55
- cacheBunLock,
56
- cachePackageJson,
57
- } = installPaths;
58
-
59
- /** @param {string} message */
60
- function log(message) {
61
- console.log(message);
62
- }
63
-
64
- /** @param {unknown} obj */
65
- function formatJson(obj) {
66
- return `${JSON.stringify(obj, null, 2)}\n`;
67
- }
68
-
69
- /** @param {string} filePath */
70
- async function readJson(filePath) {
71
- const content = await readFile(filePath, "utf-8");
72
- return /** @type {any} */ (JSON.parse(content));
73
- }
74
-
75
- /** @param {string} filePath @param {unknown} value */
76
- async function writeJsonAtomic(filePath, value) {
77
- const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random()
78
- .toString(36)
79
- .slice(2, 8)}`;
80
- const content = formatJson(value);
81
- try {
82
- await withFileOperationRetry(() => writeFile(tempPath, content, "utf-8"));
83
- await renameWithRetry(tempPath, filePath, { log });
84
- } finally {
85
- if (existsSync(tempPath)) {
86
- try {
87
- await withFileOperationRetry(() => rm(tempPath, { force: true }));
88
- } catch (error) {
89
- log(`Warning: Could not remove temporary file ${tempPath} (${error}).`);
90
- }
91
- }
92
- }
93
- }
94
-
95
- /** @param {string} sourcePath */
96
- async function backupConfig(sourcePath) {
97
- const timestamp = new Date()
98
- .toISOString()
99
- .replace(/[:.]/g, "-")
100
- .replace("T", "_")
101
- .replace("Z", "");
102
- const nonce = `${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
103
- const backupPath = `${sourcePath}.bak-${timestamp}-${nonce}`;
104
- if (!dryRun) {
105
- await copyFile(sourcePath, backupPath);
106
- }
107
- return backupPath;
108
- }
109
-
110
- async function removePluginFromCachePackage() {
111
- if (!existsSync(cachePackageJson)) {
112
- return;
113
- }
114
-
115
- /** @type {any} */
116
- let cacheData;
117
- try {
118
- cacheData = await readJson(cachePackageJson);
119
- } catch (error) {
120
- log(`Warning: Could not parse ${cachePackageJson} (${error}). Skipping.`);
121
- return;
122
- }
123
-
124
- const sections = [
125
- "dependencies",
126
- "devDependencies",
127
- "peerDependencies",
128
- "optionalDependencies",
129
- ];
130
-
131
- let changed = false;
132
- for (const section of sections) {
133
- const deps = cacheData?.[section];
134
- if (deps && typeof deps === "object" && PLUGIN_NAME in deps) {
135
- delete deps[PLUGIN_NAME];
136
- changed = true;
137
- }
138
- }
139
-
140
- if (!changed) {
141
- return;
142
- }
143
-
144
- if (dryRun) {
145
- log(`[dry-run] Would update ${cachePackageJson} to remove ${PLUGIN_NAME}`);
146
- return;
147
- }
148
-
149
- await writeJsonAtomic(cachePackageJson, cacheData);
150
- }
151
-
152
- async function clearCache() {
153
- if (skipCacheClear) {
154
- log("Skipping cache clear (--no-cache-clear).");
155
- return;
156
- }
157
-
158
- if (dryRun) {
159
- log(`[dry-run] Would remove ${cacheNodeModules}`);
160
- log(`[dry-run] Would remove ${cacheBunLock}`);
161
- } else {
162
- try {
163
- await withFileOperationRetry(() =>
164
- rm(cacheNodeModules, { recursive: true, force: true }),
165
- );
166
- await withFileOperationRetry(() => rm(cacheBunLock, { force: true }));
167
- } catch (error) {
168
- log(
169
- `Warning: Could not fully clear cache (${error instanceof Error ? error.message : String(error)}). You may need to restart Codex.`,
170
- );
171
- }
172
- }
173
-
174
- await removePluginFromCachePackage();
175
- }
176
-
177
- async function main() {
178
- if (!existsSync(templatePath)) {
179
- throw new Error(`Config template not found at ${templatePath}`);
180
- }
181
-
182
- const template = await readJson(templatePath);
183
- template.plugin = [PLUGIN_NAME];
184
-
185
- let nextConfig = template;
186
- if (existsSync(configPath)) {
187
- const backupPath = await backupConfig(configPath);
188
- log(
189
- `${dryRun ? "[dry-run] Would create backup" : "Backup created"}: ${backupPath}`,
190
- );
191
-
192
- try {
193
- const existing = await readJson(configPath);
194
- const merged = { ...existing };
195
- merged.plugin = normalizePluginList(existing.plugin);
196
- const provider =
197
- existing.provider && typeof existing.provider === "object"
198
- ? { ...existing.provider }
199
- : {};
200
- const existingOpenAi =
201
- provider.openai && typeof provider.openai === "object"
202
- ? provider.openai
203
- : {};
204
- const templateOpenAi =
205
- template.provider &&
206
- typeof template.provider === "object" &&
207
- template.provider.openai &&
208
- typeof template.provider.openai === "object"
209
- ? template.provider.openai
210
- : {};
211
- provider.openai = { ...templateOpenAi, ...existingOpenAi };
212
- merged.provider = provider;
213
- nextConfig = merged;
214
- } catch (error) {
215
- log(
216
- `Warning: Could not parse existing config (${error}). Replacing with template.`,
217
- );
218
- nextConfig = template;
219
- }
220
- } else {
221
- log("No existing config found. Creating new global config.");
222
- }
223
-
224
- if (dryRun) {
225
- log(
226
- `[dry-run] Would write ${configPath} using ${useLegacy ? "legacy" : "modern"} config`,
227
- );
228
- } else {
229
- await mkdir(configDir, { recursive: true });
230
- await writeJsonAtomic(configPath, nextConfig);
231
- log(`Wrote ${configPath} (${useLegacy ? "legacy" : "modern"} config)`);
232
- }
233
-
234
- await clearCache();
235
-
236
- log("\nDone. Restart Codex to (re)install the plugin.");
237
- log("Example: Codex");
238
- if (useLegacy) {
239
- log("Note: Legacy config requires Codex v1.0.209 or older.");
240
- }
241
- }
242
-
243
- const isDirectRun = (() => {
244
- try {
245
- return resolve(process.argv[1] ?? "") === fileURLToPath(import.meta.url);
246
- } catch {
247
- return false;
248
- }
249
- })();
250
-
251
- if (isDirectRun) {
252
- main().catch((error) => {
253
- console.error(
254
- `Installer failed: ${error instanceof Error ? error.message : error}`,
255
- );
256
- process.exit(1);
257
- });
258
- }
@@ -1,320 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { promises as fs } from "node:fs";
4
- import path from "node:path";
5
- import process from "node:process";
6
- import { execFileSync } from "node:child_process";
7
-
8
- /**
9
- * Retry-capable fs.rm for Windows EBUSY/EPERM/ENOTEMPTY errors.
10
- * Uses linear backoff (attempt * 50ms) with up to 5 retries.
11
- */
12
- async function removeWithRetry(targetPath, options) {
13
- const retryableCodes = new Set(["ENOTEMPTY", "EPERM", "EBUSY"]);
14
- const maxAttempts = 6;
15
-
16
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
17
- try {
18
- await fs.rm(targetPath, options);
19
- return;
20
- } catch (error) {
21
- const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
22
- const shouldRetry = code !== undefined && retryableCodes.has(code);
23
- if (!shouldRetry || attempt === maxAttempts) {
24
- throw error;
25
- }
26
- await new Promise((resolve) => setTimeout(resolve, attempt * 50));
27
- }
28
- }
29
- }
30
-
31
- const ROOT_TEMP_PATTERN = /^\.?tmp/i;
32
- const GENERATED_DIRS = new Set([
33
- "coverage",
34
- "dist",
35
- "node_modules",
36
- ".omx",
37
- ".opencode",
38
- ".sisyphus",
39
- ]);
40
- const TRACKED_SCRATCH_FILES = new Set([
41
- "task_plan.md",
42
- "findings.md",
43
- "progress.md",
44
- "test-results.md",
45
- ]);
46
- const TRAVERSAL_SKIP_DIRS = new Set([
47
- ".git",
48
- "node_modules",
49
- "dist",
50
- "coverage",
51
- "vendor",
52
- ".omx",
53
- ".opencode",
54
- ".sisyphus",
55
- ".history",
56
- ]);
57
- const REQUIRED_GITIGNORE_PATTERNS = [
58
- "tmp",
59
- ".tmp",
60
- "coverage/",
61
- "dist/",
62
- "node_modules/",
63
- ".omx/",
64
- ".opencode/",
65
- ".sisyphus/",
66
- "task_plan.md",
67
- "findings.md",
68
- "progress.md",
69
- "test-results.md",
70
- ];
71
-
72
- function parseArgs(argv) {
73
- const args = {
74
- command: "",
75
- mode: "safe",
76
- root: process.cwd(),
77
- dryRun: false,
78
- };
79
-
80
- if (argv.length === 0) {
81
- throw new Error("Usage: repo-hygiene.js <clean|check> [--mode aggressive] [--root path] [--dry-run]");
82
- }
83
- args.command = argv[0] ?? "";
84
-
85
- for (let i = 1; i < argv.length; i += 1) {
86
- const arg = argv[i];
87
- if (arg === "--mode") {
88
- const value = argv[i + 1];
89
- if (!value || value.startsWith("--")) {
90
- throw new Error("--mode requires a value");
91
- }
92
- args.mode = value;
93
- i += 1;
94
- } else if (arg === "--root") {
95
- const value = argv[i + 1];
96
- if (!value || value.startsWith("--")) {
97
- throw new Error("--root requires a value");
98
- }
99
- args.root = value;
100
- i += 1;
101
- } else if (arg === "--dry-run") {
102
- args.dryRun = true;
103
- } else if (arg.startsWith("--")) {
104
- throw new Error(`Unknown flag: ${arg}`);
105
- } else {
106
- throw new Error(`Unexpected positional argument: ${arg}`);
107
- }
108
- }
109
-
110
- if (!args.root) {
111
- throw new Error("--root requires a value");
112
- }
113
- if (args.command === "clean" && args.mode !== "aggressive") {
114
- throw new Error('Only "--mode aggressive" is allowed for clean');
115
- }
116
-
117
- return args;
118
- }
119
-
120
- async function ensureRepoRoot(rootPath) {
121
- const resolved = path.resolve(rootPath);
122
- const gitDir = path.join(resolved, ".git");
123
- try {
124
- await fs.stat(gitDir);
125
- return resolved;
126
- } catch {
127
- throw new Error(`Root path is not a git working tree: ${resolved}`);
128
- }
129
- }
130
-
131
- function isDeletionCandidate(name, isDirectory) {
132
- if (TRACKED_SCRATCH_FILES.has(name)) {
133
- return true;
134
- }
135
- if (isDirectory && GENERATED_DIRS.has(name)) {
136
- return true;
137
- }
138
- return ROOT_TEMP_PATTERN.test(name);
139
- }
140
-
141
- function isProtectedName(name) {
142
- return [
143
- ".git",
144
- "lib",
145
- "test",
146
- "docs",
147
- "scripts",
148
- "config",
149
- "assets",
150
- "vendor",
151
- ].includes(name);
152
- }
153
-
154
- function isDeepTempCandidate(name, isDirectory) {
155
- if (isDirectory && (name === "tmp" || ROOT_TEMP_PATTERN.test(name))) {
156
- return true;
157
- }
158
- if (!isDirectory && (name.startsWith(".tmp") || name.endsWith(".tmp"))) {
159
- return true;
160
- }
161
- return false;
162
- }
163
-
164
- async function collectNestedTempCandidates(rootPath, dirPath, addCandidate) {
165
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
166
- for (const entry of entries) {
167
- const entryPath = path.join(dirPath, entry.name);
168
- if (entry.isDirectory() && TRAVERSAL_SKIP_DIRS.has(entry.name)) {
169
- continue;
170
- }
171
- if (isDeepTempCandidate(entry.name, entry.isDirectory())) {
172
- addCandidate(
173
- path.relative(rootPath, entryPath).replaceAll("\\", "/"),
174
- entryPath,
175
- entry.isDirectory(),
176
- );
177
- if (entry.isDirectory()) {
178
- continue;
179
- }
180
- }
181
- if (entry.isDirectory()) {
182
- await collectNestedTempCandidates(rootPath, entryPath, addCandidate);
183
- }
184
- }
185
- }
186
-
187
- async function collectCandidates(rootPath) {
188
- const rootEntries = await fs.readdir(rootPath, { withFileTypes: true });
189
- const candidates = [];
190
- const candidatePaths = new Set();
191
- const addCandidate = (name, candidatePath, isDirectory) => {
192
- if (candidatePaths.has(candidatePath)) {
193
- return;
194
- }
195
- candidatePaths.add(candidatePath);
196
- candidates.push({ name, path: candidatePath, isDirectory });
197
- };
198
- for (const entry of rootEntries) {
199
- if (isProtectedName(entry.name)) {
200
- continue;
201
- }
202
- if (!isDeletionCandidate(entry.name, entry.isDirectory())) {
203
- continue;
204
- }
205
- addCandidate(entry.name, path.join(rootPath, entry.name), entry.isDirectory());
206
- }
207
-
208
- await collectNestedTempCandidates(rootPath, rootPath, addCandidate);
209
- return candidates;
210
- }
211
-
212
- async function clean(rootPath, dryRun) {
213
- const candidates = await collectCandidates(rootPath);
214
- if (candidates.length === 0) {
215
- console.log("repo-hygiene: no cleanup candidates found");
216
- return;
217
- }
218
-
219
- for (const candidate of candidates) {
220
- const relative = path.relative(rootPath, candidate.path);
221
- if (!relative || relative.includes("..")) {
222
- throw new Error(`Refusing to delete non-root candidate: ${candidate.path}`);
223
- }
224
-
225
- if (dryRun) {
226
- console.log(`[dry-run] delete ${candidate.name}`);
227
- continue;
228
- }
229
- await removeWithRetry(candidate.path, { recursive: true, force: true });
230
- console.log(`deleted ${candidate.name}`);
231
- }
232
- }
233
-
234
- function getTrackedPaths(rootPath) {
235
- try {
236
- const output = execFileSync("git", ["ls-files"], {
237
- cwd: rootPath,
238
- encoding: "utf-8",
239
- stdio: ["ignore", "pipe", "ignore"],
240
- });
241
- return output.split(/\r?\n/).filter(Boolean);
242
- } catch (error) {
243
- throw new Error(
244
- `git ls-files failed in getTrackedPaths: ${error instanceof Error ? error.message : String(error)}`,
245
- );
246
- }
247
- }
248
-
249
- async function check(rootPath) {
250
- let hasError = false;
251
-
252
- const tracked = getTrackedPaths(rootPath);
253
- const trackedViolations = new Set();
254
- for (const trackedPath of tracked) {
255
- const normalized = trackedPath.replaceAll("\\", "/");
256
- const basename = normalized.split("/").pop() ?? normalized;
257
- if (TRACKED_SCRATCH_FILES.has(basename)) {
258
- trackedViolations.add(normalized);
259
- }
260
- }
261
-
262
- if (trackedViolations.size > 0) {
263
- hasError = true;
264
- console.error("repo-hygiene check failed: tracked scratch files present:");
265
- for (const item of [...trackedViolations].sort()) {
266
- console.error(` - ${item}`);
267
- }
268
- }
269
-
270
- const gitignorePath = path.join(rootPath, ".gitignore");
271
- let gitignore = "";
272
- try {
273
- gitignore = await fs.readFile(gitignorePath, "utf-8");
274
- } catch (error) {
275
- console.warn(
276
- `repo-hygiene warning: unable to read .gitignore at ${gitignorePath}: ${error instanceof Error ? error.message : String(error)}`,
277
- );
278
- }
279
- const gitignoreLines = new Set(
280
- gitignore
281
- .split(/\r?\n/)
282
- .map((line) => line.trim())
283
- .filter((line) => line.length > 0 && !line.startsWith("#")),
284
- );
285
- const missingPatterns = REQUIRED_GITIGNORE_PATTERNS.filter((pattern) => !gitignoreLines.has(pattern));
286
- if (missingPatterns.length > 0) {
287
- hasError = true;
288
- console.error("repo-hygiene check failed: missing required .gitignore patterns:");
289
- for (const pattern of missingPatterns) {
290
- console.error(` - ${pattern}`);
291
- }
292
- }
293
-
294
- if (hasError) {
295
- process.exitCode = 1;
296
- return;
297
- }
298
- console.log("repo-hygiene check passed");
299
- }
300
-
301
- async function main() {
302
- const args = parseArgs(process.argv.slice(2));
303
- const rootPath = await ensureRepoRoot(args.root);
304
-
305
- if (args.command === "clean") {
306
- await clean(rootPath, args.dryRun);
307
- return;
308
- }
309
- if (args.command === "check") {
310
- await check(rootPath);
311
- return;
312
- }
313
-
314
- throw new Error(`Unknown command: ${args.command}`);
315
- }
316
-
317
- main().catch((error) => {
318
- console.error(`repo-hygiene error: ${error instanceof Error ? error.message : String(error)}`);
319
- process.exitCode = 1;
320
- });
@@ -1,7 +0,0 @@
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
-