@oh-my-pi/pi-ai 17.1.2 → 17.1.4
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/CHANGELOG.md +54 -0
- package/README.md +1 -1
- package/dist/types/auth-broker/client.d.ts +7 -1
- package/dist/types/auth-broker/remote-store.d.ts +4 -1
- package/dist/types/auth-broker/types.d.ts +6 -1
- package/dist/types/auth-broker/wire-schemas.d.ts +41 -0
- package/dist/types/auth-storage.d.ts +55 -0
- package/dist/types/providers/anthropic.d.ts +1 -7
- package/dist/types/providers/claude-code-fingerprint.d.ts +15 -0
- package/dist/types/providers/cursor.d.ts +30 -7
- package/dist/types/providers/openai-codex/request-transformer.d.ts +5 -4
- package/dist/types/providers/openai-codex-responses.d.ts +3 -0
- package/dist/types/registry/alibaba-token-plan.d.ts +11 -0
- package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
- package/dist/types/registry/oauth/anthropic.d.ts +1 -0
- package/dist/types/registry/oauth/index.d.ts +1 -0
- package/dist/types/registry/oauth/types.d.ts +8 -0
- package/dist/types/types.d.ts +71 -1
- package/dist/types/usage/minimax-code.d.ts +1 -0
- package/dist/types/usage.d.ts +10 -0
- package/dist/types/utils/idle-iterator.d.ts +6 -4
- package/package.json +4 -4
- package/src/auth-broker/client.ts +24 -1
- package/src/auth-broker/remote-store.ts +12 -0
- package/src/auth-broker/server.ts +7 -0
- package/src/auth-broker/types.ts +7 -0
- package/src/auth-broker/wire-schemas.ts +23 -0
- package/src/auth-storage.ts +279 -29
- package/src/error/flags.ts +1 -1
- package/src/providers/__tests__/kimi-code-thinking.test.ts +40 -5
- package/src/providers/amazon-bedrock.ts +3 -4
- package/src/providers/anthropic.ts +98 -32
- package/src/providers/azure-openai-responses.ts +36 -5
- package/src/providers/claude-code-fingerprint.ts +19 -0
- package/src/providers/cursor.ts +525 -40
- package/src/providers/openai-codex/request-transformer.ts +27 -7
- package/src/providers/openai-codex-responses.ts +37 -14
- package/src/providers/openai-completions.ts +2 -1
- package/src/providers/openai-responses.ts +16 -9
- package/src/providers/openai-shared.ts +12 -1
- package/src/registry/alibaba-token-plan.ts +103 -21
- package/src/registry/oauth/anthropic-constants.ts +12 -0
- package/src/registry/oauth/anthropic.ts +3 -1
- package/src/registry/oauth/index.ts +1 -0
- package/src/registry/oauth/types.ts +8 -0
- package/src/stream.ts +7 -2
- package/src/types.ts +79 -1
- package/src/usage/alibaba-token-plan.ts +34 -6
- package/src/usage/claude.ts +7 -2
- package/src/usage/minimax-code.ts +280 -19
- package/src/usage/openai-codex.ts +61 -14
- package/src/usage.ts +10 -0
- package/src/utils/idle-iterator.ts +8 -6
- package/src/utils.ts +5 -5
package/src/auth-storage.ts
CHANGED
|
@@ -54,6 +54,7 @@ import { googleGeminiCliUsageProvider } from "./usage/gemini";
|
|
|
54
54
|
import { githubCopilotUsageProvider } from "./usage/github-copilot";
|
|
55
55
|
import { antigravityRankingStrategy, antigravityUsageProvider } from "./usage/google-antigravity";
|
|
56
56
|
import { kimiUsageProvider } from "./usage/kimi";
|
|
57
|
+
import { minimaxCodeUsageProvider } from "./usage/minimax-code";
|
|
57
58
|
import { ollamaCloudUsageProvider, ollamaUsageProvider } from "./usage/ollama";
|
|
58
59
|
import { codexRankingStrategy, openaiCodexUsageProvider } from "./usage/openai-codex";
|
|
59
60
|
import {
|
|
@@ -170,6 +171,28 @@ export interface StoredCredentialBlock {
|
|
|
170
171
|
updatedAtMs?: number;
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Identity slice of a disabled (soft-deleted) credential tombstone — cause and
|
|
176
|
+
* account identity only, never token material. Surfaced so auto-disabled
|
|
177
|
+
* accounts (e.g. an expired Anthropic OAuth grant) stay visible in `omp usage`
|
|
178
|
+
* instead of silently vanishing until the user notices missing quota.
|
|
179
|
+
*/
|
|
180
|
+
export interface DisabledCredentialSummary {
|
|
181
|
+
/** Database row id (matches {@link StoredAuthCredential.id}). */
|
|
182
|
+
id: number;
|
|
183
|
+
provider: string;
|
|
184
|
+
type: AuthCredential["type"];
|
|
185
|
+
email?: string;
|
|
186
|
+
accountId?: string;
|
|
187
|
+
/** Organization/workspace the credential was scoped to (Anthropic/ChatGPT multi-subscription). */
|
|
188
|
+
orgId?: string;
|
|
189
|
+
orgName?: string;
|
|
190
|
+
/** Verbatim disable cause captured when the row was torn down. */
|
|
191
|
+
cause: string;
|
|
192
|
+
/** Epoch ms the row was disabled (SQLite `updated_at`), when known. */
|
|
193
|
+
disabledAtMs?: number;
|
|
194
|
+
}
|
|
195
|
+
|
|
173
196
|
/**
|
|
174
197
|
* Per-credential health record returned by {@link AuthStorage.checkCredentials}.
|
|
175
198
|
*
|
|
@@ -353,6 +376,21 @@ export interface AuthCredentialStore {
|
|
|
353
376
|
/** Optional hook to notify the underlying store that usage report cache is stale. */
|
|
354
377
|
invalidateUsageCache?(signal?: AbortSignal): Promise<void>;
|
|
355
378
|
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
379
|
+
/**
|
|
380
|
+
* Optional store hook to re-hydrate the credential snapshot from its
|
|
381
|
+
* backing source. Remote broker stores re-fetch `GET /v1/snapshot` so a
|
|
382
|
+
* disk-cached snapshot (up to an hour stale) cannot be paired with live
|
|
383
|
+
* per-credential data; local SQLite stores omit it — their reads are
|
|
384
|
+
* always current.
|
|
385
|
+
*/
|
|
386
|
+
refreshSnapshot?(): Promise<unknown>;
|
|
387
|
+
/**
|
|
388
|
+
* Disabled credential tombstones (see {@link DisabledCredentialSummary}).
|
|
389
|
+
* Optional: remote stores forward to the broker's
|
|
390
|
+
* `GET /v1/credentials/disabled` (empty list when the broker predates the
|
|
391
|
+
* endpoint); stores without tombstones omit it.
|
|
392
|
+
*/
|
|
393
|
+
listDisabledCredentials?(provider?: string, signal?: AbortSignal): Promise<DisabledCredentialSummary[]>;
|
|
356
394
|
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
357
395
|
deleteAuthCredential(id: number, disabledCause: string): void;
|
|
358
396
|
tryDisableAuthCredentialIfMatches(
|
|
@@ -381,6 +419,8 @@ export interface AuthCredentialStore {
|
|
|
381
419
|
getCredentialBlockReconcileAfter?(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
382
420
|
/** Upsert with MAX semantics: keep the later blockedUntilMs on conflict. */
|
|
383
421
|
upsertCredentialBlock?(block: StoredCredentialBlock): void;
|
|
422
|
+
/** Drop one block row for a credential/provider/scope key. */
|
|
423
|
+
deleteCredentialBlock?(credentialId: number, providerKey: string, blockScope: string): void;
|
|
384
424
|
/** Drop every block row for a credential (all providerKeys/scopes). */
|
|
385
425
|
deleteCredentialBlocks?(credentialId: number): void;
|
|
386
426
|
/** Prune rows with blocked_until_ms <= nowMs. */
|
|
@@ -606,6 +646,7 @@ const DEFAULT_USAGE_PROVIDERS: UsageProvider[] = [
|
|
|
606
646
|
alibabaTokenPlanUsageProvider,
|
|
607
647
|
openaiCodexUsageProvider,
|
|
608
648
|
kimiUsageProvider,
|
|
649
|
+
minimaxCodeUsageProvider,
|
|
609
650
|
antigravityUsageProvider,
|
|
610
651
|
googleGeminiCliUsageProvider,
|
|
611
652
|
ollamaUsageProvider,
|
|
@@ -1654,11 +1695,16 @@ export class AuthStorage {
|
|
|
1654
1695
|
provider: string,
|
|
1655
1696
|
providerKey: string,
|
|
1656
1697
|
credentialIndex: number,
|
|
1657
|
-
|
|
1698
|
+
blockScopeOrScopes: string | readonly string[] | undefined = undefined,
|
|
1658
1699
|
): number | undefined {
|
|
1659
1700
|
const nowMs = Date.now();
|
|
1701
|
+
// A request honours its own scope plus any legacy catch-all scope, so a
|
|
1702
|
+
// block written before backoff was scoped still applies to everything.
|
|
1703
|
+
const scopes = (
|
|
1704
|
+
typeof blockScopeOrScopes === "string" ? [blockScopeOrScopes] : (blockScopeOrScopes ?? [])
|
|
1705
|
+
).filter(scope => scope.length > 0);
|
|
1660
1706
|
let blockedUntil = this.#getCredentialBlockedUntilForKey(providerKey, credentialIndex, nowMs);
|
|
1661
|
-
|
|
1707
|
+
for (const blockScope of scopes) {
|
|
1662
1708
|
const scopedBlockedUntil = this.#getCredentialBlockedUntilForKey(
|
|
1663
1709
|
this.#toScopedBackoffKey(providerKey, blockScope),
|
|
1664
1710
|
credentialIndex,
|
|
@@ -1671,16 +1717,14 @@ export class AuthStorage {
|
|
|
1671
1717
|
|
|
1672
1718
|
const credentialId = this.#getStoredCredentials(provider)[credentialIndex]?.id;
|
|
1673
1719
|
if (credentialId === undefined) return blockedUntil;
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
blockedUntil = persistedGlobalBlockedUntil;
|
|
1681
|
-
}
|
|
1720
|
+
const persistedGlobalBlockedUntil = this.#readPersistedCredentialBlock(credentialId, providerKey, "");
|
|
1721
|
+
if (
|
|
1722
|
+
persistedGlobalBlockedUntil !== undefined &&
|
|
1723
|
+
(blockedUntil === undefined || persistedGlobalBlockedUntil > blockedUntil)
|
|
1724
|
+
) {
|
|
1725
|
+
blockedUntil = persistedGlobalBlockedUntil;
|
|
1682
1726
|
}
|
|
1683
|
-
|
|
1727
|
+
for (const blockScope of scopes) {
|
|
1684
1728
|
const persistedScopedBlockedUntil = this.#readPersistedCredentialBlock(credentialId, providerKey, blockScope);
|
|
1685
1729
|
if (
|
|
1686
1730
|
persistedScopedBlockedUntil !== undefined &&
|
|
@@ -1697,7 +1741,7 @@ export class AuthStorage {
|
|
|
1697
1741
|
provider: string,
|
|
1698
1742
|
providerKey: string,
|
|
1699
1743
|
credentialIndex: number,
|
|
1700
|
-
blockScope: string | undefined = undefined,
|
|
1744
|
+
blockScope: string | readonly string[] | undefined = undefined,
|
|
1701
1745
|
): boolean {
|
|
1702
1746
|
return this.#getCredentialBlockedUntil(provider, providerKey, credentialIndex, blockScope) !== undefined;
|
|
1703
1747
|
}
|
|
@@ -1882,6 +1926,8 @@ export class AuthStorage {
|
|
|
1882
1926
|
strategy: CredentialRankingStrategy;
|
|
1883
1927
|
rankingContext: CredentialRankingContext;
|
|
1884
1928
|
blockScope?: string;
|
|
1929
|
+
/** Scopes a block may live under for this request; reads honour all of them. */
|
|
1930
|
+
blockScopes?: readonly string[];
|
|
1885
1931
|
}): Promise<ApiKeyCandidate[]> {
|
|
1886
1932
|
const nowMs = Date.now();
|
|
1887
1933
|
const { strategy } = args;
|
|
@@ -1895,7 +1941,7 @@ export class AuthStorage {
|
|
|
1895
1941
|
args.provider,
|
|
1896
1942
|
args.providerKey,
|
|
1897
1943
|
selection.index,
|
|
1898
|
-
args.blockScope,
|
|
1944
|
+
args.blockScopes ?? args.blockScope,
|
|
1899
1945
|
);
|
|
1900
1946
|
if (blockedUntil !== undefined) {
|
|
1901
1947
|
return { selection, usage: null, usageChecked: false, blockedUntil };
|
|
@@ -1920,7 +1966,7 @@ export class AuthStorage {
|
|
|
1920
1966
|
args.provider,
|
|
1921
1967
|
args.providerKey,
|
|
1922
1968
|
selection.index,
|
|
1923
|
-
args.blockScope,
|
|
1969
|
+
args.blockScopes ?? args.blockScope,
|
|
1924
1970
|
);
|
|
1925
1971
|
return { selection, usage: null, usageChecked: false, blockedUntil };
|
|
1926
1972
|
});
|
|
@@ -2003,6 +2049,7 @@ export class AuthStorage {
|
|
|
2003
2049
|
|
|
2004
2050
|
const rankingContext: CredentialRankingContext = { modelId: options?.modelId };
|
|
2005
2051
|
const blockScope = strategy.blockScope?.(rankingContext);
|
|
2052
|
+
const blockScopes = strategy.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []);
|
|
2006
2053
|
const candidates = await this.#rankApiKeySelections({
|
|
2007
2054
|
providerKey,
|
|
2008
2055
|
provider,
|
|
@@ -2012,6 +2059,7 @@ export class AuthStorage {
|
|
|
2012
2059
|
strategy,
|
|
2013
2060
|
rankingContext,
|
|
2014
2061
|
blockScope,
|
|
2062
|
+
blockScopes,
|
|
2015
2063
|
});
|
|
2016
2064
|
return candidates[0]?.selection ?? fallback;
|
|
2017
2065
|
}
|
|
@@ -2708,7 +2756,10 @@ export class AuthStorage {
|
|
|
2708
2756
|
this.#resetProviderAssignments(provider);
|
|
2709
2757
|
return { type: "api_key" };
|
|
2710
2758
|
}
|
|
2711
|
-
|
|
2759
|
+
// Stamp the interactive-login instant: providers with an absolute grant
|
|
2760
|
+
// lifetime (Anthropic) need it to surface re-login deadlines, and token
|
|
2761
|
+
// refreshes only ever merge over this credential without clearing it.
|
|
2762
|
+
const newCredential: OAuthCredential = { type: "oauth", ...result, authorizedAt: Date.now() };
|
|
2712
2763
|
// Use #upsertOAuthCredential to upsert the new credential.
|
|
2713
2764
|
// Any legacy api_key rows from older versions will be cleaned up so they do not
|
|
2714
2765
|
// shadow the new OAuth row, while preserving other active OAuth credentials.
|
|
@@ -2927,6 +2978,9 @@ export class AuthStorage {
|
|
|
2927
2978
|
apiEndpoint: next.apiEndpoint,
|
|
2928
2979
|
orgId: next.orgId ?? entry.credential.orgId,
|
|
2929
2980
|
orgName: next.orgName ?? entry.credential.orgName,
|
|
2981
|
+
// Not part of UsageCredential — carried from the stored row so the
|
|
2982
|
+
// interactive-login anchor survives usage-path refresh persists.
|
|
2983
|
+
authorizedAt: entry.credential.authorizedAt,
|
|
2930
2984
|
});
|
|
2931
2985
|
}
|
|
2932
2986
|
|
|
@@ -3684,6 +3738,7 @@ export class AuthStorage {
|
|
|
3684
3738
|
|
|
3685
3739
|
const rankingContext: CredentialRankingContext = { modelId: options.modelId };
|
|
3686
3740
|
const blockScope = strategy.blockScope?.(rankingContext);
|
|
3741
|
+
const blockScopes = strategy.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []);
|
|
3687
3742
|
const reserveFraction = Number.isFinite(options.reserveFraction)
|
|
3688
3743
|
? Math.max(0, Math.min(1, options.reserveFraction))
|
|
3689
3744
|
: 0;
|
|
@@ -3692,7 +3747,7 @@ export class AuthStorage {
|
|
|
3692
3747
|
pool.map(async ({ entry, index }): Promise<ModelUsageAccountHealth> => {
|
|
3693
3748
|
const credentialType = entry.credential.type;
|
|
3694
3749
|
const providerKey = this.#getProviderTypeKey(provider, credentialType);
|
|
3695
|
-
let blockedUntil = this.#getCredentialBlockedUntil(provider, providerKey, index,
|
|
3750
|
+
let blockedUntil = this.#getCredentialBlockedUntil(provider, providerKey, index, blockScopes);
|
|
3696
3751
|
if (blockedUntil !== undefined && provider !== "openai-codex") {
|
|
3697
3752
|
return {
|
|
3698
3753
|
credentialId: entry.id,
|
|
@@ -3718,7 +3773,7 @@ export class AuthStorage {
|
|
|
3718
3773
|
}
|
|
3719
3774
|
|
|
3720
3775
|
if (provider === "openai-codex") {
|
|
3721
|
-
blockedUntil = this.#getCredentialBlockedUntil(provider, providerKey, index,
|
|
3776
|
+
blockedUntil = this.#getCredentialBlockedUntil(provider, providerKey, index, blockScopes);
|
|
3722
3777
|
}
|
|
3723
3778
|
if (blockedUntil !== undefined) {
|
|
3724
3779
|
return {
|
|
@@ -4145,6 +4200,7 @@ export class AuthStorage {
|
|
|
4145
4200
|
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
4146
4201
|
const rankingContext: CredentialRankingContext = { modelId: options?.modelId };
|
|
4147
4202
|
const blockScope = strategy?.blockScope?.(rankingContext);
|
|
4203
|
+
const siblingBlockScopes = strategy?.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []);
|
|
4148
4204
|
const now = Date.now();
|
|
4149
4205
|
let blockedUntil = now + (options?.retryAfterMs ?? AuthStorage.#defaultBackoffMs);
|
|
4150
4206
|
|
|
@@ -4179,11 +4235,14 @@ export class AuthStorage {
|
|
|
4179
4235
|
|
|
4180
4236
|
let retryAtMs: number | undefined;
|
|
4181
4237
|
for (const candidate of remainingCredentials) {
|
|
4238
|
+
// Sibling availability must use the same scope set selection reads, or
|
|
4239
|
+
// this reports a sibling as free that selection will then refuse, most
|
|
4240
|
+
// visibly when the sibling still carries a legacy shared block.
|
|
4182
4241
|
const candidateBlockedUntil = this.#getCredentialBlockedUntil(
|
|
4183
4242
|
provider,
|
|
4184
4243
|
providerKey,
|
|
4185
4244
|
candidate.index,
|
|
4186
|
-
|
|
4245
|
+
siblingBlockScopes,
|
|
4187
4246
|
);
|
|
4188
4247
|
if (candidateBlockedUntil === undefined) return { switched: true };
|
|
4189
4248
|
if (retryAtMs === undefined || candidateBlockedUntil < retryAtMs) retryAtMs = candidateBlockedUntil;
|
|
@@ -4305,6 +4364,8 @@ export class AuthStorage {
|
|
|
4305
4364
|
strategy: CredentialRankingStrategy;
|
|
4306
4365
|
rankingContext: CredentialRankingContext;
|
|
4307
4366
|
blockScope?: string;
|
|
4367
|
+
/** Scopes a block may live under for this request; reads honour all of them. */
|
|
4368
|
+
blockScopes?: readonly string[];
|
|
4308
4369
|
}): Promise<OAuthCandidate[]> {
|
|
4309
4370
|
const nowMs = Date.now();
|
|
4310
4371
|
const { strategy } = args;
|
|
@@ -4322,7 +4383,7 @@ export class AuthStorage {
|
|
|
4322
4383
|
args.provider,
|
|
4323
4384
|
args.providerKey,
|
|
4324
4385
|
selection.index,
|
|
4325
|
-
args.blockScope,
|
|
4386
|
+
args.blockScopes ?? args.blockScope,
|
|
4326
4387
|
);
|
|
4327
4388
|
let usage: UsageReport | null = null;
|
|
4328
4389
|
let usageChecked = false;
|
|
@@ -4336,7 +4397,7 @@ export class AuthStorage {
|
|
|
4336
4397
|
args.provider,
|
|
4337
4398
|
args.providerKey,
|
|
4338
4399
|
selection.index,
|
|
4339
|
-
args.blockScope,
|
|
4400
|
+
args.blockScopes ?? args.blockScope,
|
|
4340
4401
|
);
|
|
4341
4402
|
}
|
|
4342
4403
|
if (blockedUntil !== undefined) return { selection, usage, usageChecked, blockedUntil };
|
|
@@ -4447,6 +4508,8 @@ export class AuthStorage {
|
|
|
4447
4508
|
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
4448
4509
|
const rankingContext: CredentialRankingContext = { modelId: options?.modelId };
|
|
4449
4510
|
const blockScope = strategy?.blockScope?.(rankingContext);
|
|
4511
|
+
// Reads honour every scope that applies; the scalar above is for args that persist.
|
|
4512
|
+
const blockScopes = strategy?.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []);
|
|
4450
4513
|
const planRequirement = resolveOpenAICodexPlanRequirement(provider, options?.modelId);
|
|
4451
4514
|
const hasPlanRequirement = planRequirement !== "none";
|
|
4452
4515
|
const checkUsage = strategy !== undefined && (credentials.length > 1 || hasPlanRequirement);
|
|
@@ -4475,7 +4538,7 @@ export class AuthStorage {
|
|
|
4475
4538
|
const sessionPreferredIsAvailable =
|
|
4476
4539
|
sessionPreferredIndex !== undefined &&
|
|
4477
4540
|
sessionPreferredCanRefreshOrUse &&
|
|
4478
|
-
!this.#isCredentialBlocked(provider, providerKey, sessionPreferredIndex,
|
|
4541
|
+
!this.#isCredentialBlocked(provider, providerKey, sessionPreferredIndex, blockScopes);
|
|
4479
4542
|
const shouldRank = checkUsage && (!sessionPreferredIsAvailable || !sessionPreferredIsWarm || hasPlanRequirement);
|
|
4480
4543
|
// When ranking, seed the pinned credential first in the evaluation order so it wins genuine
|
|
4481
4544
|
// ties (the ranked comparator falls back to `orderPos`) without overriding a strictly-better
|
|
@@ -4504,6 +4567,7 @@ export class AuthStorage {
|
|
|
4504
4567
|
strategy: strategy!,
|
|
4505
4568
|
rankingContext,
|
|
4506
4569
|
blockScope,
|
|
4570
|
+
blockScopes,
|
|
4507
4571
|
})
|
|
4508
4572
|
: order
|
|
4509
4573
|
.map(idx => credentials[idx])
|
|
@@ -4516,7 +4580,7 @@ export class AuthStorage {
|
|
|
4516
4580
|
if (!shouldRank && sessionPreferredIndex !== undefined && !hasPlanRequirement) {
|
|
4517
4581
|
const sessionPreferredCandidate = candidates.findIndex(
|
|
4518
4582
|
candidate =>
|
|
4519
|
-
!this.#isCredentialBlocked(provider, providerKey, candidate.selection.index,
|
|
4583
|
+
!this.#isCredentialBlocked(provider, providerKey, candidate.selection.index, blockScopes) &&
|
|
4520
4584
|
candidate.selection.index === sessionPreferredIndex,
|
|
4521
4585
|
);
|
|
4522
4586
|
if (sessionPreferredCandidate > 0) {
|
|
@@ -4610,7 +4674,7 @@ export class AuthStorage {
|
|
|
4610
4674
|
if (hasPlanRequirement && sessionPreferredIndex !== undefined) {
|
|
4611
4675
|
const sessionPreferredCandidate = candidates.findIndex(
|
|
4612
4676
|
candidate =>
|
|
4613
|
-
!this.#isCredentialBlocked(provider, providerKey, candidate.selection.index,
|
|
4677
|
+
!this.#isCredentialBlocked(provider, providerKey, candidate.selection.index, blockScopes) &&
|
|
4614
4678
|
candidate.selection.index === sessionPreferredIndex,
|
|
4615
4679
|
);
|
|
4616
4680
|
if (sessionPreferredCandidate > 0) {
|
|
@@ -4647,6 +4711,7 @@ export class AuthStorage {
|
|
|
4647
4711
|
strategy,
|
|
4648
4712
|
rankingContext,
|
|
4649
4713
|
blockScope,
|
|
4714
|
+
blockScopes,
|
|
4650
4715
|
},
|
|
4651
4716
|
);
|
|
4652
4717
|
if (resolved) return resolved;
|
|
@@ -4824,6 +4889,7 @@ export class AuthStorage {
|
|
|
4824
4889
|
strategy?: CredentialRankingStrategy;
|
|
4825
4890
|
rankingContext?: CredentialRankingContext;
|
|
4826
4891
|
blockScope?: string;
|
|
4892
|
+
blockScopes?: readonly string[];
|
|
4827
4893
|
/** When false, a definitive failure of THIS credential returns undefined instead of falling back to the ranked/round-robin selector (target-only resolution). */
|
|
4828
4894
|
allowFallback?: boolean;
|
|
4829
4895
|
},
|
|
@@ -4838,9 +4904,13 @@ export class AuthStorage {
|
|
|
4838
4904
|
strategy,
|
|
4839
4905
|
rankingContext,
|
|
4840
4906
|
blockScope,
|
|
4907
|
+
blockScopes,
|
|
4841
4908
|
allowFallback = true,
|
|
4842
4909
|
} = usageOptions;
|
|
4843
|
-
if (
|
|
4910
|
+
if (
|
|
4911
|
+
!allowBlocked &&
|
|
4912
|
+
this.#isCredentialBlocked(provider, providerKey, selection.index, blockScopes ?? blockScope)
|
|
4913
|
+
) {
|
|
4844
4914
|
return undefined;
|
|
4845
4915
|
}
|
|
4846
4916
|
|
|
@@ -4933,6 +5003,7 @@ export class AuthStorage {
|
|
|
4933
5003
|
apiEndpoint: result.newCredentials.apiEndpoint ?? selection.credential.apiEndpoint,
|
|
4934
5004
|
orgId: result.newCredentials.orgId ?? selection.credential.orgId,
|
|
4935
5005
|
orgName: result.newCredentials.orgName ?? selection.credential.orgName,
|
|
5006
|
+
authorizedAt: result.newCredentials.authorizedAt ?? selection.credential.authorizedAt,
|
|
4936
5007
|
};
|
|
4937
5008
|
if (credentialId !== undefined) {
|
|
4938
5009
|
const idx = this.#replaceCredentialById(provider, credentialId, updated);
|
|
@@ -5573,6 +5644,70 @@ export class AuthStorage {
|
|
|
5573
5644
|
* persisted and in-memory `openai-codex:oauth` blocks so credential selection
|
|
5574
5645
|
* can re-include recovered seats before a stale block naturally expires.
|
|
5575
5646
|
*/
|
|
5647
|
+
/**
|
|
5648
|
+
* Narrow a usage report to the limits a backoff scope covers, so a scope is
|
|
5649
|
+
* judged only by the meters it gates. A legacy scope that meant "block
|
|
5650
|
+
* everything" keeps the whole report.
|
|
5651
|
+
*/
|
|
5652
|
+
#scopeUsageReportToBlockScope(
|
|
5653
|
+
report: UsageReport,
|
|
5654
|
+
blockScope: string | undefined,
|
|
5655
|
+
strategy: CredentialRankingStrategy | undefined,
|
|
5656
|
+
): UsageReport {
|
|
5657
|
+
if (!blockScope || !strategy?.scopeLimits || !strategy.blockScopes) return report;
|
|
5658
|
+
// Find a request context whose scope set leads with this scope; its scoped
|
|
5659
|
+
// limits are the ones this block gates.
|
|
5660
|
+
const modelId = blockScope === "spark" ? "gpt-5.3-codex-spark" : "gpt-5.3-codex";
|
|
5661
|
+
if (strategy.blockScopes({ modelId })[0] !== blockScope) return report;
|
|
5662
|
+
const scopedLimits = strategy.scopeLimits(report, { modelId });
|
|
5663
|
+
const meterStates = report.metadata?.meterStates;
|
|
5664
|
+
const meterState =
|
|
5665
|
+
meterStates !== null && typeof meterStates === "object"
|
|
5666
|
+
? (meterStates as Record<string, { allowed?: boolean; limitReached?: boolean }>)[blockScope]
|
|
5667
|
+
: undefined;
|
|
5668
|
+
return {
|
|
5669
|
+
...report,
|
|
5670
|
+
limits: scopedLimits,
|
|
5671
|
+
metadata:
|
|
5672
|
+
meterState || blockScope === "spark"
|
|
5673
|
+
? {
|
|
5674
|
+
...report.metadata,
|
|
5675
|
+
allowed: meterState?.allowed,
|
|
5676
|
+
limitReached: meterState?.limitReached,
|
|
5677
|
+
}
|
|
5678
|
+
: report.metadata,
|
|
5679
|
+
};
|
|
5680
|
+
}
|
|
5681
|
+
|
|
5682
|
+
/**
|
|
5683
|
+
* Clear one backoff scope. The in-memory backoff is per scope so it is
|
|
5684
|
+
* dropped directly; the persisted store deletes a credential's blocks as a
|
|
5685
|
+
* unit, so it is only purged once no other scope still holds a live block.
|
|
5686
|
+
* Leaving a persisted row behind is safe: the scope it belongs to is
|
|
5687
|
+
* unblocked in memory, and the row heals on the pass where its own meter
|
|
5688
|
+
* recovers.
|
|
5689
|
+
*/
|
|
5690
|
+
#clearCredentialBlockScope(
|
|
5691
|
+
provider: string,
|
|
5692
|
+
credentialId: number,
|
|
5693
|
+
credentialIndex: number,
|
|
5694
|
+
providerKey: string,
|
|
5695
|
+
blockScope: string | undefined,
|
|
5696
|
+
): void {
|
|
5697
|
+
const key = this.#toScopedBackoffKey(providerKey, blockScope);
|
|
5698
|
+
const backoffMap = this.#credentialBackoff.get(key);
|
|
5699
|
+
backoffMap?.delete(credentialIndex);
|
|
5700
|
+
if (backoffMap?.size === 0) this.#credentialBackoff.delete(key);
|
|
5701
|
+
const probeAfterMap = this.#credentialBackoffProbeAfter.get(key);
|
|
5702
|
+
probeAfterMap?.delete(credentialIndex);
|
|
5703
|
+
if (probeAfterMap?.size === 0) this.#credentialBackoffProbeAfter.delete(key);
|
|
5704
|
+
try {
|
|
5705
|
+
this.deleteCredentialBlock(credentialId, providerKey, blockScope ?? "");
|
|
5706
|
+
} catch (err) {
|
|
5707
|
+
logger.debug("Failed to clear persisted credential block", { err, provider, credentialId, blockScope });
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
|
|
5576
5711
|
#isHealthyCodexUsageReport(report: UsageReport): boolean {
|
|
5577
5712
|
if (report.provider !== "openai-codex") return false;
|
|
5578
5713
|
const metadata = report.metadata;
|
|
@@ -5581,13 +5716,51 @@ export class AuthStorage {
|
|
|
5581
5716
|
}
|
|
5582
5717
|
|
|
5583
5718
|
#reconcileCodexUsageBlockForCredential(provider: Provider, credentialId: number, report: UsageReport): void {
|
|
5584
|
-
if (!this.#isHealthyCodexUsageReport(report)) return;
|
|
5585
5719
|
const providerKey = this.#getProviderTypeKey(provider, "oauth");
|
|
5586
5720
|
const credentialIndex = this.#getStoredCredentials(provider).findIndex(entry => entry.id === credentialId);
|
|
5587
5721
|
if (credentialIndex < 0) return;
|
|
5588
5722
|
// Mirror selection: consult the same strategy scope `markUsageLimitReached`
|
|
5589
5723
|
// persists under, else a scoped block is invisible here and never healed.
|
|
5590
|
-
|
|
5724
|
+
// Reconciliation has no request context, so it cannot know which scope a
|
|
5725
|
+
// block was written under. Heal every scope the strategy can produce, plus
|
|
5726
|
+
// any legacy scope it still lists, or a block persisted under one scope
|
|
5727
|
+
// stays invisible here forever.
|
|
5728
|
+
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
5729
|
+
const blockScopes =
|
|
5730
|
+
strategy?.blockScopes?.() ?? [strategy?.blockScope?.({})].filter(scope => scope !== undefined);
|
|
5731
|
+
const scopesToHeal = blockScopes.length > 0 ? blockScopes : [undefined];
|
|
5732
|
+
for (const blockScope of scopesToHeal) {
|
|
5733
|
+
this.#healCodexUsageBlockScope(
|
|
5734
|
+
provider,
|
|
5735
|
+
providerKey,
|
|
5736
|
+
credentialId,
|
|
5737
|
+
credentialIndex,
|
|
5738
|
+
blockScope,
|
|
5739
|
+
report,
|
|
5740
|
+
strategy,
|
|
5741
|
+
);
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
5744
|
+
|
|
5745
|
+
/**
|
|
5746
|
+
* Heal one backoff scope against the limits that scope actually covers.
|
|
5747
|
+
*
|
|
5748
|
+
* Judging a scope by the whole report is wrong once scopes are per-meter: an
|
|
5749
|
+
* exhausted Spark meter would keep a recovered chat block alive, and healing
|
|
5750
|
+
* would then delete every scope including a block that is still valid. So
|
|
5751
|
+
* each scope is evaluated against its own limits and cleared on its own.
|
|
5752
|
+
*/
|
|
5753
|
+
#healCodexUsageBlockScope(
|
|
5754
|
+
provider: Provider,
|
|
5755
|
+
providerKey: string,
|
|
5756
|
+
credentialId: number,
|
|
5757
|
+
credentialIndex: number,
|
|
5758
|
+
blockScope: string | undefined,
|
|
5759
|
+
report: UsageReport,
|
|
5760
|
+
strategy: CredentialRankingStrategy | undefined,
|
|
5761
|
+
): void {
|
|
5762
|
+
const scopedReport = this.#scopeUsageReportToBlockScope(report, blockScope, strategy);
|
|
5763
|
+
if (!this.#isHealthyCodexUsageReport(scopedReport)) return;
|
|
5591
5764
|
const blockedUntilMs = this.#getCredentialBlockedUntil(provider, providerKey, credentialIndex, blockScope);
|
|
5592
5765
|
if (blockedUntilMs === undefined) return;
|
|
5593
5766
|
// `/usage` can lag the request path that just returned 429. Fresh local or
|
|
@@ -5603,10 +5776,11 @@ export class AuthStorage {
|
|
|
5603
5776
|
if (Math.max(globalProbeAfterMs, scopedProbeAfterMs, storeGlobalProbeAfterMs, storeScopedProbeAfterMs) > nowMs) {
|
|
5604
5777
|
return;
|
|
5605
5778
|
}
|
|
5606
|
-
this.#
|
|
5779
|
+
this.#clearCredentialBlockScope(provider, credentialId, credentialIndex, providerKey, blockScope);
|
|
5607
5780
|
logger.info("Cleared stale Codex usage-limit block after healthy live usage report", {
|
|
5608
5781
|
credentialId,
|
|
5609
5782
|
provider,
|
|
5783
|
+
blockScope,
|
|
5610
5784
|
clearedBlockedUntilMs: blockedUntilMs,
|
|
5611
5785
|
});
|
|
5612
5786
|
}
|
|
@@ -5647,7 +5821,6 @@ export class AuthStorage {
|
|
|
5647
5821
|
#reconcileCodexUsageBlocksFromReports(reports: UsageReport[]): void {
|
|
5648
5822
|
const reconciled = new Set<number>();
|
|
5649
5823
|
for (const report of reports) {
|
|
5650
|
-
if (!this.#isHealthyCodexUsageReport(report)) continue;
|
|
5651
5824
|
for (const credentialId of this.#findStoredCredentialIdsForUsageReport(report)) {
|
|
5652
5825
|
if (reconciled.has(credentialId)) continue;
|
|
5653
5826
|
reconciled.add(credentialId);
|
|
@@ -5892,6 +6065,28 @@ export class AuthStorage {
|
|
|
5892
6065
|
return { generation: this.#generation, generatedAt: Date.now(), credentials: entries };
|
|
5893
6066
|
}
|
|
5894
6067
|
|
|
6068
|
+
/**
|
|
6069
|
+
* Disabled credential tombstones for display surfaces (`omp usage`,
|
|
6070
|
+
* broker `GET /v1/credentials/disabled`). Empty when the backing store
|
|
6071
|
+
* keeps no tombstones or the remote broker predates the endpoint.
|
|
6072
|
+
*/
|
|
6073
|
+
async listDisabledCredentials(provider?: string, signal?: AbortSignal): Promise<DisabledCredentialSummary[]> {
|
|
6074
|
+
if (!this.#store.listDisabledCredentials) return [];
|
|
6075
|
+
return this.#store.listDisabledCredentials(provider, signal);
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
/**
|
|
6079
|
+
* Force the backing store to revalidate its credential snapshot, then
|
|
6080
|
+
* reload. Remote broker stores re-fetch the snapshot; local stores are
|
|
6081
|
+
* always current, so only the reload runs. Callers that pair live
|
|
6082
|
+
* per-credential data with stored identities (`omp usage`) use this so a
|
|
6083
|
+
* disk-cached snapshot cannot misattribute fresh reports.
|
|
6084
|
+
*/
|
|
6085
|
+
async revalidateCredentials(): Promise<void> {
|
|
6086
|
+
if (this.#store.refreshSnapshot) await this.#store.refreshSnapshot();
|
|
6087
|
+
await this.reload();
|
|
6088
|
+
}
|
|
6089
|
+
|
|
5895
6090
|
/**
|
|
5896
6091
|
* Refresh the OAuth credential with the given id through a per-credential
|
|
5897
6092
|
* single-flight. Concurrent callers for the same row await the same upstream
|
|
@@ -5982,6 +6177,7 @@ export class AuthStorage {
|
|
|
5982
6177
|
apiEndpoint: refreshed.apiEndpoint ?? attempted.apiEndpoint,
|
|
5983
6178
|
orgId: refreshed.orgId ?? attempted.orgId,
|
|
5984
6179
|
orgName: refreshed.orgName ?? attempted.orgName,
|
|
6180
|
+
authorizedAt: refreshed.authorizedAt ?? attempted.authorizedAt,
|
|
5985
6181
|
};
|
|
5986
6182
|
// Persist by id: the array may have been reordered/shrunk while the
|
|
5987
6183
|
// refresh was in flight, so the pre-await positional index is unsafe. A
|
|
@@ -6069,6 +6265,14 @@ export class AuthStorage {
|
|
|
6069
6265
|
/**
|
|
6070
6266
|
* Broker-server seam: clear all persisted blocks for one credential and notify snapshot waiters.
|
|
6071
6267
|
*/
|
|
6268
|
+
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void {
|
|
6269
|
+
const deleteCredentialBlock = this.#store.deleteCredentialBlock?.bind(this.#store);
|
|
6270
|
+
if (!deleteCredentialBlock) return;
|
|
6271
|
+
deleteCredentialBlock(credentialId, providerKey, blockScope);
|
|
6272
|
+
this.#invalidateUsageReportCacheForProviderKey(providerKey);
|
|
6273
|
+
this.#bumpGeneration("credential-block");
|
|
6274
|
+
}
|
|
6275
|
+
|
|
6072
6276
|
deleteCredentialBlocks(credentialId: number): void {
|
|
6073
6277
|
const deleteCredentialBlocks = this.#store.deleteCredentialBlocks?.bind(this.#store);
|
|
6074
6278
|
if (!deleteCredentialBlocks) return;
|
|
@@ -6152,6 +6356,9 @@ type AuthRow = {
|
|
|
6152
6356
|
identity_key: string | null;
|
|
6153
6357
|
};
|
|
6154
6358
|
|
|
6359
|
+
/** {@link AuthRow} plus `updated_at` — disabled-tombstone queries surface when the row was torn down. */
|
|
6360
|
+
type DisabledAuthRow = AuthRow & { updated_at: number | null };
|
|
6361
|
+
|
|
6155
6362
|
type CredentialBlockRow = {
|
|
6156
6363
|
credential_id: number;
|
|
6157
6364
|
provider_key: string;
|
|
@@ -6426,6 +6633,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6426
6633
|
#db: Database;
|
|
6427
6634
|
#listActiveStmt: Statement;
|
|
6428
6635
|
#listActiveByProviderStmt: Statement;
|
|
6636
|
+
#listDisabledStmt: Statement;
|
|
6429
6637
|
#listDisabledByProviderStmt: Statement;
|
|
6430
6638
|
#insertStmt: Statement;
|
|
6431
6639
|
#updateStmt: Statement;
|
|
@@ -6445,6 +6653,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6445
6653
|
#listCredentialBlocksByCredentialStmt: Statement;
|
|
6446
6654
|
#upsertCredentialBlockStmt: Statement;
|
|
6447
6655
|
#deleteCredentialBlocksStmt: Statement;
|
|
6656
|
+
#deleteCredentialBlockStmt: Statement;
|
|
6448
6657
|
#deleteExpiredCredentialBlocksStmt: Statement;
|
|
6449
6658
|
#acquireCredentialRefreshLeaseStmt: Statement;
|
|
6450
6659
|
#getCredentialRefreshLeaseStmt: Statement;
|
|
@@ -6469,8 +6678,11 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6469
6678
|
this.#listActiveByProviderStmt = this.#db.prepare(
|
|
6470
6679
|
"SELECT id, provider, credential_type, data, disabled_cause, identity_key FROM auth_credentials WHERE provider = ? AND disabled_cause IS NULL ORDER BY id ASC",
|
|
6471
6680
|
);
|
|
6681
|
+
this.#listDisabledStmt = this.#db.prepare(
|
|
6682
|
+
"SELECT id, provider, credential_type, data, disabled_cause, identity_key, updated_at FROM auth_credentials WHERE disabled_cause IS NOT NULL ORDER BY id ASC",
|
|
6683
|
+
);
|
|
6472
6684
|
this.#listDisabledByProviderStmt = this.#db.prepare(
|
|
6473
|
-
"SELECT id, provider, credential_type, data, disabled_cause, identity_key FROM auth_credentials WHERE provider = ? AND disabled_cause IS NOT NULL ORDER BY id ASC",
|
|
6685
|
+
"SELECT id, provider, credential_type, data, disabled_cause, identity_key, updated_at FROM auth_credentials WHERE provider = ? AND disabled_cause IS NOT NULL ORDER BY id ASC",
|
|
6474
6686
|
);
|
|
6475
6687
|
this.#insertStmt = this.#db.prepare(
|
|
6476
6688
|
`INSERT INTO auth_credentials (provider, credential_type, data, identity_key, created_at, updated_at) VALUES (?, ?, ?, ?, ${SQLITE_NOW_EPOCH}, ${SQLITE_NOW_EPOCH}) RETURNING id`,
|
|
@@ -6532,6 +6744,9 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6532
6744
|
updated_at = excluded.updated_at`,
|
|
6533
6745
|
);
|
|
6534
6746
|
this.#deleteCredentialBlocksStmt = this.#db.prepare("DELETE FROM auth_credential_blocks WHERE credential_id = ?");
|
|
6747
|
+
this.#deleteCredentialBlockStmt = this.#db.prepare(
|
|
6748
|
+
"DELETE FROM auth_credential_blocks WHERE credential_id = ? AND provider_key = ? AND block_scope = ?",
|
|
6749
|
+
);
|
|
6535
6750
|
this.#deleteExpiredCredentialBlocksStmt = this.#db.prepare(
|
|
6536
6751
|
"DELETE FROM auth_credential_blocks WHERE blocked_until_ms <= ?",
|
|
6537
6752
|
);
|
|
@@ -6979,6 +7194,34 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
6979
7194
|
return results;
|
|
6980
7195
|
}
|
|
6981
7196
|
|
|
7197
|
+
async listDisabledCredentials(provider?: string): Promise<DisabledCredentialSummary[]> {
|
|
7198
|
+
const rows =
|
|
7199
|
+
(provider
|
|
7200
|
+
? (this.#listDisabledByProviderStmt.all(provider) as DisabledAuthRow[])
|
|
7201
|
+
: (this.#listDisabledStmt.all() as DisabledAuthRow[])) ?? [];
|
|
7202
|
+
const results: DisabledCredentialSummary[] = [];
|
|
7203
|
+
for (const row of rows) {
|
|
7204
|
+
const credential = deserializeCredential(row);
|
|
7205
|
+
const summary: DisabledCredentialSummary = {
|
|
7206
|
+
id: row.id,
|
|
7207
|
+
provider: row.provider,
|
|
7208
|
+
type: row.credential_type === "api_key" ? "api_key" : "oauth",
|
|
7209
|
+
cause: row.disabled_cause ?? "disabled",
|
|
7210
|
+
};
|
|
7211
|
+
if (credential?.type === "oauth") {
|
|
7212
|
+
if (credential.email) summary.email = credential.email;
|
|
7213
|
+
if (credential.accountId) summary.accountId = credential.accountId;
|
|
7214
|
+
if (credential.orgId) summary.orgId = credential.orgId;
|
|
7215
|
+
if (credential.orgName) summary.orgName = credential.orgName;
|
|
7216
|
+
}
|
|
7217
|
+
if (typeof row.updated_at === "number" && Number.isFinite(row.updated_at)) {
|
|
7218
|
+
summary.disabledAtMs = row.updated_at * 1000;
|
|
7219
|
+
}
|
|
7220
|
+
results.push(summary);
|
|
7221
|
+
}
|
|
7222
|
+
return results;
|
|
7223
|
+
}
|
|
7224
|
+
|
|
6982
7225
|
replaceAuthCredentialsForProvider(provider: string, credentials: AuthCredential[]): StoredAuthCredential[] {
|
|
6983
7226
|
const replace = this.#db.transaction((providerName: string, items: AuthCredential[]) => {
|
|
6984
7227
|
const existingRows = this.#listActiveByProviderStmt.all(providerName) as AuthRow[];
|
|
@@ -7296,6 +7539,11 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
7296
7539
|
);
|
|
7297
7540
|
}
|
|
7298
7541
|
|
|
7542
|
+
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void {
|
|
7543
|
+
this.#deleteCredentialBlockStmt.run(credentialId, providerKey, blockScope);
|
|
7544
|
+
this.#credentialBlockReconcileAfter.delete(`${credentialId}\0${providerKey}\0${blockScope}`);
|
|
7545
|
+
}
|
|
7546
|
+
|
|
7299
7547
|
deleteCredentialBlocks(credentialId: number): void {
|
|
7300
7548
|
this.#deleteCredentialBlocksStmt.run(credentialId);
|
|
7301
7549
|
for (const key of this.#credentialBlockReconcileAfter.keys()) {
|
|
@@ -7651,6 +7899,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
7651
7899
|
this.#closed = true;
|
|
7652
7900
|
this.#listActiveStmt.finalize();
|
|
7653
7901
|
this.#listActiveByProviderStmt.finalize();
|
|
7902
|
+
this.#listDisabledStmt.finalize();
|
|
7654
7903
|
this.#listDisabledByProviderStmt.finalize();
|
|
7655
7904
|
this.#insertStmt.finalize();
|
|
7656
7905
|
this.#updateStmt.finalize();
|
|
@@ -7666,6 +7915,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
7666
7915
|
this.#listCredentialBlocksByCredentialStmt.finalize();
|
|
7667
7916
|
this.#upsertCredentialBlockStmt.finalize();
|
|
7668
7917
|
this.#deleteCredentialBlocksStmt.finalize();
|
|
7918
|
+
this.#deleteCredentialBlockStmt.finalize();
|
|
7669
7919
|
this.#deleteExpiredCredentialBlocksStmt.finalize();
|
|
7670
7920
|
this.#insertUsageHistoryStmt.finalize();
|
|
7671
7921
|
this.#lastUsageHistoryStmt.finalize();
|
package/src/error/flags.ts
CHANGED
|
@@ -90,7 +90,7 @@ const TRANSIENT_ENVELOPE_PATTERN = /anthropic stream envelope error:/i;
|
|
|
90
90
|
const TRANSIENT_ENVELOPE_BEFORE_START_PATTERN = /before message_start/i;
|
|
91
91
|
export const STREAM_READ_ERROR_PATTERN = /stream[_ -]?read[_ -]?error/i;
|
|
92
92
|
export const TRANSIENT_TRANSPORT_PATTERN =
|
|
93
|
-
|
|
93
|
+
/\b(?:no[_ -]?capacity|(?:high|peak)[ _-]?demand|(?:at|over|insufficient)[ _-]?capacity|capacity[ _-]?(?:exceeded|exhausted)|peak[ _-]?load)\b|overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|retry your request|network.?error|connection.?error|connection.?refused|unable.?to.?connect\.\s*is the computer able to access the url\?|other side closed|fetch failed|upstream.?connect|upstream.?request.?failed|reset before headers|socket hang up|timed? out|timeout|terminated|retry delay|stream stall|no error details in response|HTTP2(?:StreamReset|RefusedStream|EnhanceYourCalm)|malformed.?function.?call/i;
|
|
94
94
|
const AUTH_FAILURE_PATTERN =
|
|
95
95
|
/\b(?:401|403|unauthorized|forbidden|authentication|auth[_ ]?unavailable|no auth available|(?:invalid|no)[_ ]?api[_ ]?key)\b/i;
|
|
96
96
|
const MALFORMED_FUNCTION_CALL_PATTERN = /\bmalformed.?function.?call\b/i;
|