codex-multi-auth 2.16.0 → 2.17.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/.codex-plugin/plugin.json +1 -1
- package/README.md +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/lib/account-policy.d.ts +4 -0
- package/dist/lib/account-policy.d.ts.map +1 -1
- package/dist/lib/account-policy.js +8 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +2 -0
- package/dist/lib/accounts.d.ts +21 -3
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +229 -17
- package/dist/lib/api-route-store.d.ts +24 -0
- package/dist/lib/api-route-store.d.ts.map +1 -0
- package/dist/lib/api-route-store.js +145 -0
- package/dist/lib/auth/account-access.d.ts +115 -0
- package/dist/lib/auth/account-access.d.ts.map +1 -0
- package/dist/lib/auth/account-access.js +214 -0
- package/dist/lib/auth/token-utils.d.ts +54 -1
- package/dist/lib/auth/token-utils.d.ts.map +1 -1
- package/dist/lib/auth/token-utils.js +92 -7
- package/dist/lib/cli.d.ts +1 -1
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +4 -0
- package/dist/lib/codex-cli/state.d.ts +7 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -1
- package/dist/lib/codex-cli/state.js +18 -0
- package/dist/lib/codex-cli/writer.d.ts.map +1 -1
- package/dist/lib/codex-cli/writer.js +60 -8
- package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-manager-commands.js +1 -0
- package/dist/lib/codex-manager/account-pool-write.d.ts +3 -1
- package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-pool-write.js +4 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
- package/dist/lib/codex-manager/api-login-menu.js +224 -0
- package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/account.js +27 -0
- package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/best.js +6 -2
- package/dist/lib/codex-manager/commands/check.d.ts +7 -1
- package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/check.js +24 -3
- package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/forecast.js +5 -1
- package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/report.js +5 -1
- package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
- package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/resets.js +67 -0
- package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
- package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/rotation.js +63 -12
- package/dist/lib/codex-manager/commands/status.d.ts +12 -1
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/status.js +166 -22
- package/dist/lib/codex-manager/commands/uninstall.js +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
- package/dist/lib/codex-manager/health-check.d.ts +3 -0
- package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
- package/dist/lib/codex-manager/health-check.js +56 -6
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +4 -2
- package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-flow.js +69 -7
- package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-menu-data.js +14 -5
- package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
- package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-oauth.js +6 -1
- package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
- package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
- package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
- package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
- package/dist/lib/codex-manager/persist-selected-account.js +2 -1
- package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
- package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/repair-commands.js +143 -8
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +25 -6
- package/dist/lib/concurrency.d.ts +3 -0
- package/dist/lib/concurrency.d.ts.map +1 -0
- package/dist/lib/concurrency.js +31 -0
- package/dist/lib/model-route-policy.d.ts +52 -0
- package/dist/lib/model-route-policy.d.ts.map +1 -0
- package/dist/lib/model-route-policy.js +159 -0
- package/dist/lib/policy/runtime-policy.d.ts +1 -0
- package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
- package/dist/lib/policy/runtime-policy.js +3 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
- package/dist/lib/preemptive-quota-scheduler.js +4 -0
- package/dist/lib/quota-probe.d.ts +5 -0
- package/dist/lib/quota-probe.d.ts.map +1 -1
- package/dist/lib/quota-probe.js +26 -2
- package/dist/lib/request/client-cancellation.d.ts +5 -0
- package/dist/lib/request/client-cancellation.d.ts.map +1 -0
- package/dist/lib/request/client-cancellation.js +7 -0
- package/dist/lib/request/response-outcome.d.ts +25 -0
- package/dist/lib/request/response-outcome.d.ts.map +1 -0
- package/dist/lib/request/response-outcome.js +49 -0
- package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
- package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
- package/dist/lib/request/stream-failover-runtime.js +29 -10
- package/dist/lib/runtime/account-check.d.ts.map +1 -1
- package/dist/lib/runtime/account-check.js +2 -8
- package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
- package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
- package/dist/lib/runtime/account-model-catalog.js +154 -0
- package/dist/lib/runtime/account-pool.d.ts +3 -1
- package/dist/lib/runtime/account-pool.d.ts.map +1 -1
- package/dist/lib/runtime/account-pool.js +4 -0
- package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
- package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/account-reset-credits.js +82 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-capabilities.js +434 -0
- package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
- package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-runtime.js +358 -0
- package/dist/lib/runtime/app-bind.d.ts +19 -1
- package/dist/lib/runtime/app-bind.d.ts.map +1 -1
- package/dist/lib/runtime/app-bind.js +79 -9
- package/dist/lib/runtime/automatic-account-checks.d.ts +31 -0
- package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-account-checks.js +113 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-subscription-checks.js +57 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/catalog-capabilities.js +61 -0
- package/dist/lib/runtime/inference-activity.d.ts +15 -0
- package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
- package/dist/lib/runtime/inference-activity.js +95 -0
- package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
- package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
- package/dist/lib/runtime/model-discovery-status.js +387 -0
- package/dist/lib/runtime/native-account-storage.d.ts +11 -0
- package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-storage.js +50 -0
- package/dist/lib/runtime/native-account-sync.d.ts +19 -0
- package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-sync.js +62 -0
- package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
- package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
- package/dist/lib/runtime/native-binding-lock.js +5 -0
- package/dist/lib/runtime/native-client-auth.d.ts +3 -0
- package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
- package/dist/lib/runtime/native-client-auth.js +42 -0
- package/dist/lib/runtime/native-provider-config.d.ts +4 -0
- package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
- package/dist/lib/runtime/native-provider-config.js +52 -0
- package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
- package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
- package/dist/lib/runtime/native-rate-limits.js +135 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credit-routing.js +66 -0
- package/dist/lib/runtime/reset-credits.d.ts +101 -0
- package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credits.js +162 -0
- package/dist/lib/runtime/response-output-history.d.ts +17 -0
- package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
- package/dist/lib/runtime/response-output-history.js +85 -0
- package/dist/lib/runtime/responses-websocket.d.ts +31 -0
- package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
- package/dist/lib/runtime/responses-websocket.js +636 -0
- package/dist/lib/runtime/resume-picker.d.ts +41 -0
- package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
- package/dist/lib/runtime/resume-picker.js +169 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-account-selection.js +63 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
- package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
- package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-capability-failures.js +76 -0
- package/dist/lib/runtime/runtime-observability.d.ts +7 -0
- package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-observability.js +42 -2
- package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
- package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-first-use.js +64 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-event.js +31 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-order.js +31 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
- package/dist/lib/runtime/workspace-model-scopes.js +34 -0
- package/dist/lib/runtime-constants.d.ts +3 -0
- package/dist/lib/runtime-constants.d.ts.map +1 -1
- package/dist/lib/runtime-constants.js +5 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -1
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.js +852 -42
- package/dist/lib/schemas.d.ts +20 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/storage/credential-sidecars.d.ts +13 -0
- package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
- package/dist/lib/storage/credential-sidecars.js +52 -0
- package/dist/lib/storage/file-lock.d.ts +9 -0
- package/dist/lib/storage/file-lock.d.ts.map +1 -0
- package/dist/lib/storage/file-lock.js +186 -0
- package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
- package/dist/lib/storage/flagged-storage.js +12 -0
- package/dist/lib/storage/path-state.d.ts +2 -0
- package/dist/lib/storage/path-state.d.ts.map +1 -1
- package/dist/lib/storage/path-state.js +9 -0
- package/dist/lib/storage/paths.js +4 -4
- package/dist/lib/storage/pending-auth.d.ts +17 -0
- package/dist/lib/storage/pending-auth.d.ts.map +1 -0
- package/dist/lib/storage/pending-auth.js +153 -0
- package/dist/lib/storage/public-types.d.ts +13 -0
- package/dist/lib/storage/public-types.d.ts.map +1 -1
- package/dist/lib/storage/snapshot-merge.d.ts +16 -0
- package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
- package/dist/lib/storage/snapshot-merge.js +206 -0
- package/dist/lib/storage/transactions.d.ts.map +1 -1
- package/dist/lib/storage/transactions.js +5 -4
- package/dist/lib/storage.d.ts +6 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +77 -7
- package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
- package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu-builder.js +1 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +1 -0
- package/dist/lib/ui/check-progress.d.ts +8 -0
- package/dist/lib/ui/check-progress.d.ts.map +1 -0
- package/dist/lib/ui/check-progress.js +30 -0
- package/dist/lib/ui/format.d.ts +3 -1
- package/dist/lib/ui/format.d.ts.map +1 -1
- package/dist/lib/ui/format.js +8 -0
- package/dist/lib/ui/ui-copy.d.ts +2 -2
- package/dist/lib/ui/ui-copy.js +2 -2
- package/dist/lib/usage/pricing.js +2 -2
- package/dist/lib/usage/usage-extraction.d.ts +3 -1
- package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
- package/dist/lib/usage/usage-extraction.js +46 -9
- package/package.json +7 -4
- package/scripts/codex-app-router.js +7 -0
- package/scripts/codex-bin-resolver.js +40 -8
- package/scripts/codex-routing.js +1 -0
- package/scripts/codex.js +100 -22
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Workspace } from "../accounts.js";
|
|
2
2
|
import type { ModelFamily } from "../prompts/codex.js";
|
|
3
|
-
import type { AccountStorageV3 } from "../storage.js";
|
|
3
|
+
import type { AccountMetadataV3, AccountStorageV3 } from "../storage.js";
|
|
4
4
|
import type { AccountIdSource, TokenResult } from "../types.js";
|
|
5
5
|
export type TokenSuccessWithAccount = Extract<TokenResult, {
|
|
6
6
|
type: "success";
|
|
@@ -9,6 +9,8 @@ export type TokenSuccessWithAccount = Extract<TokenResult, {
|
|
|
9
9
|
accountIdSource?: AccountIdSource;
|
|
10
10
|
accountLabel?: string;
|
|
11
11
|
workspaces?: Workspace[];
|
|
12
|
+
/** Set, keep (undefined) or clear (null) the saved CodexCliMirror. */
|
|
13
|
+
codexCliMirror?: AccountMetadataV3["codexCliMirror"] | null;
|
|
12
14
|
};
|
|
13
15
|
export declare function persistAccountPoolResults(params: {
|
|
14
16
|
results: TokenSuccessWithAccount[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-pool.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"account-pool.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAC5C,WAAW,EACX;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CACnB,GAAG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,sEAAsE;IACtE,cAAc,CAAC,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;CAC5D,CAAC;AAEF,wBAAsB,yBAAyB,CAAC,MAAM,EAAE;IACvD,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,6BAA6B,EAAE,CAAC,CAAC,EAChC,OAAO,EAAE,CACR,aAAa,EAAE,gBAAgB,GAAG,IAAI,EACtC,OAAO,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,KACjD,OAAO,CAAC,CAAC,CAAC,KACX,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,wBAAwB,EAAE,cAAc,eAAe,EAAE,wBAAwB,CAAC;IAClF,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC9D,mBAAmB,EAAE,CACpB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,KACZ,MAAM,GAAG,SAAS,CAAC;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;CACjE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiLhB"}
|
|
@@ -48,6 +48,7 @@ export async function persistAccountPoolResults(params) {
|
|
|
48
48
|
lastUsed: now,
|
|
49
49
|
workspaces: result.workspaces,
|
|
50
50
|
currentWorkspaceIndex: initialWorkspaceIndex,
|
|
51
|
+
...(result.codexCliMirror ? { codexCliMirror: result.codexCliMirror } : {}),
|
|
51
52
|
});
|
|
52
53
|
continue;
|
|
53
54
|
}
|
|
@@ -105,6 +106,9 @@ export async function persistAccountPoolResults(params) {
|
|
|
105
106
|
lastUsed: now,
|
|
106
107
|
workspaces: mergedWorkspaces,
|
|
107
108
|
currentWorkspaceIndex: nextCurrentWorkspaceIndex,
|
|
109
|
+
codexCliMirror: result.codexCliMirror === null
|
|
110
|
+
? undefined
|
|
111
|
+
: (result.codexCliMirror ?? existing.codexCliMirror),
|
|
108
112
|
};
|
|
109
113
|
}
|
|
110
114
|
if (accounts.length === 0)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AccountManager } from '../accounts.js';
|
|
2
|
+
import { type AccountMetadataV3 } from '../storage.js';
|
|
3
|
+
import { ResetCreditService, type ResetTarget } from './reset-credits.js';
|
|
4
|
+
export declare function resetTargetForStoredAccount(account: AccountMetadataV3): ResetTarget | null;
|
|
5
|
+
export declare function isResetTargetEnabled(account: AccountMetadataV3, target: ResetTarget): boolean;
|
|
6
|
+
export declare function createResetCreditService(manager?: AccountManager): ResetCreditService;
|
|
7
|
+
export declare function loadResetCreditState(): Promise<{
|
|
8
|
+
version: 1;
|
|
9
|
+
policy: "manual" | "last-resort";
|
|
10
|
+
snapshots: Record<string, {
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
availableCount: number | null;
|
|
13
|
+
ordinaryUsageAllowed: boolean | null;
|
|
14
|
+
planType: string | null;
|
|
15
|
+
primary: {
|
|
16
|
+
usedPercent?: number | undefined;
|
|
17
|
+
resetsAt?: number | null | undefined;
|
|
18
|
+
windowDurationMins?: number | null | undefined;
|
|
19
|
+
};
|
|
20
|
+
secondary: {
|
|
21
|
+
usedPercent?: number | undefined;
|
|
22
|
+
resetsAt?: number | null | undefined;
|
|
23
|
+
windowDurationMins?: number | null | undefined;
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
pending?: {
|
|
27
|
+
key: string;
|
|
28
|
+
idempotencyKey: string;
|
|
29
|
+
} | undefined;
|
|
30
|
+
lastAutomaticCheckAt?: number | undefined;
|
|
31
|
+
lastRedemptionAt?: number | undefined;
|
|
32
|
+
lastRedemption?: {
|
|
33
|
+
key: string;
|
|
34
|
+
at: number;
|
|
35
|
+
outcome: "reset" | "nothingToReset" | "noCredit" | "alreadyRedeemed";
|
|
36
|
+
automatic: boolean;
|
|
37
|
+
} | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
/** Local terminal labels only; never used in provider requests or public artifacts. */
|
|
40
|
+
export declare function resetAccountLabel(account: Pick<AccountMetadataV3, "email">, index: number): string;
|
|
41
|
+
export declare function refreshAndPrintResetCredits(log: (message: string) => void): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=account-reset-credits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-reset-credits.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-reset-credits.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAA2D,MAAM,gBAAgB,CAAC;AACzG,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAOrE,OAAO,EAAE,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAG1E,wBAAgB,2BAA2B,CAAC,OAAO,EAAC,iBAAiB,GAAE,WAAW,GAAC,IAAI,CAOtF;AACD,wBAAgB,oBAAoB,CAAC,OAAO,EAAC,iBAAiB,EAAC,MAAM,EAAC,WAAW,GAAE,OAAO,CAMzF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAC,cAAc,GAAE,kBAAkB,CA2BnF;AACD,wBAAsB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAA+C;AACzF,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlG;AACD,wBAAsB,2BAA2B,CAAC,GAAG,EAAC,CAAC,OAAO,EAAC,MAAM,KAAG,IAAI,iBAQ3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { AccountManager, extractAccountId, resolveAccountRecordId, sanitizeEmail } from '../accounts.js';
|
|
3
|
+
import { loadAccounts } from '../storage.js';
|
|
4
|
+
import { getCodexMultiAuthDir } from '../runtime-paths.js';
|
|
5
|
+
import { ensureFreshAccessToken } from './rotation-token-refresh.js';
|
|
6
|
+
import { modelScopeId, workspaceModelScopes } from './workspace-model-scopes.js';
|
|
7
|
+
import { nativeRateLimitsRpc } from './native-rate-limits.js';
|
|
8
|
+
import { codexCliAccountIdFor } from '../auth/token-utils.js';
|
|
9
|
+
import { isRecord } from '../utils.js';
|
|
10
|
+
import { ResetCreditService } from './reset-credits.js';
|
|
11
|
+
import { withCheckProgress } from '../ui/check-progress.js';
|
|
12
|
+
export function resetTargetForStoredAccount(account) {
|
|
13
|
+
// Organization display aliases are not native ChatGPT workspace identities.
|
|
14
|
+
const stored = account.accountId?.trim();
|
|
15
|
+
const id = stored?.startsWith("org-") ? extractAccountId(account.accessToken)?.trim() : stored;
|
|
16
|
+
const workspace = account.workspaces?.find(w => w.id === id);
|
|
17
|
+
if (!id || id.startsWith("org-") || account.enabled === false || workspace?.enabled === false)
|
|
18
|
+
return null;
|
|
19
|
+
return { key: modelScopeId('oauth', resolveAccountRecordId(account), id), accountId: id };
|
|
20
|
+
}
|
|
21
|
+
export function isResetTargetEnabled(account, target) {
|
|
22
|
+
if (account.enabled === false)
|
|
23
|
+
return false;
|
|
24
|
+
const canonical = resetTargetForStoredAccount(account);
|
|
25
|
+
if (canonical?.key === target.key && canonical.accountId === target.accountId)
|
|
26
|
+
return true;
|
|
27
|
+
const scopeKey = modelScopeId('oauth', resolveAccountRecordId(account), target.accountId);
|
|
28
|
+
return !target.accountId.startsWith('org-') && scopeKey === target.key && Boolean(account.workspaces?.some(w => w.id === target.accountId && w.enabled !== false));
|
|
29
|
+
}
|
|
30
|
+
export function createResetCreditService(manager) {
|
|
31
|
+
const auth = async (target) => {
|
|
32
|
+
const account = manager?.getAccountsSnapshot().find(a => workspaceModelScopes(a).some(s => s.id === target.key && s.accountId === target.accountId && s.routable) || (resetTargetForStoredAccount({ ...a, accessToken: a.access })?.key === target.key && resetTargetForStoredAccount({ ...a, accessToken: a.access })?.accountId === target.accountId));
|
|
33
|
+
const current = account && manager?.getAccountByIndex(account.index);
|
|
34
|
+
if (!manager || !current || current.enabled === false)
|
|
35
|
+
throw Error('Reset workspace is not enabled');
|
|
36
|
+
const fresh = await ensureFreshAccessToken({ accountManager: manager, account: current, family: 'codex', model: null, now: Date.now(), tokenRefreshSkewMs: 60000, tokenInvalidationCooldownMs: 300000 });
|
|
37
|
+
if (!fresh.ok)
|
|
38
|
+
throw Error('Reset account authentication unavailable');
|
|
39
|
+
// The mirror carries the id Codex CLI accepts when the backend refused an explicit binding.
|
|
40
|
+
return { accessToken: fresh.accessToken, accountId: target.accountId, expiresAt: fresh.account.expires ?? 0, codexCliMirror: current.codexCliMirror };
|
|
41
|
+
};
|
|
42
|
+
return new ResetCreditService(join(getCodexMultiAuthDir(), 'reset-credits.json'), {
|
|
43
|
+
read: async (target) => {
|
|
44
|
+
// The backend answers for the id written into its auth.json (the mirror id when one
|
|
45
|
+
// applies). Verify the reply against that id; the snapshot stays keyed on the target.
|
|
46
|
+
const freshAuth = await auth(target);
|
|
47
|
+
const sent = codexCliAccountIdFor(freshAuth, freshAuth.accessToken) ?? freshAuth.accountId;
|
|
48
|
+
const reply = await nativeRateLimitsRpc(freshAuth, 'account/rateLimits/read', { excludeResetCreditDetails: true });
|
|
49
|
+
if (!isRecord(reply) || reply.accountId !== sent)
|
|
50
|
+
throw Error('Reset credit workspace identity mismatch');
|
|
51
|
+
return { ...reply, accountId: target.accountId };
|
|
52
|
+
},
|
|
53
|
+
consume: async (target, idempotencyKey) => {
|
|
54
|
+
const freshAuth = await auth(target);
|
|
55
|
+
const disk = await loadAccounts();
|
|
56
|
+
if (!disk?.accounts.some(a => isResetTargetEnabled(a, target)))
|
|
57
|
+
throw Error('Reset target was removed, disabled, or changed; no credit consumed');
|
|
58
|
+
return nativeRateLimitsRpc(freshAuth, 'account/rateLimitResetCredit/consume', { idempotencyKey });
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export async function loadResetCreditState() { return createResetCreditService().status(); }
|
|
63
|
+
/** Local terminal labels only; never used in provider requests or public artifacts. */
|
|
64
|
+
export function resetAccountLabel(account, index) {
|
|
65
|
+
const email = sanitizeEmail(account.email);
|
|
66
|
+
const printable = email && !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(email);
|
|
67
|
+
return `Account ${index + 1}${printable ? ` (${email})` : ""}`;
|
|
68
|
+
}
|
|
69
|
+
export async function refreshAndPrintResetCredits(log) {
|
|
70
|
+
const storage = await loadAccounts();
|
|
71
|
+
if (!storage?.accounts.length)
|
|
72
|
+
return;
|
|
73
|
+
const manager = new AccountManager(undefined, storage);
|
|
74
|
+
const targets = storage.accounts.map(resetTargetForStoredAccount).filter((t) => Boolean(t));
|
|
75
|
+
try {
|
|
76
|
+
const snapshots = await withCheckProgress(`Checking reset credits for ${targets.length} accounts`, () => createResetCreditService(manager).refresh(targets), log);
|
|
77
|
+
storage.accounts.forEach((a, i) => { const target = resetTargetForStoredAccount(a); const count = target ? snapshots[target.key]?.availableCount : undefined; log(`${resetAccountLabel(a, i)}: available subscription resets: ${count ?? 'unknown (not verified)'}`); });
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await manager.flushPendingSave();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ApiRouteCredential } from "../api-route-store.js";
|
|
2
|
+
import { type RouteModel } from "../model-route-policy.js";
|
|
3
|
+
/** Read the explicit model-specific statement, never surrounding examples or another model's settings. */
|
|
4
|
+
export declare function parseApiReasoningDocumentation(id: string, text: string): string[];
|
|
5
|
+
export type ProbeResult = {
|
|
6
|
+
at: number;
|
|
7
|
+
levels: string[];
|
|
8
|
+
tiers: string[];
|
|
9
|
+
status: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
export declare class ApiModelCapabilities {
|
|
12
|
+
private readonly fetchImpl;
|
|
13
|
+
private readonly now;
|
|
14
|
+
private activeProbes;
|
|
15
|
+
private readonly probeWaiters;
|
|
16
|
+
private withProbeSlot;
|
|
17
|
+
private readonly inFlight;
|
|
18
|
+
private readonly probes;
|
|
19
|
+
private readonly cache;
|
|
20
|
+
constructor(fetchImpl?: typeof fetch, now?: () => number);
|
|
21
|
+
/** Probe results keyed by an opaque hash, for a fresh CLI process to honour the 15-minute cache. */
|
|
22
|
+
exportProbes(): Array<[string, ProbeResult]>;
|
|
23
|
+
importProbes(entries: Array<[string, ProbeResult]>): void;
|
|
24
|
+
reasoning(id: string, refresh?: boolean): Promise<string[]>;
|
|
25
|
+
private documentation;
|
|
26
|
+
private probe;
|
|
27
|
+
private runProbe;
|
|
28
|
+
enrich(models: RouteModel[], refresh?: boolean, route?: ApiRouteCredential, forceProbes?: boolean): Promise<RouteModel[]>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=api-model-capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-model-capabilities.d.ts","sourceRoot":"","sources":["../../../lib/runtime/api-model-capabilities.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAEN,KAAK,UAAU,EACf,MAAM,0BAA0B,CAAC;AAYlC,0GAA0G;AAC1G,wBAAgB,8BAA8B,CAC7C,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACV,MAAM,EAAE,CAuBV;AACD,MAAM,MAAM,WAAW,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,CAAC;AAwCF,qBAAa,oBAAoB;IA2B/B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IA3BrB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;YACxC,aAAa;IAY3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2C;IACpE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAQnB;IACJ,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8D;gBAElE,SAAS,GAAE,OAAO,KAAa,EAC/B,GAAG,GAAE,MAAM,MAAiB;IAE9C,oGAAoG;IACpG,YAAY,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAG5C,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,IAAI;IAMnD,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAGjD,aAAa;YA4Cb,KAAK;YAyBL,QAAQ;IA8MhB,MAAM,CACX,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,kBAAkB,EAC1B,WAAW,UAAQ,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC;CAqDxB"}
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { mapWithConcurrency } from "../concurrency.js";
|
|
2
|
+
import { classifyCapabilityFailure } from "./runtime-capability-failures.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { canonicalServiceTier, } from "../model-route-policy.js";
|
|
5
|
+
import { isRecord } from "../utils.js";
|
|
6
|
+
const efforts = new Set([
|
|
7
|
+
"none",
|
|
8
|
+
"minimal",
|
|
9
|
+
"low",
|
|
10
|
+
"medium",
|
|
11
|
+
"high",
|
|
12
|
+
"xhigh",
|
|
13
|
+
"max",
|
|
14
|
+
]);
|
|
15
|
+
/** Read the explicit model-specific statement, never surrounding examples or another model's settings. */
|
|
16
|
+
export function parseApiReasoningDocumentation(id, text) {
|
|
17
|
+
if (text.match(/^Model ID:\s*`([^`]+)`/m)?.[1] !== id)
|
|
18
|
+
return [];
|
|
19
|
+
const statement = text.match(/`?reasoning\.effort`?\s+supports\s*:?\s*([^\n.]+)\./i)?.[1];
|
|
20
|
+
if (!statement)
|
|
21
|
+
return [];
|
|
22
|
+
const values = statement
|
|
23
|
+
.replace(/`|\(default\)/g, " ")
|
|
24
|
+
.split(/,|\band\b/)
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
if (!values.length ||
|
|
28
|
+
values.length > 16 ||
|
|
29
|
+
values.some((v) => !/^[a-z][a-z0-9_-]{0,31}$/.test(v) ||
|
|
30
|
+
v === "ultra" ||
|
|
31
|
+
v === "persistent"))
|
|
32
|
+
return [];
|
|
33
|
+
return [...new Set(values)];
|
|
34
|
+
}
|
|
35
|
+
/** Only the exact model's input declaration can enable native image attachments. */
|
|
36
|
+
function parseInputModalities(id, text) {
|
|
37
|
+
if (text.match(/^Model ID:\s*`([^`]+)`/m)?.[1] !== id)
|
|
38
|
+
return undefined;
|
|
39
|
+
const details = text.split(/^## Model details[ \t]*\r?\n/m)[1]?.split(/^## /m)[0];
|
|
40
|
+
const declaration = details?.match(/^- Input modalities:[ \t]*([^\r\n]+)$/m)?.[1];
|
|
41
|
+
if (!declaration)
|
|
42
|
+
return undefined;
|
|
43
|
+
const modalities = declaration.split(",").map(value => value.trim());
|
|
44
|
+
if (!modalities.length || modalities.some(value => !["text", "image", "audio", "video"].includes(value)))
|
|
45
|
+
return undefined;
|
|
46
|
+
// The native coding picker currently accepts text and image inputs.
|
|
47
|
+
return [...new Set(modalities.filter(value => value === "text" || value === "image"))];
|
|
48
|
+
}
|
|
49
|
+
/** Codes and params that say the key, organization or project itself lacks access. */
|
|
50
|
+
const CREDENTIAL_ACCESS_CODES = new Set([
|
|
51
|
+
"invalid_api_key", "insufficient_permissions", "permission_denied", "unauthorized",
|
|
52
|
+
"organization_deactivated", "account_deactivated", "project_not_found", "project_archived",
|
|
53
|
+
"ip_not_authorized", "unsupported_country_region_territory",
|
|
54
|
+
]);
|
|
55
|
+
/**
|
|
56
|
+
* A 403 either removes the credential's access ("lost"), denies the whole model
|
|
57
|
+
* on this credential ("model"), denies only the probed setting ("setting"), or
|
|
58
|
+
* explains nothing ("ambiguous").
|
|
59
|
+
*/
|
|
60
|
+
function classifyProbe403(data, setting) {
|
|
61
|
+
const error = isRecord(data) && isRecord(data.error) ? data.error : null;
|
|
62
|
+
if (!error)
|
|
63
|
+
return "ambiguous";
|
|
64
|
+
const code = String(error.code ?? ""), param = String(error.param ?? "");
|
|
65
|
+
if (CREDENTIAL_ACCESS_CODES.has(code) || ["api_key", "organization", "project"].includes(param))
|
|
66
|
+
return "lost";
|
|
67
|
+
// Same model-level denial codes the runtime capability classifier uses.
|
|
68
|
+
if (classifyCapabilityFailure(403, data) === "model")
|
|
69
|
+
return "model";
|
|
70
|
+
// Compatibility and tier probes also carry an effort, but only an effort probe
|
|
71
|
+
// tests it: an effort-param 403 elsewhere explains nothing about the probed setting.
|
|
72
|
+
const effortProbe = Boolean(setting.effort) && !setting.tier && !setting.compatibility;
|
|
73
|
+
if (effortProbe && ["reasoning.effort", "reasoning"].includes(param))
|
|
74
|
+
return "setting";
|
|
75
|
+
if (setting.tier && param === "service_tier")
|
|
76
|
+
return "setting";
|
|
77
|
+
if (setting.compatibility && ["tools", "tool_choice"].includes(param))
|
|
78
|
+
return "setting";
|
|
79
|
+
return "ambiguous";
|
|
80
|
+
}
|
|
81
|
+
export class ApiModelCapabilities {
|
|
82
|
+
fetchImpl;
|
|
83
|
+
now;
|
|
84
|
+
activeProbes = 0;
|
|
85
|
+
probeWaiters = [];
|
|
86
|
+
async withProbeSlot(operation) {
|
|
87
|
+
if (this.activeProbes >= 4)
|
|
88
|
+
await new Promise((resolve) => this.probeWaiters.push(resolve));
|
|
89
|
+
else
|
|
90
|
+
this.activeProbes++;
|
|
91
|
+
try {
|
|
92
|
+
return await operation();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
const next = this.probeWaiters.shift();
|
|
96
|
+
if (next)
|
|
97
|
+
next();
|
|
98
|
+
else
|
|
99
|
+
this.activeProbes--;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
inFlight = new Map();
|
|
103
|
+
probes = new Map();
|
|
104
|
+
cache = new Map();
|
|
105
|
+
constructor(fetchImpl = fetch, now = Date.now) {
|
|
106
|
+
this.fetchImpl = fetchImpl;
|
|
107
|
+
this.now = now;
|
|
108
|
+
}
|
|
109
|
+
/** Probe results keyed by an opaque hash, for a fresh CLI process to honour the 15-minute cache. */
|
|
110
|
+
exportProbes() {
|
|
111
|
+
return [...this.probes];
|
|
112
|
+
}
|
|
113
|
+
importProbes(entries) {
|
|
114
|
+
for (const [key, value] of entries.slice(-1000)) {
|
|
115
|
+
if (this.probes.has(key) || value.at > this.now())
|
|
116
|
+
continue;
|
|
117
|
+
this.probes.set(key, value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async reasoning(id, refresh = false) {
|
|
121
|
+
return (await this.documentation(id, refresh)).levels;
|
|
122
|
+
}
|
|
123
|
+
async documentation(id, refresh = false) {
|
|
124
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/.test(id))
|
|
125
|
+
return { levels: [] };
|
|
126
|
+
const cached = this.cache.get(id);
|
|
127
|
+
if (!refresh && cached && cached.at + 60000 > this.now())
|
|
128
|
+
return cached;
|
|
129
|
+
let metadata = { levels: [] };
|
|
130
|
+
try {
|
|
131
|
+
const response = await this.fetchImpl(`https://developers.openai.com/api/docs/models/${encodeURIComponent(id)}.md`, { redirect: "error", signal: AbortSignal.timeout(5000) });
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
await response.body?.cancel();
|
|
134
|
+
throw Error("Documentation unavailable");
|
|
135
|
+
}
|
|
136
|
+
const reader = response.body?.getReader();
|
|
137
|
+
if (!reader)
|
|
138
|
+
throw Error("No documentation");
|
|
139
|
+
const chunks = [];
|
|
140
|
+
let size = 0;
|
|
141
|
+
try {
|
|
142
|
+
for (;;) {
|
|
143
|
+
const { done, value } = await reader.read();
|
|
144
|
+
if (done)
|
|
145
|
+
break;
|
|
146
|
+
size += value.byteLength;
|
|
147
|
+
if (size > 256 * 1024)
|
|
148
|
+
throw Error("Documentation too large");
|
|
149
|
+
chunks.push(value);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await reader.cancel();
|
|
154
|
+
}
|
|
155
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
156
|
+
metadata = {
|
|
157
|
+
levels: parseApiReasoningDocumentation(id, text),
|
|
158
|
+
inputModalities: parseInputModalities(id, text),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// An outage is not a capability revocation. Keep last successful evidence.
|
|
163
|
+
metadata = cached ?? metadata;
|
|
164
|
+
}
|
|
165
|
+
if (this.cache.size >= 1000)
|
|
166
|
+
this.cache.delete(this.cache.keys().next().value ?? "");
|
|
167
|
+
this.cache.set(id, { ...metadata, at: this.now() });
|
|
168
|
+
return metadata;
|
|
169
|
+
}
|
|
170
|
+
async probe(route, id, documented, force = false) {
|
|
171
|
+
const key = createHash("sha256")
|
|
172
|
+
.update(route.id)
|
|
173
|
+
.update("\0")
|
|
174
|
+
.update(route.apiKey)
|
|
175
|
+
.update("\0")
|
|
176
|
+
.update(id)
|
|
177
|
+
.digest("hex");
|
|
178
|
+
const old = this.probes.get(key);
|
|
179
|
+
// A probe cut short by 401/403/429 is retried after a minute, not trusted for 15.
|
|
180
|
+
const freshFor = old?.status.retry === "transient" ? 60_000 : 900_000;
|
|
181
|
+
if (!force && old && old.at + freshFor > this.now())
|
|
182
|
+
return old;
|
|
183
|
+
const running = this.inFlight.get(key);
|
|
184
|
+
if (running)
|
|
185
|
+
return running;
|
|
186
|
+
const pending = this.runProbe(route, id, documented, key).finally(() => this.inFlight.delete(key));
|
|
187
|
+
this.inFlight.set(key, pending);
|
|
188
|
+
return pending;
|
|
189
|
+
}
|
|
190
|
+
async runProbe(route, id, documented, key) {
|
|
191
|
+
const result = {
|
|
192
|
+
at: this.now(),
|
|
193
|
+
levels: [...documented],
|
|
194
|
+
tiers: [],
|
|
195
|
+
status: {},
|
|
196
|
+
};
|
|
197
|
+
let credentialUnavailable = false;
|
|
198
|
+
// 401/403 means this credential lost access: nothing it verified before
|
|
199
|
+
// may still be advertised. A throttle (429) or transport failure is only
|
|
200
|
+
// inconclusive and keeps what an earlier probe verified. Only
|
|
201
|
+
// "unsupported" or "downgraded" removes a setting otherwise.
|
|
202
|
+
let lostAccess = false;
|
|
203
|
+
// A model-level denial hides the model on this credential: no efforts, no tiers.
|
|
204
|
+
let modelDenied = false;
|
|
205
|
+
const previous = this.probes.get(key);
|
|
206
|
+
const keep = (name, status) => status === "unverified" && !lostAccess && !modelDenied && previous?.status[name] === "verified" ? "verified" : status;
|
|
207
|
+
const attempt = async (setting) => {
|
|
208
|
+
// Lost access drops everything this credential verified; a model-level
|
|
209
|
+
// denial hides the model; a 403 naming the probed setting removes only it.
|
|
210
|
+
// An unexplained 403 changes nothing and the probe is retried soon.
|
|
211
|
+
const denied403 = (data) => {
|
|
212
|
+
const kind = classifyProbe403(data, setting);
|
|
213
|
+
if (kind === "lost") {
|
|
214
|
+
credentialUnavailable = true;
|
|
215
|
+
lostAccess = true;
|
|
216
|
+
return "unverified";
|
|
217
|
+
}
|
|
218
|
+
if (kind === "model") {
|
|
219
|
+
modelDenied = true;
|
|
220
|
+
return "unsupported";
|
|
221
|
+
}
|
|
222
|
+
if (kind === "setting")
|
|
223
|
+
return "unsupported";
|
|
224
|
+
credentialUnavailable = true;
|
|
225
|
+
return "unverified";
|
|
226
|
+
};
|
|
227
|
+
return this.withProbeSlot(async () => {
|
|
228
|
+
if (credentialUnavailable || modelDenied)
|
|
229
|
+
return "unverified";
|
|
230
|
+
try {
|
|
231
|
+
const response = await this.fetchImpl("https://api.openai.com/v1/responses", {
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: {
|
|
234
|
+
authorization: `Bearer ${route.apiKey}`,
|
|
235
|
+
"content-type": "application/json",
|
|
236
|
+
},
|
|
237
|
+
redirect: "error",
|
|
238
|
+
signal: AbortSignal.timeout(15000),
|
|
239
|
+
body: JSON.stringify({
|
|
240
|
+
model: id,
|
|
241
|
+
input: "Reply OK.",
|
|
242
|
+
store: false,
|
|
243
|
+
max_output_tokens: 16,
|
|
244
|
+
...(setting.compatibility
|
|
245
|
+
? {
|
|
246
|
+
tools: [
|
|
247
|
+
{
|
|
248
|
+
type: "function",
|
|
249
|
+
name: "capability_probe",
|
|
250
|
+
parameters: {
|
|
251
|
+
type: "object",
|
|
252
|
+
properties: {},
|
|
253
|
+
required: [],
|
|
254
|
+
additionalProperties: false,
|
|
255
|
+
},
|
|
256
|
+
strict: true,
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
tool_choice: {
|
|
260
|
+
type: "function",
|
|
261
|
+
name: "capability_probe",
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
: {}),
|
|
265
|
+
...(setting.effort
|
|
266
|
+
? { reasoning: { effort: setting.effort } }
|
|
267
|
+
: {}),
|
|
268
|
+
...(setting.tier ? { service_tier: setting.tier } : {}),
|
|
269
|
+
}),
|
|
270
|
+
});
|
|
271
|
+
if ([401, 429].includes(response.status))
|
|
272
|
+
credentialUnavailable = true;
|
|
273
|
+
if (response.status === 401)
|
|
274
|
+
lostAccess = true;
|
|
275
|
+
// Probe bodies are tiny, but an upstream error must not grow local memory unboundedly.
|
|
276
|
+
const reader = response.body?.getReader();
|
|
277
|
+
if (!reader)
|
|
278
|
+
return response.status === 403 ? denied403(null) : "unverified";
|
|
279
|
+
const chunks = [];
|
|
280
|
+
let bytes = 0;
|
|
281
|
+
try {
|
|
282
|
+
for (;;) {
|
|
283
|
+
const { done, value } = await reader.read();
|
|
284
|
+
if (done)
|
|
285
|
+
break;
|
|
286
|
+
bytes += value.byteLength;
|
|
287
|
+
if (bytes > 256 * 1024)
|
|
288
|
+
throw Error("Probe response too large");
|
|
289
|
+
chunks.push(value);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
await reader.cancel();
|
|
294
|
+
}
|
|
295
|
+
let data;
|
|
296
|
+
try {
|
|
297
|
+
data = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return response.status === 403 ? denied403(null) : "unverified";
|
|
301
|
+
}
|
|
302
|
+
if (response.status === 403)
|
|
303
|
+
return denied403(data);
|
|
304
|
+
if (!response.ok) {
|
|
305
|
+
if (setting.compatibility) {
|
|
306
|
+
const error = isRecord(data) && isRecord(data.error) ? data.error : null;
|
|
307
|
+
return classifyCapabilityFailure(response.status, data) ===
|
|
308
|
+
"model" ||
|
|
309
|
+
(response.status === 400 &&
|
|
310
|
+
error &&
|
|
311
|
+
["tools", "tool_choice"].includes(String(error.param)) &&
|
|
312
|
+
["unsupported_parameter", "unsupported_value"].includes(String(error.code)))
|
|
313
|
+
? "unsupported"
|
|
314
|
+
: "unverified";
|
|
315
|
+
}
|
|
316
|
+
const error = isRecord(data) && isRecord(data.error) ? data.error : null;
|
|
317
|
+
return response.status === 400 &&
|
|
318
|
+
error &&
|
|
319
|
+
typeof error.param === "string" &&
|
|
320
|
+
["reasoning.effort", "reasoning", "service_tier"].includes(error.param)
|
|
321
|
+
? "unsupported"
|
|
322
|
+
: "unverified";
|
|
323
|
+
}
|
|
324
|
+
if (setting.compatibility)
|
|
325
|
+
return isRecord(data) &&
|
|
326
|
+
Array.isArray(data.output) &&
|
|
327
|
+
data.output.some((item) => isRecord(item) &&
|
|
328
|
+
item.type === "function_call" &&
|
|
329
|
+
item.name === "capability_probe")
|
|
330
|
+
? "verified"
|
|
331
|
+
: "unverified";
|
|
332
|
+
if (!setting.tier)
|
|
333
|
+
return isRecord(data) &&
|
|
334
|
+
isRecord(data.reasoning) &&
|
|
335
|
+
data.reasoning.effort === setting.effort
|
|
336
|
+
? "verified"
|
|
337
|
+
: "unverified";
|
|
338
|
+
if (!isRecord(data) || typeof data.service_tier !== "string")
|
|
339
|
+
return "unverified";
|
|
340
|
+
return canonicalServiceTier(data.service_tier) ===
|
|
341
|
+
canonicalServiceTier(setting.tier)
|
|
342
|
+
? "verified"
|
|
343
|
+
: "downgraded";
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return "unverified";
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
{
|
|
351
|
+
const checked = await Promise.all([...efforts]
|
|
352
|
+
.filter((effort) => !documented.includes(effort))
|
|
353
|
+
.map(async (effort) => ({
|
|
354
|
+
effort,
|
|
355
|
+
status: await attempt({ effort }),
|
|
356
|
+
})));
|
|
357
|
+
for (const { effort, status } of checked) {
|
|
358
|
+
const effective = keep(`effort:${effort}`, status);
|
|
359
|
+
result.status[`effort:${effort}`] = effective;
|
|
360
|
+
if (effective === "verified")
|
|
361
|
+
result.levels.push(effort);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const effort = result.levels.includes("low") ? "low" : result.levels[0];
|
|
365
|
+
result.status.responses = keep("responses", await attempt({ compatibility: true, effort }));
|
|
366
|
+
const checkedTiers = await Promise.all(["fast", "ultrafast"].map(async (tier) => ({
|
|
367
|
+
tier,
|
|
368
|
+
status: await attempt({ tier: tier === "fast" ? "priority" : tier, effort }),
|
|
369
|
+
})));
|
|
370
|
+
for (const { tier, status } of checkedTiers) {
|
|
371
|
+
const effective = keep(tier, status);
|
|
372
|
+
result.status[tier] = effective;
|
|
373
|
+
if (effective === "verified")
|
|
374
|
+
result.tiers.push(tier);
|
|
375
|
+
}
|
|
376
|
+
if (modelDenied) {
|
|
377
|
+
result.levels = [];
|
|
378
|
+
result.tiers = [];
|
|
379
|
+
for (const name of Object.keys(result.status))
|
|
380
|
+
result.status[name] = "unsupported";
|
|
381
|
+
result.status.responses = "unsupported";
|
|
382
|
+
}
|
|
383
|
+
if (credentialUnavailable && !modelDenied)
|
|
384
|
+
result.status.retry = "transient";
|
|
385
|
+
result.at = this.now();
|
|
386
|
+
if (this.probes.size >= 1000)
|
|
387
|
+
this.probes.delete(this.probes.keys().next().value ?? "");
|
|
388
|
+
this.probes.set(key, result);
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
async enrich(models, refresh = false, route, forceProbes = false) {
|
|
392
|
+
return mapWithConcurrency(models, 3, async (model) => {
|
|
393
|
+
const documented = await this.documentation(model.slug, refresh);
|
|
394
|
+
let levels = documented.levels;
|
|
395
|
+
let extra = {};
|
|
396
|
+
if (route?.enabled && route.probeCapabilities) {
|
|
397
|
+
const probed = await this.probe(route, model.slug, levels, forceProbes);
|
|
398
|
+
levels = probed.levels;
|
|
399
|
+
extra = {
|
|
400
|
+
service_tiers: probed.tiers.map((id) => ({
|
|
401
|
+
id: id === "fast" ? "priority" : id,
|
|
402
|
+
name: id === "fast" ? "Fast" : "Ultrafast",
|
|
403
|
+
description: "Verified by an API probe; runtime capacity can still downgrade processing.",
|
|
404
|
+
})),
|
|
405
|
+
default_service_tier: "default",
|
|
406
|
+
additional_speed_tiers: probed.tiers,
|
|
407
|
+
capability_probe_status: probed.status,
|
|
408
|
+
capability_checked_at: probed.at,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
// Ultra is a native orchestration mode. Codex resolves it to this
|
|
412
|
+
// supported wire effort; it is not an API entitlement named "ultra".
|
|
413
|
+
const multiAgentEffort = ["max", "xhigh", "high"].find((level) => levels.includes(level)) ?? null;
|
|
414
|
+
const nativeLevels = multiAgentEffort
|
|
415
|
+
? [...levels, "ultra"]
|
|
416
|
+
: levels;
|
|
417
|
+
return {
|
|
418
|
+
...model,
|
|
419
|
+
...extra,
|
|
420
|
+
...(documented.inputModalities ? { input_modalities: documented.inputModalities } : {}),
|
|
421
|
+
multi_agent_reasoning_effort: multiAgentEffort,
|
|
422
|
+
supported_reasoning_levels: nativeLevels.map((effort) => ({
|
|
423
|
+
effort,
|
|
424
|
+
description: effort === "ultra"
|
|
425
|
+
? "Proactive multi-agent mode using a supported API reasoning effort"
|
|
426
|
+
: `${effort} reasoning effort`,
|
|
427
|
+
})),
|
|
428
|
+
default_reasoning_level: levels.includes("medium")
|
|
429
|
+
? "medium"
|
|
430
|
+
: (levels[0] ?? null),
|
|
431
|
+
};
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|