codex-multi-auth 2.19.0 → 2.20.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 (107) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/LICENSE +21 -21
  3. package/README.md +149 -148
  4. package/assets/codex-multi-auth-icon.svg +39 -39
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +31 -31
  7. package/config/codex-legacy.json +1021 -1021
  8. package/config/codex-modern.json +347 -347
  9. package/config/minimal-codex.json +15 -15
  10. package/dist/index.js +1 -1
  11. package/dist/lib/accounts/rate-limits.js +5 -0
  12. package/dist/lib/accounts.js +5 -4
  13. package/dist/lib/auth/account-access.js +2 -2
  14. package/dist/lib/codex-cli/version.d.ts +22 -0
  15. package/dist/lib/codex-cli/version.js +78 -0
  16. package/dist/lib/codex-manager/commands/limits.d.ts +2 -0
  17. package/dist/lib/codex-manager/commands/limits.js +31 -0
  18. package/dist/lib/codex-manager/commands/status.js +1 -1
  19. package/dist/lib/codex-manager/dashboard-settings-data.js +2 -0
  20. package/dist/lib/codex-manager/formatters/subscription-status.d.ts +2 -2
  21. package/dist/lib/codex-manager/formatters/text-style.js +4 -1
  22. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts +1 -1
  23. package/dist/lib/codex-manager/settings-hub/dashboard.js +4 -0
  24. package/dist/lib/codex-manager/settings-panels.d.ts +1 -0
  25. package/dist/lib/codex-manager/settings-panels.js +1 -0
  26. package/dist/lib/codex-manager/theme-settings-panel.d.ts +2 -1
  27. package/dist/lib/codex-manager/theme-settings-panel.js +39 -0
  28. package/dist/lib/codex-manager.js +2 -0
  29. package/dist/lib/constants.d.ts +2 -0
  30. package/dist/lib/constants.js +2 -0
  31. package/dist/lib/context-budget/model-context-windows.d.ts +1 -1
  32. package/dist/lib/context-budget/model-context-windows.js +4 -0
  33. package/dist/lib/context-budget-response.js +8 -8
  34. package/dist/lib/context-overflow.js +10 -10
  35. package/dist/lib/dashboard-settings.d.ts +2 -0
  36. package/dist/lib/dashboard-settings.js +18 -0
  37. package/dist/lib/luna-reserve.d.ts +26 -0
  38. package/dist/lib/luna-reserve.js +68 -0
  39. package/dist/lib/model-route-policy.d.ts +5 -5
  40. package/dist/lib/oauth-success.html +125 -125
  41. package/dist/lib/policy/runtime-policy.js +14 -0
  42. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  43. package/dist/lib/prompts/codex.js +79 -79
  44. package/dist/lib/quota-readiness.d.ts +2 -2
  45. package/dist/lib/request/helpers/model-map.d.ts +1 -1
  46. package/dist/lib/request/helpers/model-map.js +12 -0
  47. package/dist/lib/request/response-outcome.d.ts +1 -1
  48. package/dist/lib/request/response-outcome.js +4 -1
  49. package/dist/lib/request/url-rewriting.js +3 -1
  50. package/dist/lib/runtime/account-model-catalog.d.ts +5 -5
  51. package/dist/lib/runtime/account-reset-credits.d.ts +2 -2
  52. package/dist/lib/runtime/account-status.js +2 -1
  53. package/dist/lib/runtime/luna-reserve-usage.d.ts +9 -0
  54. package/dist/lib/runtime/luna-reserve-usage.js +68 -0
  55. package/dist/lib/runtime/model-discovery-status.d.ts +11 -7
  56. package/dist/lib/runtime/model-discovery-status.js +13 -5
  57. package/dist/lib/runtime/reset-credit-routing.js +4 -1
  58. package/dist/lib/runtime/reset-credits.d.ts +7 -7
  59. package/dist/lib/runtime/resume-picker.js +1 -1
  60. package/dist/lib/runtime/rotation-server-types.d.ts +2 -0
  61. package/dist/lib/runtime/runtime-observability.d.ts +2 -0
  62. package/dist/lib/runtime/runtime-observability.js +1 -0
  63. package/dist/lib/runtime/workspace-model-scopes.d.ts +3 -3
  64. package/dist/lib/runtime-rotation-proxy.js +200 -32
  65. package/dist/lib/schemas.d.ts +65 -65
  66. package/dist/lib/ui/index.d.ts +1 -1
  67. package/dist/lib/ui/index.js +1 -1
  68. package/dist/lib/ui/runtime.d.ts +3 -1
  69. package/dist/lib/ui/runtime.js +11 -1
  70. package/dist/lib/ui/theme.d.ts +20 -1
  71. package/dist/lib/ui/theme.js +154 -30
  72. package/dist/lib/ui/ui-copy.d.ts +3 -2
  73. package/dist/lib/ui/ui-copy.js +3 -2
  74. package/dist/lib/usage/pricing.d.ts +1 -1
  75. package/dist/lib/usage/pricing.js +4 -0
  76. package/dist/lib/usage/usage-extraction.d.ts +2 -0
  77. package/dist/lib/usage/usage-extraction.js +25 -1
  78. package/dist/lib/utils.d.ts +1 -1
  79. package/dist/lib/utils.js +1 -1
  80. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  81. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  82. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  83. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  84. package/node_modules/@codex-ai/plugin/package.json +9 -9
  85. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
  86. package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
  87. package/node_modules/@codex-ai/sdk/package.json +8 -8
  88. package/package.json +207 -206
  89. package/scripts/codex-app-launcher.js +653 -653
  90. package/scripts/codex-app-router.js +374 -373
  91. package/scripts/codex-bin-resolver.js +332 -332
  92. package/scripts/codex-multi-auth.js +65 -65
  93. package/scripts/codex-routing.js +62 -62
  94. package/scripts/codex.js +7006 -7005
  95. package/scripts/install-codex-auth-utils.js +213 -213
  96. package/scripts/mcodex.js +367 -367
  97. package/scripts/postinstall.js +114 -114
  98. package/scripts/preuninstall.js +265 -265
  99. package/skills/codex-auth-setup/SKILL.md +73 -73
  100. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  101. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  102. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  103. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  104. package/vendor/codex-ai-plugin/package.json +9 -9
  105. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  106. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  107. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -0,0 +1,68 @@
