oc-codex-multi-auth 6.14.3 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -293,6 +293,52 @@ Primary config files:
293
293
  - `~/.config/opencode/tui.json`
294
294
  - `~/.opencode/openai-codex-auth-config.json`
295
295
 
296
+ ### Desktop quota notifications
297
+
298
+ Quota notifications are an optional macOS-only feature. While the plugin is
299
+ running, it checks all enabled accounts and alerts through Notification Center
300
+ when the best remaining 5-hour or weekly pool quota crosses 25%, 10%, or 0%.
301
+ The feature is disabled by default.
302
+
303
+ Each line reports the enabled account with the most headroom in that window,
304
+ together with that same account's reset time, so the pair always describes a
305
+ quota that one account actually has. Windows a plan has switched off are
306
+ skipped rather than counted as full. Account identities are omitted for
307
+ readability and lock-screen privacy:
308
+
309
+ ```text
310
+ 5h: 10% | resets 22:30
311
+ Weekly: 72% | resets 22:30 on Aug 30
312
+ ```
313
+
314
+ ```json
315
+ {
316
+ "quotaNotifications": {
317
+ "enabled": true,
318
+ "intervalMs": 1800000,
319
+ "notifyEveryCheck": false,
320
+ "thresholds": [25, 10, 0]
321
+ }
322
+ }
323
+ ```
324
+
325
+ Add the object above to `~/.opencode/openai-codex-auth-config.json`, or set
326
+ `CODEX_AUTH_QUOTA_NOTIFICATIONS=1`, then quit and restart OpenCode. The minimum
327
+ interval is 30 seconds. If macOS blocks the alert, allow notifications for
328
+ the process shown in **System Settings > Notifications**. The setting is
329
+ ignored on Windows and Linux.
330
+
331
+ Set `"notifyEveryCheck": true` to show the aggregate quota notification after
332
+ every successful poll interval instead of only when a configured threshold is
333
+ crossed. Set `"thresholds": []` to turn threshold alerts off entirely; pair it
334
+ with `"notifyEveryCheck": true` or the monitor has nothing to deliver and stops
335
+ polling.
336
+
337
+ Delivery state lives beside the accounts file the alerts are computed from, so
338
+ OpenCode processes working in the same account scope show only one alert per
339
+ interval. With the default `perProjectAccounts`, that scope is one project:
340
+ two projects have separate account pools and therefore alert independently.
341
+
296
342
  ### Route models to preferred accounts
297
343
 
298
344
  Use `modelAccountPools` to assign one or more preferred ChatGPT accounts or Business seats to a
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsU/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAq8H/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAuU/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAq9H/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
36
36
  import { getCodexMode, getRequestTransformMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRetryProfile, getRetryBudgetOverrides, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getAutoUpdate, getToastDurationMs, getAccountToastsEnabled, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getRotationStrategy, getModelAccountPool, getModelAccountPoolMode, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, getBeginnerSafeMode, getCodexTuiMaskEmail, loadPluginConfig, } from "./lib/config.js";
37
37
  import { AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, ACCOUNT_LIMITS, } from "./lib/constants.js";
38
38
  import { initLogger, logRequest, logDebug, logInfo, logWarn, logError, setCorrelationId, clearCorrelationId, } from "./lib/logger.js";
39
+ import { createQuotaMonitor } from "./lib/quota-notifications.js";
39
40
  import { checkAndNotify } from "./lib/auto-update-checker.js";
40
41
  import { handleContextOverflow } from "./lib/context-overflow.js";
41
42
  import { AccountManager, extractAccountEmail, extractAccountId, formatAccountLabel, formatWaitTime, sanitizeEmail, shouldUpdateAccountIdFromToken, resolveRequestAccountId, parseRateLimitReason, lookupCodexCliTokensByEmail, } from "./lib/accounts.js";
@@ -314,7 +315,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
314
315
  return "No authorization code found. Paste the full callback URL (e.g., http://localhost:1455/auth/callback?code=...). If browser callback keeps failing, retry with Device Code.";
315
316
  }
316
317
  if (!parsed.state) {
317
- return "Missing OAuth state. Paste the full callback URL including both code and state parameters. If needed, retry with Device Code.";
318
+ return parsed.source === "raw"
319
+ ? "That is a bare authorization code. This flow needs the full callback URL, including the state parameter (e.g., http://localhost:1455/auth/callback?code=...&state=...). If needed, retry with Device Code."
320
+ : "Missing OAuth state. Paste the full callback URL including both code and state parameters. If needed, retry with Device Code.";
318
321
  }
319
322
  if (parsed.state !== expectedState) {
320
323
  return "OAuth state mismatch. Restart login and paste the callback URL generated for this login attempt, or retry with Device Code.";
@@ -1126,10 +1129,22 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1126
1129
  });
1127
1130
  }
1128
1131
  };
1132
+ // Created here, after `invalidateAccountManagerCache`, because the
1133
+ // monitor refreshes access tokens unattended. A refresh rotates the
1134
+ // single-use refresh token durably on disk, so the cached
1135
+ // AccountManager holding the old one must be dropped or its debounced
1136
+ // save clobbers the rotation (see `reloadCachedAccountManager` above).
1137
+ const quotaMonitor = createQuotaMonitor({
1138
+ onCredentialsPersisted: invalidateAccountManagerCache,
1139
+ });
1129
1140
  // Event handler for session recovery and account selection
1130
1141
  const eventHandler = async (input) => {
1131
1142
  try {
1132
1143
  const { event } = input;
1144
+ if (event.type === "server.instance.disposed") {
1145
+ quotaMonitor.dispose();
1146
+ return;
1147
+ }
1133
1148
  // Handle TUI account selection events
1134
1149
  // Accepts generic selection events with an index property
1135
1150
  if (event.type === "account.select" ||
@@ -1236,6 +1251,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1236
1251
  const startupPerProjectAccounts = getPerProjectAccounts(startupPluginConfig);
1237
1252
  setStoragePath(startupPerProjectAccounts ? process.cwd() : null);
1238
1253
  await backfillHostOpenAIAuthFromPool();
1254
+ quotaMonitor.start();
1239
1255
  return {
1240
1256
  event: eventHandler,
1241
1257
  auth: {