codex-multi-auth 2.1.13-beta.1 → 2.2.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.
Files changed (214) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +415 -415
  3. package/assets/codex-multi-auth-icon.svg +39 -39
  4. package/assets/readme-hero.svg +31 -31
  5. package/config/README.md +29 -29
  6. package/config/codex-legacy.json +571 -571
  7. package/config/codex-modern.json +258 -258
  8. package/config/minimal-codex.json +15 -15
  9. package/config/schema/config.schema.json +20 -20
  10. package/dist/lib/accounts.d.ts.map +1 -1
  11. package/dist/lib/accounts.js +30 -1
  12. package/dist/lib/accounts.js.map +1 -1
  13. package/dist/lib/auth/org-override.d.ts +19 -0
  14. package/dist/lib/auth/org-override.d.ts.map +1 -0
  15. package/dist/lib/auth/org-override.js +23 -0
  16. package/dist/lib/auth/org-override.js.map +1 -0
  17. package/dist/lib/circuit-breaker.d.ts +6 -0
  18. package/dist/lib/circuit-breaker.d.ts.map +1 -1
  19. package/dist/lib/circuit-breaker.js +8 -0
  20. package/dist/lib/circuit-breaker.js.map +1 -1
  21. package/dist/lib/codex-manager/account-manager-commands.d.ts +14 -0
  22. package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -0
  23. package/dist/lib/codex-manager/account-manager-commands.js +43 -0
  24. package/dist/lib/codex-manager/account-manager-commands.js.map +1 -0
  25. package/dist/lib/codex-manager/commands/best.d.ts +1 -1
  26. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
  27. package/dist/lib/codex-manager/commands/best.js +2 -1
  28. package/dist/lib/codex-manager/commands/best.js.map +1 -1
  29. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +16 -0
  30. package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -1
  31. package/dist/lib/codex-manager/commands/debug-bundle.js +80 -5
  32. package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -1
  33. package/dist/lib/codex-manager/commands/forecast.d.ts +1 -1
  34. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  35. package/dist/lib/codex-manager/commands/forecast.js +2 -1
  36. package/dist/lib/codex-manager/commands/forecast.js.map +1 -1
  37. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  38. package/dist/lib/codex-manager/commands/report.js +2 -2
  39. package/dist/lib/codex-manager/commands/report.js.map +1 -1
  40. package/dist/lib/codex-manager/commands/status.d.ts +2 -0
  41. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  42. package/dist/lib/codex-manager/commands/status.js +84 -25
  43. package/dist/lib/codex-manager/commands/status.js.map +1 -1
  44. package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -1
  45. package/dist/lib/codex-manager/commands/switch.js +7 -0
  46. package/dist/lib/codex-manager/commands/switch.js.map +1 -1
  47. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -1
  48. package/dist/lib/codex-manager/experimental-settings-prompt.js +13 -3
  49. package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -1
  50. package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -1
  51. package/dist/lib/codex-manager/forecast-report-commands.js +3 -3
  52. package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -1
  53. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  54. package/dist/lib/codex-manager/repair-commands.js +11 -1
  55. package/dist/lib/codex-manager/repair-commands.js.map +1 -1
  56. package/dist/lib/codex-manager/settings-hub/experimental.d.ts.map +1 -1
  57. package/dist/lib/codex-manager/settings-hub/experimental.js +8 -0
  58. package/dist/lib/codex-manager/settings-hub/experimental.js.map +1 -1
  59. package/dist/lib/codex-manager.d.ts +3 -0
  60. package/dist/lib/codex-manager.d.ts.map +1 -1
  61. package/dist/lib/codex-manager.js +53 -65
  62. package/dist/lib/codex-manager.js.map +1 -1
  63. package/dist/lib/config.d.ts +26 -0
  64. package/dist/lib/config.d.ts.map +1 -1
  65. package/dist/lib/config.js +152 -5
  66. package/dist/lib/config.js.map +1 -1
  67. package/dist/lib/context-overflow.d.ts +7 -2
  68. package/dist/lib/context-overflow.d.ts.map +1 -1
  69. package/dist/lib/context-overflow.js +65 -47
  70. package/dist/lib/context-overflow.js.map +1 -1
  71. package/dist/lib/errors.d.ts +16 -0
  72. package/dist/lib/errors.d.ts.map +1 -1
  73. package/dist/lib/errors.js +23 -0
  74. package/dist/lib/errors.js.map +1 -1
  75. package/dist/lib/local-bridge.d.ts +8 -0
  76. package/dist/lib/local-bridge.d.ts.map +1 -1
  77. package/dist/lib/local-bridge.js +81 -7
  78. package/dist/lib/local-bridge.js.map +1 -1
  79. package/dist/lib/logger.d.ts +9 -1
  80. package/dist/lib/logger.d.ts.map +1 -1
  81. package/dist/lib/logger.js +104 -19
  82. package/dist/lib/logger.js.map +1 -1
  83. package/dist/lib/oauth-success.html +125 -125
  84. package/dist/lib/oc-chatgpt-orchestrator.d.ts +7 -1
  85. package/dist/lib/oc-chatgpt-orchestrator.d.ts.map +1 -1
  86. package/dist/lib/oc-chatgpt-orchestrator.js +68 -9
  87. package/dist/lib/oc-chatgpt-orchestrator.js.map +1 -1
  88. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  89. package/dist/lib/policy/runtime-policy.js +15 -1
  90. package/dist/lib/policy/runtime-policy.js.map +1 -1
  91. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  92. package/dist/lib/prompts/codex.d.ts.map +1 -1
  93. package/dist/lib/prompts/codex.js +203 -108
  94. package/dist/lib/prompts/codex.js.map +1 -1
  95. package/dist/lib/prompts/fetch-utils.d.ts +72 -0
  96. package/dist/lib/prompts/fetch-utils.d.ts.map +1 -0
  97. package/dist/lib/prompts/fetch-utils.js +165 -0
  98. package/dist/lib/prompts/fetch-utils.js.map +1 -0
  99. package/dist/lib/prompts/host-codex-prompt.d.ts.map +1 -1
  100. package/dist/lib/prompts/host-codex-prompt.js +24 -10
  101. package/dist/lib/prompts/host-codex-prompt.js.map +1 -1
  102. package/dist/lib/quota-cache.d.ts.map +1 -1
  103. package/dist/lib/quota-cache.js +10 -1
  104. package/dist/lib/quota-cache.js.map +1 -1
  105. package/dist/lib/quota-probe.d.ts +21 -0
  106. package/dist/lib/quota-probe.d.ts.map +1 -1
  107. package/dist/lib/quota-probe.js +39 -0
  108. package/dist/lib/quota-probe.js.map +1 -1
  109. package/dist/lib/quota-readiness.d.ts +3 -1
  110. package/dist/lib/quota-readiness.d.ts.map +1 -1
  111. package/dist/lib/quota-readiness.js +16 -3
  112. package/dist/lib/quota-readiness.js.map +1 -1
  113. package/dist/lib/recovery/storage.d.ts +7 -0
  114. package/dist/lib/recovery/storage.d.ts.map +1 -1
  115. package/dist/lib/recovery/storage.js +178 -10
  116. package/dist/lib/recovery/storage.js.map +1 -1
  117. package/dist/lib/refresh-lease.d.ts +1 -1
  118. package/dist/lib/refresh-lease.d.ts.map +1 -1
  119. package/dist/lib/refresh-lease.js +25 -3
  120. package/dist/lib/refresh-lease.js.map +1 -1
  121. package/dist/lib/refresh-queue.d.ts.map +1 -1
  122. package/dist/lib/refresh-queue.js +31 -17
  123. package/dist/lib/refresh-queue.js.map +1 -1
  124. package/dist/lib/request/fetch-helpers.d.ts +0 -5
  125. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  126. package/dist/lib/request/fetch-helpers.js +36 -7
  127. package/dist/lib/request/fetch-helpers.js.map +1 -1
  128. package/dist/lib/rotation.d.ts +12 -0
  129. package/dist/lib/rotation.d.ts.map +1 -1
  130. package/dist/lib/rotation.js +36 -0
  131. package/dist/lib/rotation.js.map +1 -1
  132. package/dist/lib/runtime/account-check-types.d.ts +1 -0
  133. package/dist/lib/runtime/account-check-types.d.ts.map +1 -1
  134. package/dist/lib/runtime/account-check-types.js +1 -0
  135. package/dist/lib/runtime/account-check-types.js.map +1 -1
  136. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  137. package/dist/lib/runtime/account-check.js +15 -4
  138. package/dist/lib/runtime/account-check.js.map +1 -1
  139. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  140. package/dist/lib/runtime/runtime-observability.js +24 -2
  141. package/dist/lib/runtime/runtime-observability.js.map +1 -1
  142. package/dist/lib/runtime-rotation-proxy.d.ts +3 -0
  143. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  144. package/dist/lib/runtime-rotation-proxy.js +296 -21
  145. package/dist/lib/runtime-rotation-proxy.js.map +1 -1
  146. package/dist/lib/schemas.d.ts +2 -0
  147. package/dist/lib/schemas.d.ts.map +1 -1
  148. package/dist/lib/schemas.js +2 -0
  149. package/dist/lib/schemas.js.map +1 -1
  150. package/dist/lib/storage/account-clear.d.ts.map +1 -1
  151. package/dist/lib/storage/account-clear.js +4 -2
  152. package/dist/lib/storage/account-clear.js.map +1 -1
  153. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -1
  154. package/dist/lib/storage/flagged-storage-io.js +4 -1
  155. package/dist/lib/storage/flagged-storage-io.js.map +1 -1
  156. package/dist/lib/storage/import-export.d.ts.map +1 -1
  157. package/dist/lib/storage/import-export.js +29 -9
  158. package/dist/lib/storage/import-export.js.map +1 -1
  159. package/dist/lib/storage/paths.d.ts.map +1 -1
  160. package/dist/lib/storage/paths.js +93 -0
  161. package/dist/lib/storage/paths.js.map +1 -1
  162. package/dist/lib/storage/storage-parser.d.ts.map +1 -1
  163. package/dist/lib/storage/storage-parser.js +7 -1
  164. package/dist/lib/storage/storage-parser.js.map +1 -1
  165. package/dist/lib/storage.d.ts.map +1 -1
  166. package/dist/lib/storage.js +13 -9
  167. package/dist/lib/storage.js.map +1 -1
  168. package/dist/lib/table-formatter.d.ts.map +1 -1
  169. package/dist/lib/table-formatter.js +20 -2
  170. package/dist/lib/table-formatter.js.map +1 -1
  171. package/dist/lib/types.d.ts +7 -0
  172. package/dist/lib/types.d.ts.map +1 -1
  173. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  174. package/dist/lib/ui/auth-menu.js +8 -2
  175. package/dist/lib/ui/auth-menu.js.map +1 -1
  176. package/dist/lib/ui/display-width.d.ts +27 -0
  177. package/dist/lib/ui/display-width.d.ts.map +1 -0
  178. package/dist/lib/ui/display-width.js +194 -0
  179. package/dist/lib/ui/display-width.js.map +1 -0
  180. package/dist/lib/ui/select.d.ts +23 -0
  181. package/dist/lib/ui/select.d.ts.map +1 -1
  182. package/dist/lib/ui/select.js +64 -16
  183. package/dist/lib/ui/select.js.map +1 -1
  184. package/dist/lib/ui/theme.d.ts +13 -0
  185. package/dist/lib/ui/theme.d.ts.map +1 -1
  186. package/dist/lib/ui/theme.js +34 -1
  187. package/dist/lib/ui/theme.js.map +1 -1
  188. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  189. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  190. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  191. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  192. package/node_modules/@codex-ai/plugin/package.json +9 -9
  193. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -0
  194. package/node_modules/@codex-ai/sdk/dist/index.js +2 -0
  195. package/node_modules/@codex-ai/sdk/package.json +8 -0
  196. package/package.json +196 -193
  197. package/scripts/codex-app-launcher.js +653 -653
  198. package/scripts/codex-app-router.js +322 -322
  199. package/scripts/codex-bin-resolver.js +254 -254
  200. package/scripts/codex-multi-auth.js +59 -59
  201. package/scripts/codex-routing.js +59 -56
  202. package/scripts/codex.js +4782 -4275
  203. package/scripts/install-codex-auth-utils.js +206 -206
  204. package/scripts/mcodex +117 -0
  205. package/scripts/postinstall.js +343 -343
  206. package/scripts/preuninstall.js +254 -254
  207. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  208. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  209. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  210. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  211. package/vendor/codex-ai-plugin/package.json +9 -9
  212. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  213. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  214. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -1,206 +1,206 @@