1
+ import { AccountManager } from "../accounts.js";
2
+ import { LUNA_RESERVE_MODEL } from "../constants.js";
3
+ import { parseLunaReserveRateLimits, } from "../luna-reserve.js";
4
+ import { codexCliAccountIdFor } from "../auth/token-utils.js";
5
+ import { isRecord } from "../utils.js";
6
+ import { workspaceModelScopes } from "./workspace-model-scopes.js";
7
+ import { nativeRateLimitsRpc } from "./native-rate-limits.js";
8
+ import { ensureFreshAccessToken } from "./rotation-token-refresh.js";
9
+ /**
10
+ * Read Luna Reserve usage through the same isolated native app-server bridge
11
+ * used for reset credits. Failures are per-account: one stale/revoked account
12
+ * never prevents other rows from reporting their Reserve percentage.
13
+ */
14
+ export async function refreshLunaReserveUsage(storage, now = Date.now) {
15
+ const manager = new AccountManager(undefined, storage);
16
+ const result = {};
17
+ try {
18
+ for (let index = 0; index < storage.accounts.length; index += 1) {
19
+ const stored = storage.accounts[index];
20
+ const account = manager.getAccountByIndex(index);
21
+ if (!stored || !account || account.enabled === false)
22
+ continue;
23
+ const scopes = workspaceModelScopes(account);
24
+ const targetScope = account.workspaces?.length
25
+ ? scopes.find((scope) => scope.selected && scope.routable)
26
+ : scopes.find((scope) => scope.bound && scope.routable);
27
+ if (!targetScope)
28
+ continue;
29
+ try {
30
+ const fresh = await ensureFreshAccessToken({
31
+ accountManager: manager,
32
+ account,
33
+ family: "gpt-5.2",
34
+ model: LUNA_RESERVE_MODEL,
35
+ now: now(),
36
+ tokenRefreshSkewMs: 60_000,
37
+ tokenInvalidationCooldownMs: 300_000,
38
+ });
39
+ if (!fresh.ok)
40
+ continue;
41
+ const auth = {
42
+ accessToken: fresh.accessToken,
43
+ accountId: targetScope.accountId,
44
+ expiresAt: fresh.account.expires ?? 0,
45
+ codexCliMirror: account.codexCliMirror,
46
+ };
47
+ const sentAccountId = codexCliAccountIdFor(auth, fresh.accessToken) ?? targetScope.accountId;
48
+ const reply = await nativeRateLimitsRpc(auth, "account/rateLimits/read", {
49
+ supportsLunaReserve: true,
50
+ excludeResetCreditDetails: true,
51
+ });
52
+ if (isRecord(reply) &&
53
+ typeof reply.accountId === "string" &&
54
+ reply.accountId !== sentAccountId) {
55
+ continue;
56
+ }
57
+ result[index] = parseLunaReserveRateLimits(reply, now());
58
+ }
59
+ catch {
60
+ // Best-effort usage reporting; callers still emit normal quota.
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ finally {
66
+ await manager.flushPendingSave();
67
+ }
68
+ }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
- import { type AccountStorageV3 } from "../storage.js";
3
- import { type ApiRouteCredential, updateApiModelDiscovery } from "../api-route-store.js";
2
+ import { type AccountStorageV3, loadAccounts } from "../storage.js";
3
+ import { type ApiRouteCredential, loadApiRoutes, updateApiModelDiscovery } from "../api-route-store.js";
4
4
  declare const schema: z.ZodObject<{
5
5
  apiConfigurationUnavailable: z.ZodOptional<z.ZodBoolean>;
6
6
  version: z.ZodLiteral<1>;
@@ -23,10 +23,10 @@ declare const schema: z.ZodObject<{
23
23
  model: z.ZodString;
24
24
  checkedAt: z.ZodOptional<z.ZodNumber>;
25
25
  probes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
26
+ downgraded: "downgraded";
26
27
  unsupported: "unsupported";
27
- verified: "verified";
28
28
  unverified: "unverified";
29
- downgraded: "downgraded";
29
+ verified: "verified";
30
30
  }>>>;
31
31
  serviceTiers: z.ZodArray<z.ZodObject<{
32
32
  id: z.ZodString;
@@ -38,8 +38,8 @@ declare const schema: z.ZodObject<{
38
38
  }, z.core.$strip>>;
39
39
  label: z.ZodString;
40
40
  kind: z.ZodEnum<{
41
- oauth: "oauth";
42
41
  api: "api";
42
+ oauth: "oauth";
43
43
  zdr: "zdr";
44
44
  }>;
45
45
  enabled: z.ZodBoolean;
@@ -51,10 +51,10 @@ declare const schema: z.ZodObject<{
51
51
  model: z.ZodString;
52
52
  checkedAt: z.ZodOptional<z.ZodNumber>;
53
53
  probes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
54
+ downgraded: "downgraded";
54
55
  unsupported: "unsupported";
55
- verified: "verified";
56
56
  unverified: "unverified";
57
- downgraded: "downgraded";
57
+ verified: "verified";
58
58
  }>>>;
59
59
  serviceTiers: z.ZodArray<z.ZodObject<{
60
60
  id: z.ZodString;
@@ -90,9 +90,13 @@ export declare function refreshAndPrintModelInventory(log: (message: string) =>
90
90
  }>;
91
91
  fetchImpl?: typeof fetch;
92
92
  loadInventory?: typeof loadModelInventory;
93
+ loadAccounts?: typeof loadAccounts;
94
+ loadApiRoutes?: typeof loadApiRoutes;
93
95
  now?: () => number;
94
96
  /** Only an explicit `check capabilities` bypasses the 15-minute probe cache. */
95
97
  forceProbes?: boolean;
98
+ /** Installed `codex --version` probe, used when the inventory stores no clientVersion. */
99
+ detectClientVersion?: () => Promise<string | null>;
96
100
  }): Promise<boolean>;
97
101
  /** Compare stable credential/workspace scopes, keeping the last successful baseline through outages. */
98
102
  export declare function withInventoryChanges(current: ModelInventory, previous: ModelInventory | null): ModelInventory;
@@ -15,6 +15,7 @@ import { loadApiRoutes, updateApiModelDiscovery, } from "../api-route-store.js";
15
15
  import { ApiModelRuntime, boundedJson } from "./api-model-runtime.js";
16
16
  import { AccountModelCatalog } from "./account-model-catalog.js";
17
17
  import { ensureFreshAccessToken } from "./rotation-token-refresh.js";
18
+ import { CLIENT_VERSION_PATTERN, detectInstalledCodexCliVersion, } from "../codex-cli/version.js";
18
19
  import { createCodexHeaders } from "../request/headers.js";
19
20
  import { CODEX_BASE_URL } from "../constants.js";
20
21
  import { getCodexMultiAuthDir } from "../runtime-paths.js";
@@ -103,7 +104,7 @@ export async function discoverModelInventory(storage, routes, options = {}) {
103
104
  throw Error("Identity unavailable");
104
105
  const url = new URL(`${CODEX_BASE_URL}/codex/models`);
105
106
  const version = options.clientVersion;
106
- if (version && /^[0-9A-Za-z.+_-]{1,80}$/.test(version))
107
+ if (version && CLIENT_VERSION_PATTERN.test(version))
107
108
  url.searchParams.set("client_version", version);
108
109
  const response = await fetcher(url, {
109
110
  headers: createCodexHeaders({
@@ -250,6 +251,11 @@ export async function refreshAndPrintModelInventory(log, deps = {}) {
250
251
  try {
251
252
  const load = deps.loadInventory ?? loadModelInventory;
252
253
  const previous = await load();
254
+ // The catalog requires client_version (issue #733): the stored inventory
255
+ // version wins, otherwise probe the installed `codex --version` so a fresh
256
+ // inventory seeds one and saves it for the next check.
257
+ const detectClientVersion = async () => (await (deps.detectClientVersion ?? detectInstalledCodexCliVersion)().catch(() => null)) ?? undefined;
258
+ const clientVersion = previous?.clientVersion;
253
259
  const binding = await (deps.getBinding ?? getAppBindStatus)();
254
260
  if (binding.running && binding.state?.nativeOpenai) {
255
261
  const started = (deps.now ?? Date.now)();
@@ -266,8 +272,9 @@ export async function refreshAndPrintModelInventory(log, deps = {}) {
266
272
  url.searchParams.set("refresh_capabilities", "1");
267
273
  if (deps.forceProbes)
268
274
  url.searchParams.set("force_probes", "1");
269
- if (previous?.clientVersion)
270
- url.searchParams.set("client_version", previous.clientVersion);
275
+ const proxyClientVersion = clientVersion ?? (await detectClientVersion());
276
+ if (proxyClientVersion)
277
+ url.searchParams.set("client_version", proxyClientVersion);
271
278
  const clientApiKey = binding.state.clientApiKey;
272
279
  const current = await withCheckProgress("Refreshing workspace catalogs and API/ZDR probes", async () => {
273
280
  const response = await (deps.fetchImpl ?? fetch)(url, {
@@ -294,8 +301,9 @@ export async function refreshAndPrintModelInventory(log, deps = {}) {
294
301
  }
295
302
  const value = await withCheckProgress("Discovering workspace models and API/ZDR capabilities", async () => {
296
303
  let apiConfigurationUnavailable = false;
297
- const value = await discoverModelInventory(await loadAccounts(), await loadApiRoutes().catch(() => { apiConfigurationUnavailable = true; log("API configuration unavailable; checking subscription models only."); return []; }), {
298
- clientVersion: previous?.clientVersion,
304
+ const value = await discoverModelInventory(await (deps.loadAccounts ?? loadAccounts)(), await (deps.loadApiRoutes ?? loadApiRoutes)().catch(() => { apiConfigurationUnavailable = true; log("API configuration unavailable; checking subscription models only."); return []; }), {
305
+ clientVersion: clientVersion ?? (await detectClientVersion()),
306
+ fetchImpl: deps.fetchImpl,
299
307
  updateApiDiscovery: updateApiModelDiscovery,
300
308
  forceProbes: deps.forceProbes === true,
301
309
  persistProbes: true,
@@ -1,9 +1,10 @@
1
1
  import { extractAccountId } from '../accounts.js';
2
+ import { LUNA_RESERVE_MODEL } from '../constants.js';
2
3
  import { parseModelRoute } from '../model-route-policy.js';
3
4
  import { subscriptionQuotaPreference } from './subscription-quota-order.js';
4
5
  import { resetSnapshotQuota } from './reset-credits.js';
5
6
  export async function recoverResetQuota(options) {
6
- if (!options.native || options.pinned || parseModelRoute(options.model).kind !== 'oauth')
7
+ if (options.model === LUNA_RESERVE_MODEL || !options.native || options.pinned || parseModelRoute(options.model).kind !== 'oauth')
7
8
  return false;
8
9
  const family = options.family ?? 'codex';
9
10
  const candidates = [...options.scopes].flatMap(([index, scopes]) => { const account = options.manager.getAccountByIndex(index); return account && !account.authInvalidatedAt ? scopes.map(scope => ({ account, scope })) : []; });
@@ -50,6 +51,8 @@ export async function recoverResetQuota(options) {
50
51
  }
51
52
  /** Apply a confirmed earned reset once per workspace/model observation. */
52
53
  export function applyConfirmedReset(options) {
54
+ if (options.model === LUNA_RESERVE_MODEL)
55
+ return false;
53
56
  const s = options.snapshot;
54
57
  if (!s || options.lastRedemptionAt === undefined || s.updatedAt < options.lastRedemptionAt || s.ordinaryUsageAllowed !== true || s.updatedAt <= (options.previous?.updatedAt ?? 0) || options.now - s.updatedAt > 60000 || s.updatedAt > options.now)
55
58
  return false;
@@ -24,8 +24,8 @@ export type ResetSnapshot = z.infer<typeof snapshotSchema>;
24
24
  declare const stateSchema: z.ZodObject<{
25
25
  version: z.ZodLiteral<1>;
26
26
  policy: z.ZodDefault<z.ZodEnum<{
27
- manual: "manual";
28
27
  "last-resort": "last-resort";
28
+ manual: "manual";
29
29
  }>>;
30
30
  snapshots: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
31
31
  updatedAt: z.ZodNumber;
@@ -53,10 +53,10 @@ declare const stateSchema: z.ZodObject<{
53
53
  key: z.ZodString;
54
54
  at: z.ZodNumber;
55
55
  outcome: z.ZodEnum<{
56
- reset: "reset";
57
- nothingToReset: "nothingToReset";
58
- noCredit: "noCredit";
59
56
  alreadyRedeemed: "alreadyRedeemed";
57
+ noCredit: "noCredit";
58
+ nothingToReset: "nothingToReset";
59
+ reset: "reset";
60
60
  }>;
61
61
  automatic: z.ZodBoolean;
62
62
  }, z.core.$strip>>;
@@ -64,10 +64,10 @@ declare const stateSchema: z.ZodObject<{
64
64
  type State = z.infer<typeof stateSchema>;
65
65
  declare const outcomeSchema: z.ZodObject<{
66
66
  outcome: z.ZodEnum<{
67
- reset: "reset";
68
- nothingToReset: "nothingToReset";
69
- noCredit: "noCredit";
70
67
  alreadyRedeemed: "alreadyRedeemed";
68
+ noCredit: "noCredit";
69
+ nothingToReset: "nothingToReset";
70
+ reset: "reset";
71
71
  }>;
72
72
  }, z.core.$strip>;
73
73
  export type ResetOutcome = z.infer<typeof outcomeSchema>['outcome'];
@@ -104,7 +104,7 @@ function displayText(value) {
104
104
  // Session titles and directory names are untrusted terminal output.
105
105
  return String(value).replace(/[\x00-\x1f\x7f-\x9f]/g, " ").slice(0, 180);
106
106
  }
107
- export async function pickResumeThread(options, deps = { createCatalog: createResumeCatalog, select: (select) }) {
107
+ export async function pickResumeThread(options, deps = { createCatalog: createResumeCatalog, select: select }) {
108
108
  const catalog = deps.createCatalog(options);
109
109
  const pages = [];
110
110
  let pageIndex = 0;
@@ -26,6 +26,8 @@ export interface RuntimeRotationProxyStatus {
26
26
  totalRequests: number;
27
27
  upstreamRequests: number;
28
28
  retries: number;
29
+ /** Number of ordinary GPT-6 Luna requests retried through Luna Reserve. */
30
+ lunaReserveFallbacks?: number;
29
31
  rotations: number;
30
32
  streamsStarted: number;
31
33
  lastError: string | null;
@@ -35,6 +35,8 @@ export interface RuntimeObservabilitySnapshot {
35
35
  responsesRequests: number;
36
36
  authRefreshRequests: number;
37
37
  diagnosticProbeRequests: number;
38
+ /** Number of ordinary Luna requests that switched to the separately metered Reserve model. */
39
+ lunaReserveFallbacks: number;
38
40
  poolExhaustionCooldownUntil: number | null;
39
41
  serverBurstCooldownUntil: number | null;
40
42
  lastAccountIndex?: number | null;
@@ -27,6 +27,7 @@ function createDefaultSnapshot() {
27
27
  responsesRequests: 0,
28
28
  authRefreshRequests: 0,
29
29
  diagnosticProbeRequests: 0,
30
+ lunaReserveFallbacks: 0,
30
31
  poolExhaustionCooldownUntil: null,
31
32
  serverBurstCooldownUntil: null,
32
33
  lastAccountIndex: null,
@@ -2,13 +2,13 @@ import { type ManagedAccount } from "../accounts.js";
2
2
  export declare function modelScopeId(...parts: string[]): string;
3
3
  /** Request scopes are immutable views. Discovery must never switch a saved account. */
4
4
  export declare function workspaceModelScopes(account: ManagedAccount): {
5
+ accountId: string;
6
+ workspaceIndex: number;
7
+ enabled: boolean;
5
8
  id: string;
6
9
  accountIndex: number;
7
10
  routable: boolean;
8
11
  bound: boolean;
9
12
  selected: boolean;
10
13
  label: string;
11
- accountId: string;
12
- workspaceIndex: number;
13
- enabled: boolean;
14
14
  }[];