1
- // @ts-check
2
-
3
- import { rename as fsRename } from "node:fs/promises";
4
- import { homedir } from "node:os";
5
- import { join } from "node:path";
6
- import process from "node:process";
7
-
8
- const PLUGIN_NAME = "codex-multi-auth";
9
-
10
- const TRUE_VALUES = new Set(["1", "true", "yes"]);
11
- const FALSE_VALUES = new Set(["0", "false", "no"]);
12
- const CI_ENV_KEYS = [
13
- "CI",
14
- "GITHUB_ACTIONS",
15
- "GITLAB_CI",
16
- "CIRCLECI",
17
- "BUILDKITE",
18
- "TF_BUILD",
19
- "TEAMCITY_VERSION",
20
- "JENKINS_URL",
21
- "TRAVIS",
22
- "APPVEYOR",
23
- "BITBUCKET_BUILD_NUMBER",
24
- ];
25
-
26
- /** @param {string | undefined} value */
27
- function readOptionalBooleanValue(value) {
28
- if (value === undefined || value.trim().length === 0) return null;
29
- const normalized = value.trim().toLowerCase();
30
- if (TRUE_VALUES.has(normalized)) return true;
31
- if (FALSE_VALUES.has(normalized)) return false;
32
- return null;
33
- }
34
-
35
- /** @param {NodeJS.ProcessEnv} env @param {string} key */
36
- function isEnabledEnvFlag(env, key) {
37
- const value = env[key];
38
- if (value === undefined || value.trim().length === 0) return false;
39
- return readOptionalBooleanValue(value) !== false;
40
- }
41
-
42
- /** @param {NodeJS.ProcessEnv} [env] */
43
- export function isCiEnvironment(env = process.env) {
44
- if (readOptionalBooleanValue(env.npm_config_ignore_scripts) === true) return true;
45
- return CI_ENV_KEYS.some((key) => isEnabledEnvFlag(env, key));
46
- }
47
- export const FILE_RETRY_CODES = new Set([
48
- "EBUSY",
49
- "EPERM",
50
- "EAGAIN",
51
- "ENOTEMPTY",
52
- "EACCES",
53
- ]);
54
- export const FILE_RETRY_MAX_ATTEMPTS = 6;
55
- export const FILE_RETRY_BASE_DELAY_MS = 25;
56
- export const FILE_RETRY_JITTER_MS = 20;
57
-
58
- /**
59
- * @param {NodeJS.Platform} [platform]
60
- * @param {NodeJS.ProcessEnv} [env]
61
- * @param {string} [home]
62
- */
63
- export function resolveInstallPaths(
64
- platform = process.platform,
65
- env = process.env,
66
- home = homedir(),
67
- ) {
68
- const isWindows = platform === "win32";
69
- const appData = (env.APPDATA ?? "").trim();
70
- const localAppData = (env.LOCALAPPDATA ?? appData).trim();
71
- const configBase = isWindows
72
- ? appData || join(home, "AppData", "Roaming")
73
- : join(home, ".config");
74
- const cacheBase = isWindows
75
- ? localAppData || join(home, "AppData", "Local")
76
- : join(home, ".cache");
77
- const configDir = join(configBase, "Codex");
78
- const configPath = join(configDir, "Codex.json");
79
- const cacheDir = join(cacheBase, "Codex");
80
- return {
81
- configDir,
82
- configPath,
83
- cacheDir,
84
- cacheNodeModules: join(cacheDir, "node_modules", PLUGIN_NAME),
85
- cacheBunLock: join(cacheDir, "bun.lock"),
86
- cachePackageJson: join(cacheDir, "package.json"),
87
- };
88
- }
89
-
90
- /** @param {unknown} list */
91
- export function removePluginFromList(list) {
92
- const entries = Array.isArray(list) ? list.filter(Boolean) : [];
93
- return entries.filter((entry) => {
94
- if (typeof entry !== "string") return true;
95
- return entry !== PLUGIN_NAME && !entry.startsWith(`${PLUGIN_NAME}@`);
96
- });
97
- }
98
-
99
- /** @param {unknown} list */
100
- export function normalizePluginList(list) {
101
- const entries = Array.isArray(list) ? list.filter(Boolean) : [];
102
- const filtered = entries.filter((entry) => {
103
- if (typeof entry !== "string") return true;
104
- return entry !== PLUGIN_NAME && !entry.startsWith(`${PLUGIN_NAME}@`);
105
- });
106
- const deduped = [];
107
- const seen = new Set();
108
- for (const entry of filtered) {
109
- const key =
110
- typeof entry === "string" ? `s:${entry}` : `j:${JSON.stringify(entry)}`;
111
- if (seen.has(key)) continue;
112
- seen.add(key);
113
- deduped.push(entry);
114
- }
115
- return [...deduped, PLUGIN_NAME];
116
- }
117
-
118
- /** @param {number} ms */
119
- function sleep(ms) {
120
- // Keep this helper local so installer scripts remain standalone and do not depend on lib/.
121
- return new Promise((resolve) => {
122
- setTimeout(resolve, ms);
123
- });
124
- }
125
-
126
- /** @param {unknown} error */
127
- function shouldRetryFileOperation(error) {
128
- const fileError = /** @type {NodeJS.ErrnoException | undefined} */ (error);
129
- return (
130
- fileError instanceof Error &&
131
- typeof fileError.code === "string" &&
132
- FILE_RETRY_CODES.has(fileError.code)
133
- );
134
- }
135
-
136
- /** @template T @param {() => Promise<T>} operation */
137
- export async function withFileOperationRetry(operation) {
138
- for (let attempt = 1; ; attempt += 1) {
139
- try {
140
- return await operation();
141
- } catch (error) {
142
- if (
143
- !shouldRetryFileOperation(error) ||
144
- attempt >= FILE_RETRY_MAX_ATTEMPTS
145
- ) {
146
- throw error;
147
- }
148
-
149
- const jitter = Math.floor(Math.random() * FILE_RETRY_JITTER_MS);
150
- const delayMs = FILE_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1) + jitter;
151
- await sleep(delayMs);
152
- }
153
- }
154
- }
155
-
156
- /**
157
- * @param {string} sourcePath
158
- * @param {string} targetPath
159
- * @param {{
160
- * rename?: (sourcePath: string, targetPath: string) => Promise<void>,
161
- * log?: (message: string) => void,
162
- * maxRetries?: number,
163
- * baseDelayMs?: number,
164
- * jitterMs?: number,
165
- * random?: () => number,
166
- * sleep?: (ms: number) => Promise<void>,
167
- * }} [options]
168
- */
169
- export async function renameWithRetry(sourcePath, targetPath, options = {}) {
170
- const {
171
- rename = fsRename,
172
- log = () => {},
173
- maxRetries = FILE_RETRY_MAX_ATTEMPTS,
174
- baseDelayMs = FILE_RETRY_BASE_DELAY_MS,
175
- jitterMs = FILE_RETRY_JITTER_MS,
176
- random = Math.random,
177
- sleep: sleepImpl = sleep,
178
- } = options;
179
-
180
- if (!Number.isInteger(maxRetries) || maxRetries < 1) {
181
- throw new RangeError("maxRetries must be an integer >= 1");
182
- }
183
-
184
- for (let attempt = 0; attempt < maxRetries; attempt += 1) {
185
- try {
186
- await rename(sourcePath, targetPath);
187
- return;
188
- } catch (error) {
189
- const code =
190
- error && typeof error === "object" && "code" in error
191
- ? error.code
192
- : undefined;
193
- const isRetryable =
194
- typeof code === "string" && FILE_RETRY_CODES.has(code);
195
- if (!isRetryable || attempt === maxRetries - 1) {
196
- throw error;
197
- }
198
- const delayMs =
199
- baseDelayMs * 2 ** attempt + Math.floor(random() * jitterMs);
200
- log(
201
- `Retrying atomic rename (${attempt + 1}/${maxRetries}) code=${code ?? "unknown"} source=${sourcePath} target=${targetPath} delayMs=${delayMs}`,
202
- );
203
- await sleepImpl(delayMs);
204
- }
205
- }
206
- }
1
+ // @ts-check
2
+
3
+ import { rename as fsRename } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import process from "node:process";
7
+
8
+ const PLUGIN_NAME = "codex-multi-auth";
9
+
10
+ const TRUE_VALUES = new Set(["1", "true", "yes"]);
11
+ const FALSE_VALUES = new Set(["0", "false", "no"]);
12
+ const CI_ENV_KEYS = [
13
+ "CI",
14
+ "GITHUB_ACTIONS",
15
+ "GITLAB_CI",
16
+ "CIRCLECI",
17
+ "BUILDKITE",
18
+ "TF_BUILD",
19
+ "TEAMCITY_VERSION",
20
+ "JENKINS_URL",
21
+ "TRAVIS",
22
+ "APPVEYOR",
23
+ "BITBUCKET_BUILD_NUMBER",
24
+ ];
25
+
26
+ /** @param {string | undefined} value */
27
+ function readOptionalBooleanValue(value) {
28
+ if (value === undefined || value.trim().length === 0) return null;
29
+ const normalized = value.trim().toLowerCase();
30
+ if (TRUE_VALUES.has(normalized)) return true;
31
+ if (FALSE_VALUES.has(normalized)) return false;
32
+ return null;
33
+ }
34
+
35
+ /** @param {NodeJS.ProcessEnv} env @param {string} key */
36
+ function isEnabledEnvFlag(env, key) {
37
+ const value = env[key];
38
+ if (value === undefined || value.trim().length === 0) return false;
39
+ return readOptionalBooleanValue(value) !== false;
40
+ }
41
+
42
+ /** @param {NodeJS.ProcessEnv} [env] */
43
+ export function isCiEnvironment(env = process.env) {
44
+ if (readOptionalBooleanValue(env.npm_config_ignore_scripts) === true) return true;
45
+ return CI_ENV_KEYS.some((key) => isEnabledEnvFlag(env, key));
46
+ }
47
+ export const FILE_RETRY_CODES = new Set([
48
+ "EBUSY",
49
+ "EPERM",
50
+ "EAGAIN",
51
+ "ENOTEMPTY",
52
+ "EACCES",
53
+ ]);
54
+ export const FILE_RETRY_MAX_ATTEMPTS = 6;
55
+ export const FILE_RETRY_BASE_DELAY_MS = 25;
56
+ export const FILE_RETRY_JITTER_MS = 20;
57
+
58
+ /**
59
+ * @param {NodeJS.Platform} [platform]
60
+ * @param {NodeJS.ProcessEnv} [env]
61
+ * @param {string} [home]
62
+ */
63
+ export function resolveInstallPaths(
64
+ platform = process.platform,
65
+ env = process.env,
66
+ home = homedir(),
67
+ ) {
68
+ const isWindows = platform === "win32";
69
+ const appData = (env.APPDATA ?? "").trim();
70
+ const localAppData = (env.LOCALAPPDATA ?? appData).trim();
71
+ const configBase = isWindows
72
+ ? appData || join(home, "AppData", "Roaming")
73
+ : join(home, ".config");
74
+ const cacheBase = isWindows
75
+ ? localAppData || join(home, "AppData", "Local")
76
+ : join(home, ".cache");
77
+ const configDir = join(configBase, "Codex");
78
+ const configPath = join(configDir, "Codex.json");
79
+ const cacheDir = join(cacheBase, "Codex");
80
+ return {
81
+ configDir,
82
+ configPath,
83
+ cacheDir,
84
+ cacheNodeModules: join(cacheDir, "node_modules", PLUGIN_NAME),
85
+ cacheBunLock: join(cacheDir, "bun.lock"),
86
+ cachePackageJson: join(cacheDir, "package.json"),
87
+ };
88
+ }
89
+
90
+ /** @param {unknown} list */
91
+ export function removePluginFromList(list) {
92
+ const entries = Array.isArray(list) ? list.filter(Boolean) : [];
93
+ return entries.filter((entry) => {
94
+ if (typeof entry !== "string") return true;
95
+ return entry !== PLUGIN_NAME && !entry.startsWith(`${PLUGIN_NAME}@`);
96
+ });
97
+ }
98
+
99
+ /** @param {unknown} list */
100
+ export function normalizePluginList(list) {
101
+ const entries = Array.isArray(list) ? list.filter(Boolean) : [];
102
+ const filtered = entries.filter((entry) => {
103
+ if (typeof entry !== "string") return true;
104
+ return entry !== PLUGIN_NAME && !entry.startsWith(`${PLUGIN_NAME}@`);
105
+ });
106
+ const deduped = [];
107
+ const seen = new Set();
108
+ for (const entry of filtered) {
109
+ const key =
110
+ typeof entry === "string" ? `s:${entry}` : `j:${JSON.stringify(entry)}`;
111
+ if (seen.has(key)) continue;
112
+ seen.add(key);
113
+ deduped.push(entry);
114
+ }
115
+ return [...deduped, PLUGIN_NAME];
116
+ }
117
+
118
+ /** @param {number} ms */
119
+ function sleep(ms) {
120
+ // Keep this helper local so installer scripts remain standalone and do not depend on lib/.
121
+ return new Promise((resolve) => {
122
+ setTimeout(resolve, ms);
123
+ });
124
+ }
125
+
126
+ /** @param {unknown} error */
127
+ function shouldRetryFileOperation(error) {
128
+ const fileError = /** @type {NodeJS.ErrnoException | undefined} */ (error);
129
+ return (
130
+ fileError instanceof Error &&
131
+ typeof fileError.code === "string" &&
132
+ FILE_RETRY_CODES.has(fileError.code)
133
+ );
134
+ }
135
+
136
+ /** @template T @param {() => Promise<T>} operation */
137
+ export async function withFileOperationRetry(operation) {
138
+ for (let attempt = 1; ; attempt += 1) {
139
+ try {
140
+ return await operation();
141
+ } catch (error) {
142
+ if (
143
+ !shouldRetryFileOperation(error) ||
144
+ attempt >= FILE_RETRY_MAX_ATTEMPTS
145
+ ) {
146
+ throw error;
147
+ }
148
+
149
+ const jitter = Math.floor(Math.random() * FILE_RETRY_JITTER_MS);
150
+ const delayMs = FILE_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1) + jitter;
151
+ await sleep(delayMs);
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @param {string} sourcePath
158
+ * @param {string} targetPath
159
+ * @param {{
160
+ * rename?: (sourcePath: string, targetPath: string) => Promise<void>,
161
+ * log?: (message: string) => void,
162
+ * maxRetries?: number,
163
+ * baseDelayMs?: number,
164
+ * jitterMs?: number,
165
+ * random?: () => number,
166
+ * sleep?: (ms: number) => Promise<void>,
167
+ * }} [options]
168
+ */
169
+ export async function renameWithRetry(sourcePath, targetPath, options = {}) {
170
+ const {
171
+ rename = fsRename,
172
+ log = () => {},
173
+ maxRetries = FILE_RETRY_MAX_ATTEMPTS,
174
+ baseDelayMs = FILE_RETRY_BASE_DELAY_MS,
175
+ jitterMs = FILE_RETRY_JITTER_MS,
176
+ random = Math.random,
177
+ sleep: sleepImpl = sleep,
178
+ } = options;
179
+
180
+ if (!Number.isInteger(maxRetries) || maxRetries < 1) {
181
+ throw new RangeError("maxRetries must be an integer >= 1");
182
+ }
183
+
184
+ for (let attempt = 0; attempt < maxRetries; attempt += 1) {
185
+ try {
186
+ await rename(sourcePath, targetPath);
187
+ return;
188
+ } catch (error) {
189
+ const code =
190
+ error && typeof error === "object" && "code" in error
191
+ ? error.code
192
+ : undefined;
193
+ const isRetryable =
194
+ typeof code === "string" && FILE_RETRY_CODES.has(code);
195
+ if (!isRetryable || attempt === maxRetries - 1) {
196
+ throw error;
197
+ }
198
+ const delayMs =
199
+ baseDelayMs * 2 ** attempt + Math.floor(random() * jitterMs);
200
+ log(
201
+ `Retrying atomic rename (${attempt + 1}/${maxRetries}) code=${code ?? "unknown"} source=${sourcePath} target=${targetPath} delayMs=${delayMs}`,
202
+ );
203
+ await sleepImpl(delayMs);
204
+ }
205
+ }
206
+ }
package/scripts/mcodex ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ monitor_interval="${MCODEX_MONITOR_INTERVAL:-5}"
5
+ live_accounts=0
6
+ tmux_history_limit="${MCODEX_TMUX_HISTORY_LIMIT:-50000}"
7
+
8
+ # Security: monitor_interval is embedded into `watch -n <n> ...` command strings
9
+ # that tmux hands to a shell. An attacker-controlled MCODEX_MONITOR_INTERVAL like
10
+ # "5; rm -rf ~" would otherwise execute. Require a positive integer (optionally
11
+ # with a fractional part for sub-second intervals) and fall back to the default
12
+ # otherwise, so no shell metacharacters can ever reach the command string.
13
+ if ! [[ "$monitor_interval" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
14
+ echo "mcodex: invalid MCODEX_MONITOR_INTERVAL '$monitor_interval'; using 5" >&2
15
+ monitor_interval=5
16
+ fi
17
+
18
+ # Same hardening for the tmux scrollback history limit (used as a tmux option
19
+ # argument): must be a plain integer.
20
+ if ! [[ "$tmux_history_limit" =~ ^[0-9]+$ ]]; then
21
+ echo "mcodex: invalid MCODEX_TMUX_HISTORY_LIMIT '$tmux_history_limit'; using 50000" >&2
22
+ tmux_history_limit=50000
23
+ fi
24
+
25
+ quote_args() {
26
+ local quoted=()
27
+ local arg
28
+ for arg in "$@"; do
29
+ printf -v arg '%q' "$arg"
30
+ quoted+=("$arg")
31
+ done
32
+ # Join with single spaces and NO trailing space (a trailing space would become
33
+ # part of the interpolated tmux command string).
34
+ local IFS=' '
35
+ printf '%s' "${quoted[*]}"
36
+ }
37
+
38
+ require_watch() {
39
+ if ! command -v watch >/dev/null 2>&1; then
40
+ echo "mcodex: 'watch' is not installed; the live account monitor requires it (install procps / procps-ng)." >&2
41
+ return 1
42
+ fi
43
+ }
44
+
45
+ run_monitor() {
46
+ require_watch || return 1
47
+ watch -n "$monitor_interval" 'codex-multi-auth list'
48
+ }
49
+
50
+ configure_tmux_scrollback() {
51
+ local target="${1:-}"
52
+ local target_args=()
53
+ if [[ -n "$target" ]]; then
54
+ target_args=(-t "$target")
55
+ fi
56
+
57
+ tmux set-option "${target_args[@]}" mouse on >/dev/null 2>&1
58
+ tmux set-option "${target_args[@]}" history-limit "$tmux_history_limit" >/dev/null 2>&1
59
+ tmux bind-key -T root WheelUpPane copy-mode -e >/dev/null 2>&1
60
+ tmux bind-key -T copy-mode WheelUpPane send-keys -X scroll-up >/dev/null 2>&1
61
+ tmux bind-key -T copy-mode WheelDownPane send-keys -X scroll-down >/dev/null 2>&1
62
+ tmux bind-key -T copy-mode-vi WheelUpPane send-keys -X scroll-up >/dev/null 2>&1
63
+ tmux bind-key -T copy-mode-vi WheelDownPane send-keys -X scroll-down >/dev/null 2>&1
64
+ }
65
+
66
+ if [[ "${1:-}" == "--monitor" ]]; then
67
+ # --monitor takes no extra args; the live account list is fixed. Propagate the
68
+ # watch-availability check's exit code instead of masking it with `exit 0`.
69
+ run_monitor
70
+ exit $?
71
+ fi
72
+
73
+ if [[ "${1:-}" == "--tmux" || "${1:-}" == "-t" ]]; then
74
+ shift
75
+ if [[ "${1:-}" == "--live-accounts" ]]; then
76
+ live_accounts=1
77
+ shift
78
+ fi
79
+ if ! command -v tmux >/dev/null 2>&1; then
80
+ echo "mcodex: tmux is not installed; launching without tmux" >&2
81
+ exec codex-multi-auth-codex "$@"
82
+ fi
83
+
84
+ if [[ -n "${TMUX:-}" ]]; then
85
+ configure_tmux_scrollback
86
+ if [[ "$live_accounts" == "1" ]] && require_watch; then
87
+ tmux split-window -h "watch -n $monitor_interval 'codex-multi-auth list'"
88
+ fi
89
+ exec codex-multi-auth-codex "$@"
90
+ fi
91
+
92
+ session="${MCODEX_TMUX_SESSION:-mcodex}"
93
+ suffix="$(date +%H%M%S)"
94
+ if tmux has-session -t "$session" 2>/dev/null; then
95
+ session="${session}-${suffix}"
96
+ fi
97
+
98
+ # Build the inner shell-command as a single string. quote_args runs each arg
99
+ # through printf %q, so every token is already shell-safe (embedded quotes,
100
+ # spaces, $, backticks are escaped); the surrounding expansion is not
101
+ # re-evaluated, so the command tmux receives is exactly this string.
102
+ cmd="codex-multi-auth-codex"
103
+ args="$(quote_args "$@")"
104
+ if [[ -n "$args" ]]; then
105
+ cmd="$cmd $args"
106
+ fi
107
+ tmux new-session -d -s "$session" -n codex "$cmd"
108
+ configure_tmux_scrollback "$session"
109
+ if [[ "$live_accounts" == "1" ]] && require_watch; then
110
+ tmux split-window -h -t "$session:0" "watch -n $monitor_interval 'codex-multi-auth list'"
111
+ fi
112
+ tmux select-pane -t "$session:0.0"
113
+ tmux attach-session -t "$session"
114
+ exit 0
115
+ fi
116
+
117
+ exec codex-multi-auth-codex "$@"