@oh-my-pi/pi-ai 13.3.13 → 13.4.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/CHANGELOG.md +140 -0
- package/package.json +10 -2
- package/src/auth-storage.ts +207 -29
- package/src/index.ts +1 -1
- package/src/models.json +489 -312
- package/src/provider-models/openai-compat.ts +2 -1
- package/src/providers/amazon-bedrock.ts +8 -9
- package/src/providers/anthropic.ts +214 -102
- package/src/providers/azure-openai-responses.ts +7 -8
- package/src/providers/google-gemini-cli.ts +223 -44
- package/src/providers/google-shared.ts +11 -462
- package/src/providers/google-vertex.ts +1 -2
- package/src/providers/google.ts +1 -5
- package/src/providers/openai-codex-responses.ts +9 -12
- package/src/providers/openai-completions.ts +8 -11
- package/src/providers/openai-responses.ts +7 -10
- package/src/types.ts +1 -2
- package/src/usage/claude.ts +13 -2
- package/src/usage/openai-codex.ts +31 -0
- package/src/usage.ts +16 -0
- package/src/utils/discovery/antigravity.ts +77 -76
- package/src/utils/discovery/codex.ts +3 -3
- package/src/utils/discovery/openai-compatible.ts +2 -2
- package/src/utils/oauth/anthropic.ts +16 -5
- package/src/utils/oauth/callback-server.ts +1 -1
- package/src/utils/oauth/cursor.ts +1 -1
- package/src/utils/oauth/google-antigravity.ts +108 -47
- package/src/utils/oauth/google-gemini-cli.ts +0 -11
- package/src/utils/oauth/index.ts +13 -4
- package/src/utils/schema/CONSTRAINTS.md +160 -0
- package/src/utils/schema/adapt.ts +20 -0
- package/src/utils/schema/compatibility.ts +397 -0
- package/src/utils/schema/equality.ts +93 -0
- package/src/utils/schema/fields.ts +147 -0
- package/src/utils/schema/index.ts +8 -0
- package/src/utils/schema/normalize-cca.ts +479 -0
- package/src/utils/schema/sanitize-google.ts +207 -0
- package/src/utils/schema/strict-mode.ts +353 -0
- package/src/utils/schema/types.ts +5 -0
- package/src/utils/sanitize-unicode.ts +0 -25
- package/src/utils/typebox-helpers.ts +0 -261
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,127 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.4.0] - 2026-03-01
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Removed `TInput` generic parameter from `ToolResultMessage` interface and removed `$normative` property
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `hasUnrepresentableStrictObjectMap()` pre-flight check in `tryEnforceStrictSchema`: schemas with `patternProperties` or schema-valued `additionalProperties` now degrade gracefully to non-strict mode instead of throwing during enforcement
|
|
14
|
+
- `generateClaudeCloakingUserId()` generates structured user IDs for Anthropic OAuth metadata (`user_{hex64}_account_{uuid}_session_{uuid}`)
|
|
15
|
+
- `isClaudeCloakingUserId()` validates whether a string matches the cloaking user-ID format
|
|
16
|
+
- `mapStainlessOs()` and `mapStainlessArch()` map `process.platform`/`process.arch` to Stainless header values; X-Stainless-Os and X-Stainless-Arch in `claudeCodeHeaders` are now runtime-computed
|
|
17
|
+
- `buildClaudeCodeTlsFetchOptions()` attaches SNI and default TLS ciphers for direct `api.anthropic.com` connections
|
|
18
|
+
- `createClaudeBillingHeader()` generates the `x-anthropic-billing-header` block (SHA-256 payload fingerprint + random build hash)
|
|
19
|
+
- `buildAnthropicSystemBlocks()` now injects a billing header block and the Claude Agent SDK identity block with `ephemeral` 1h cache-control when `includeClaudeCodeInstruction` is set
|
|
20
|
+
- `resolveAnthropicMetadataUserId()` auto-generates a cloaking user ID for OAuth requests when `metadata.user_id` is absent or invalid
|
|
21
|
+
- `AnthropicOAuthFlow` is now exported for direct use
|
|
22
|
+
- OAuth callback server timeout extended from 2 min to 5 min
|
|
23
|
+
- `parseGeminiCliCredentials()` parses Google Cloud credential JSON with support for legacy (`{token,projectId}`), alias (`project_id`/`refresh`/`expires`), and enriched formats
|
|
24
|
+
- `shouldRefreshGeminiCliCredentials()` and proactive token refresh before requests for both Gemini CLI and Antigravity providers (60s pre-expiry buffer)
|
|
25
|
+
- `normalizeAntigravityTools()` converts `parametersJsonSchema` → `parameters` in function declarations for Antigravity compatibility
|
|
26
|
+
- `ANTIGRAVITY_SYSTEM_INSTRUCTION` is now exported for use by search and other consumers
|
|
27
|
+
- `ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA` constant exported from OAuth module with `ANTIGRAVITY` ideType
|
|
28
|
+
- Antigravity project onboarding: `onboardProjectWithRetries()` provisions a new project via `onboardUser` LRO when `loadCodeAssist` returns no existing project (up to 5 attempts, 2s interval)
|
|
29
|
+
- `getOAuthApiKey` now includes `refreshToken`, `expiresAt`, `email`, and `accountId` in the Gemini/Antigravity JSON credential payload to enable proactive refresh
|
|
30
|
+
- Antigravity model discovery now tries the production daily endpoint first, with sandbox as fallback
|
|
31
|
+
- `ANTIGRAVITY_DISCOVERY_DENYLIST` filters low-quality/internal models from discovery results
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Replaced `sanitizeSurrogates()` utility with native `String.prototype.toWellFormed()` for handling unpaired Unicode surrogates across all providers
|
|
36
|
+
- Extended `ANTHROPIC_OAUTH_BETA` constant in the OpenAI-compat Anthropic route with `interleaved-thinking-2025-05-14`, `context-management-2025-06-27`, and `prompt-caching-scope-2026-01-05` beta flags
|
|
37
|
+
- `claudeCodeVersion` bumped to `2.1.63`; `claudeCodeSystemInstruction` updated to identify as Claude Agent SDK
|
|
38
|
+
- `claudeCodeHeaders`: removed `X-Stainless-Helper-Method`, updated package version to `0.74.0`, runtime version to `v24.3.0`
|
|
39
|
+
- `applyClaudeToolPrefix` / `stripClaudeToolPrefix` now accept an optional prefix override and skip Anthropic built-in tool names (`web_search`, `code_execution`, `text_editor`, `computer`)
|
|
40
|
+
- Accept-Encoding header updated to `gzip, deflate, br, zstd`
|
|
41
|
+
- Non-Anthropic base URLs now receive `Authorization: Bearer` regardless of OAuth status
|
|
42
|
+
- Prompt-caching logic now skips applying breakpoints when any block already carries `cache_control`, instead of stripping then re-applying
|
|
43
|
+
- `fine-grained-tool-streaming-2025-05-14` removed from default beta set
|
|
44
|
+
- Anthropic OAuth token URL changed from `platform.claude.com` to `api.anthropic.com`
|
|
45
|
+
- Anthropic OAuth scopes reduced to `org:create_api_key user:profile user:inference`
|
|
46
|
+
- OAuth code exchange now strips URL fragment from callback code, using the fragment as state override when present
|
|
47
|
+
- Claude usage headers aligned: user-agent updated to `claude-cli/2.1.63 (external, cli)`, anthropic-beta extended with full beta set
|
|
48
|
+
- Antigravity session ID format changed to signed decimal (negative int63 derived from SHA-256 of first user message, or random bounded int63)
|
|
49
|
+
- Antigravity `requestId` now uses `agent-{uuid}` format; non-Antigravity requests no longer include requestId/userAgent/requestType in the payload
|
|
50
|
+
- `ANTIGRAVITY_DAILY_ENDPOINT` corrected to `daily-cloudcode-pa.googleapis.com`; sandbox endpoint kept as fallback only
|
|
51
|
+
- Antigravity discovery: removed `recommended`/`agentModelSorts` filter; now includes all non-internal, non-denylisted models
|
|
52
|
+
- Antigravity discovery no longer sends `project` in the request body
|
|
53
|
+
- Gemini/Antigravity OAuth flows no longer use PKCE (code_challenge removed)
|
|
54
|
+
- Antigravity `loadCodeAssist` metadata ideType changed from `IDE_UNSPECIFIED` to `ANTIGRAVITY`
|
|
55
|
+
- Antigravity `discoverProject` now uses a single canonical production endpoint; falls back to project onboarding instead of a hardcoded default project ID
|
|
56
|
+
- `VALIDATED` tool calling config applied to Antigravity requests with Claude models
|
|
57
|
+
- `maxOutputTokens` removed from Antigravity generation config for non-Claude models
|
|
58
|
+
- System instruction injection for Antigravity scoped to Claude and `gemini-3-pro-high` models only
|
|
59
|
+
|
|
60
|
+
### Removed
|
|
61
|
+
|
|
62
|
+
- Removed `sanitizeSurrogates()` utility function; use native `String.prototype.toWellFormed()` instead
|
|
63
|
+
|
|
64
|
+
## [13.3.14] - 2026-02-28
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Exported schema utilities from new `./utils/schema` module, consolidating JSON Schema handling across providers
|
|
69
|
+
- Added `CredentialRankingStrategy` interface for providers to implement usage-based credential selection
|
|
70
|
+
- Added `claudeRankingStrategy` for Anthropic OAuth credentials to enable smart multi-account selection based on usage windows
|
|
71
|
+
- Added `codexRankingStrategy` for OpenAI Codex OAuth credentials with priority boost for fresh 5-hour window starts
|
|
72
|
+
- Added `adaptSchemaForStrict()` helper for unified OpenAI strict schema enforcement across providers
|
|
73
|
+
- Added schema equality and merging utilities: `areJsonValuesEqual()`, `mergeCompatibleEnumSchemas()`, `mergePropertySchemas()`
|
|
74
|
+
- Added Cloud Code Assist schema normalization: `copySchemaWithout()`, `stripResidualCombiners()`, `prepareSchemaForCCA()`
|
|
75
|
+
- Added `sanitizeSchemaForGoogle()` and `sanitizeSchemaForCCA()` for provider-specific schema sanitization
|
|
76
|
+
- Added `StringEnum()` helper for creating string enum schemas compatible with Google and other providers
|
|
77
|
+
- Added `enforceStrictSchema()` and `sanitizeSchemaForStrictMode()` for OpenAI strict mode schema validation
|
|
78
|
+
- Added package exports for `./utils/schema` and `./utils/schema/*` subpaths
|
|
79
|
+
- Added `validateSchemaCompatibility()` to statically audit a JSON Schema against provider-specific rules (`openai-strict`, `google`, `cloud-code-assist-claude`) and return structured violations
|
|
80
|
+
- Added `validateStrictSchemaEnforcement()` to verify the strict-fail-open contract: enforced schemas pass strict validation, failed schemas return the original object identity
|
|
81
|
+
- Added `COMBINATOR_KEYS` (`anyOf`, `allOf`, `oneOf`) and `CCA_UNSUPPORTED_SCHEMA_FIELDS` as exported constants in `fields.ts` to eliminate duplication across modules
|
|
82
|
+
- Added `tryEnforceStrictSchema` result cache (`WeakMap`) to avoid redundant sanitize + enforce work for the same schema object
|
|
83
|
+
- Added comprehensive schema normalization test suite (`schema-normalization.test.ts`) covering strict mode, Google, and Cloud Code Assist normalization paths
|
|
84
|
+
- Added schema compatibility validation test suite (`schema-compatibility.test.ts`) covering all three provider targets
|
|
85
|
+
|
|
86
|
+
### Changed
|
|
87
|
+
|
|
88
|
+
- Moved schema utilities from `./utils/typebox-helpers` to new `./utils/schema` module with expanded functionality
|
|
89
|
+
- Refactored OpenAI provider tool conversion to use unified `adaptSchemaForStrict()` helper across codex, completions, and responses
|
|
90
|
+
- Updated `AuthStorage` to support generic credential ranking via `CredentialRankingStrategy` instead of Codex-only logic
|
|
91
|
+
- Moved Google schema sanitization functions from `google-shared.ts` to `./utils/schema` module
|
|
92
|
+
- Changed export path: `./utils/typebox-helpers` → `./utils/schema` in main index
|
|
93
|
+
- `sanitizeSchemaForGoogle()` / `sanitizeSchemaForCCA()` now accept a parameterized `unsupportedFields` set internally, enabling code reuse between the two sanitizers
|
|
94
|
+
- `copySchemaWithout()` rewritten using object-rest destructuring for clarity
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- Fixed cycle detection: `WeakSet` guards added to all recursive schema traversals (`sanitizeSchemaForStrictMode`, `enforceStrictSchema`, `normalizeSchemaForCCA`, `normalizeNullablePropertiesForCloudCodeAssist`, `stripResidualCombiners`, `sanitizeSchemaImpl`, `hasResidualCloudCodeAssistIncompatibilities`) — circular schemas no longer cause infinite loops or stack overflows
|
|
99
|
+
- Fixed `hasResidualCloudCodeAssistIncompatibilities`: cycle detection now returns `false` (not `true`) for already-visited nodes, eliminating false positives that forced the CCA fallback schema on valid recursive inputs
|
|
100
|
+
- Fixed `stripResidualCombiners` to iterate to a fixpoint rather than making a single pass, ensuring chained combiner reductions (where one reduction enables another) are fully resolved
|
|
101
|
+
- Fixed `mergeObjectCombinerVariants` required-field computation: the flattened object now takes the intersection of all variants' `required` arrays (unioned with own-level required properties that exist in the merged schema), preventing required fields from being silently dropped or over-included
|
|
102
|
+
- Fixed `mergeCompatibleEnumSchemas` to use deep structural equality (`areJsonValuesEqual`) instead of `Object.is` when deduplicating object-valued enum members
|
|
103
|
+
- Fixed `sanitizeSchemaForGoogle` const-to-enum deduplication to use deep equality instead of reference equality
|
|
104
|
+
- Fixed `sanitizeSchemaForGoogle` type inference for `anyOf`/`oneOf`-flattened const enums: type is now derived from all variants (must agree), falling back to inference from enum values; mixed null/non-null infers the non-null type and sets `nullable`
|
|
105
|
+
- Fixed `sanitizeSchemaForGoogle` recursion to spread options when descending (previously only `insideProperties`, `normalizeTypeArrayToNullable`, `stripNullableKeyword` were forwarded; new fields `unsupportedFields` and `seen` were silently dropped)
|
|
106
|
+
- Fixed `sanitizeSchemaForGoogle` array-valued `type` filtering to exclude non-string entries before processing
|
|
107
|
+
- Removed incorrect `additionalProperties: false` stripping from `sanitizeSchemaForGoogle` (the field is valid in Google schemas when `false`)
|
|
108
|
+
- Fixed `sanitizeSchemaForStrictMode` to strip the `nullable` keyword and expand it into `anyOf: [schema, {type: "null"}]` in the output, matching what OpenAI strict mode actually expects
|
|
109
|
+
- Fixed `sanitizeSchemaForStrictMode` to infer `type: "array"` when `items` is present but `type` is absent
|
|
110
|
+
- Fixed `sanitizeSchemaForStrictMode` to infer a scalar `type` from uniform `enum` values when `type` is not explicitly set
|
|
111
|
+
- Fixed `sanitizeSchemaForStrictMode` const-to-enum merge to use deep equality, preventing duplicate enum entries when `const` and `enum` both exist with the same value
|
|
112
|
+
- Fixed `enforceStrictSchema` to drop `additionalProperties` unconditionally (previously only object-valued `additionalProperties` was recursed into; non-object values were passed through, violating strict schema requirements)
|
|
113
|
+
- Fixed `enforceStrictSchema` to recurse into `$defs` and `definitions` blocks so referenced sub-schemas are also made strict-compliant
|
|
114
|
+
- Fixed `enforceStrictSchema` to handle tuple-style `items` arrays (previously only single-schema `items` objects were recursed)
|
|
115
|
+
- Fixed `enforceStrictSchema` double-wrapping: optional properties already expressed as `anyOf: [..., {type: "null"}]` are not wrapped again
|
|
116
|
+
- Fixed `enforceStrictSchema` `Array.isArray` type-narrowing for `type` field to filter non-string entries before checking for `"object"`
|
|
117
|
+
|
|
5
118
|
## [13.3.8] - 2026-02-28
|
|
119
|
+
|
|
6
120
|
### Fixed
|
|
7
121
|
|
|
8
122
|
- Fixed response body reuse error when handling 429 rate limit responses with retry logic
|
|
9
123
|
|
|
10
124
|
## [13.3.7] - 2026-02-27
|
|
125
|
+
|
|
11
126
|
### Added
|
|
12
127
|
|
|
13
128
|
- Added `tryEnforceStrictSchema` function that gracefully downgrades to non-strict mode when schema enforcement fails, enabling better compatibility with malformed or circular schemas
|
|
@@ -25,6 +140,7 @@
|
|
|
25
140
|
- Fixed `enforceStrictSchema` to correctly process nested object schemas within `anyOf`, `allOf`, and `oneOf` combinators
|
|
26
141
|
|
|
27
142
|
## [13.3.1] - 2026-02-26
|
|
143
|
+
|
|
28
144
|
### Added
|
|
29
145
|
|
|
30
146
|
- Added `topP`, `topK`, `minP`, `presencePenalty`, and `repetitionPenalty` options to `StreamOptions` for fine-grained control over model sampling behavior
|
|
@@ -32,8 +148,11 @@
|
|
|
32
148
|
## [13.3.0] - 2026-02-26
|
|
33
149
|
|
|
34
150
|
### Changed
|
|
151
|
+
|
|
35
152
|
- Allowed OAuth provider logins to supply a manual authorization code handler with a default prompt when none is provided
|
|
153
|
+
|
|
36
154
|
## [13.2.0] - 2026-02-23
|
|
155
|
+
|
|
37
156
|
### Added
|
|
38
157
|
|
|
39
158
|
- Added support for GitHub Copilot provider in strict mode for both openai-completions and openai-responses tool schemas
|
|
@@ -43,6 +162,7 @@
|
|
|
43
162
|
- Fixed tool descriptions being rejected when undefined by providing empty string fallback across all providers
|
|
44
163
|
|
|
45
164
|
## [12.19.1] - 2026-02-22
|
|
165
|
+
|
|
46
166
|
### Added
|
|
47
167
|
|
|
48
168
|
- Exported `isProviderRetryableError` function for detecting rate-limit and transient stream errors
|
|
@@ -53,6 +173,7 @@
|
|
|
53
173
|
- Expanded retry detection to include JSON parse errors (unterminated strings, unexpected end of input) in addition to rate-limit errors
|
|
54
174
|
|
|
55
175
|
## [12.19.0] - 2026-02-22
|
|
176
|
+
|
|
56
177
|
### Added
|
|
57
178
|
|
|
58
179
|
- Added GitLab Duo provider with support for Claude, GPT-5, and other models via GitLab AI Gateway
|
|
@@ -78,6 +199,7 @@
|
|
|
78
199
|
- Removed `CliAuthStorage` class in favor of new `AuthCredentialStore` with enhanced functionality
|
|
79
200
|
|
|
80
201
|
## [12.17.2] - 2026-02-21
|
|
202
|
+
|
|
81
203
|
### Added
|
|
82
204
|
|
|
83
205
|
- Exported `getAntigravityUserAgent()` function for constructing Antigravity User-Agent headers
|
|
@@ -88,6 +210,7 @@
|
|
|
88
210
|
- Unified User-Agent header generation across Antigravity API calls to use centralized `getAntigravityUserAgent()` function
|
|
89
211
|
|
|
90
212
|
## [12.17.1] - 2026-02-21
|
|
213
|
+
|
|
91
214
|
### Added
|
|
92
215
|
|
|
93
216
|
- Added new export paths for provider models via `./provider-models` and `./provider-models/*`
|
|
@@ -102,10 +225,13 @@
|
|
|
102
225
|
- Reorganized package.json field ordering for improved readability
|
|
103
226
|
|
|
104
227
|
## [12.17.0] - 2026-02-21
|
|
228
|
+
|
|
105
229
|
### Fixed
|
|
230
|
+
|
|
106
231
|
- Cursor provider: bind `execHandlers` when passing handler methods to the exec protocol so handlers receive correct `this` context (fixes "undefined is not an object (evaluating 'this.options')" when using exec tools such as web search with Cursor)
|
|
107
232
|
|
|
108
233
|
## [12.16.0] - 2026-02-21
|
|
234
|
+
|
|
109
235
|
### Added
|
|
110
236
|
|
|
111
237
|
- Exported `readModelCache` and `writeModelCache` functions for direct SQLite-backed model cache access
|
|
@@ -123,6 +249,7 @@
|
|
|
123
249
|
- Updated tool call tracking to use status map (Resolved/Aborted) instead of separate sets for better handling of duplicate and aborted tool results
|
|
124
250
|
|
|
125
251
|
## [12.15.0] - 2026-02-20
|
|
252
|
+
|
|
126
253
|
### Fixed
|
|
127
254
|
|
|
128
255
|
- Improved error messages for OAuth token refresh failures by including detailed error information from the provider
|
|
@@ -134,6 +261,7 @@
|
|
|
134
261
|
- Changed 429 retry strategy for OpenAI Codex and Google Gemini CLI to use a 5-minute time budget when the server provides a retry delay, instead of a fixed attempt cap
|
|
135
262
|
|
|
136
263
|
## [12.14.0] - 2026-02-19
|
|
264
|
+
|
|
137
265
|
### Added
|
|
138
266
|
|
|
139
267
|
- Added `gemini-3.1-pro` model to opencode provider with text and image input support
|
|
@@ -161,6 +289,7 @@
|
|
|
161
289
|
- Added NanoGPT provider support with API-key login, dynamic model discovery from `https://nano-gpt.com/api/v1/models`, and text-model filtering for catalog/runtime discovery ([#111](https://github.com/can1357/oh-my-pi/issues/111))
|
|
162
290
|
|
|
163
291
|
## [12.12.3] - 2026-02-19
|
|
292
|
+
|
|
164
293
|
### Fixed
|
|
165
294
|
|
|
166
295
|
- Fixed retry logic to recognize 'unable to connect' errors as transient failures
|
|
@@ -173,6 +302,7 @@
|
|
|
173
302
|
- Fixed Codex websocket append fallback by resetting stale turn-state/model-etag session metadata when request shape diverges from appendable history.
|
|
174
303
|
|
|
175
304
|
## [12.11.1] - 2026-02-19
|
|
305
|
+
|
|
176
306
|
### Added
|
|
177
307
|
|
|
178
308
|
- Added support for Claude 4.6 Opus and Sonnet models via Cursor API
|
|
@@ -224,6 +354,7 @@
|
|
|
224
354
|
- Updated README documentation to list all newly supported providers and their authentication requirements
|
|
225
355
|
|
|
226
356
|
## [12.10.1] - 2026-02-18
|
|
357
|
+
|
|
227
358
|
- Added Synthetic provider
|
|
228
359
|
- Added API-key login helpers for Synthetic and Cerebras providers
|
|
229
360
|
|
|
@@ -279,6 +410,7 @@
|
|
|
279
410
|
- Updated Qwen model context window and max token limits for improved accuracy
|
|
280
411
|
|
|
281
412
|
## [12.7.0] - 2026-02-16
|
|
413
|
+
|
|
282
414
|
### Added
|
|
283
415
|
|
|
284
416
|
- Added DeepSeek-V3.2 model support via Amazon Bedrock
|
|
@@ -391,6 +523,7 @@
|
|
|
391
523
|
- Added deprecation filter in model generation script to prevent re-adding deprecated Anthropic models ([#33](https://github.com/can1357/oh-my-pi/issues/33))
|
|
392
524
|
|
|
393
525
|
## [11.14.1] - 2026-02-12
|
|
526
|
+
|
|
394
527
|
### Added
|
|
395
528
|
|
|
396
529
|
- Added prompt-caching-scope-2026-01-05 beta feature support
|
|
@@ -410,6 +543,7 @@
|
|
|
410
543
|
- Removed fine-grained-tool-streaming-2025-05-14 beta feature
|
|
411
544
|
|
|
412
545
|
## [11.13.1] - 2026-02-12
|
|
546
|
+
|
|
413
547
|
### Added
|
|
414
548
|
|
|
415
549
|
- Added Perplexity (Pro/Max) OAuth login support via native macOS app extraction or email OTP authentication
|
|
@@ -417,6 +551,7 @@
|
|
|
417
551
|
- Added Socket.IO v4 client implementation for authenticated WebSocket communication with Perplexity API
|
|
418
552
|
|
|
419
553
|
## [11.12.0] - 2026-02-11
|
|
554
|
+
|
|
420
555
|
### Changed
|
|
421
556
|
|
|
422
557
|
- Increased maximum retry attempts for Codex requests from 2 to 5 to improve reliability on transient failures
|
|
@@ -444,6 +579,7 @@
|
|
|
444
579
|
- Updated `@anthropic-ai/sdk` dependency from ^0.72.1 to ^0.74.0
|
|
445
580
|
|
|
446
581
|
## [11.10.0] - 2026-02-10
|
|
582
|
+
|
|
447
583
|
### Added
|
|
448
584
|
|
|
449
585
|
- Added support for Kimi K2, K2 Turbo Preview, and K2.5 models with reasoning capabilities
|
|
@@ -454,6 +590,7 @@
|
|
|
454
590
|
- Fixed Claude Sonnet 4 context window to 200K across multiple providers (was incorrectly set to 1M)
|
|
455
591
|
|
|
456
592
|
## [11.8.0] - 2026-02-10
|
|
593
|
+
|
|
457
594
|
### Added
|
|
458
595
|
|
|
459
596
|
- Added `auto` model alias for OpenRouter with automatic model routing
|
|
@@ -532,11 +669,13 @@
|
|
|
532
669
|
- Fixed Bedrock `supportsPromptCaching` to also check model cost fields
|
|
533
670
|
|
|
534
671
|
## [11.5.1] - 2026-02-07
|
|
672
|
+
|
|
535
673
|
### Fixed
|
|
536
674
|
|
|
537
675
|
- Fixed schema normalization to handle array-valued `type` fields by converting them to a single type with nullable flag for Google provider compatibility
|
|
538
676
|
|
|
539
677
|
## [11.3.0] - 2026-02-06
|
|
678
|
+
|
|
540
679
|
### Added
|
|
541
680
|
|
|
542
681
|
- Added `cacheRetention` option to control prompt cache retention preference ('none', 'short', 'long') across providers
|
|
@@ -562,6 +701,7 @@
|
|
|
562
701
|
- Fixed handling of conversations ending with assistant messages on Anthropic-routed models that reject assistant prefill requests
|
|
563
702
|
|
|
564
703
|
## [11.2.3] - 2026-02-05
|
|
704
|
+
|
|
565
705
|
### Added
|
|
566
706
|
|
|
567
707
|
- Added Claude Opus 4.6 model support across multiple providers (Anthropic, Amazon Bedrock, GitHub Copilot, OpenRouter, OpenCode, Vercel AI Gateway)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "13.
|
|
4
|
+
"version": "13.4.0",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@aws-sdk/client-bedrock-runtime": "^3.998",
|
|
42
42
|
"@bufbuild/protobuf": "^2.11",
|
|
43
43
|
"@google/genai": "^1.43",
|
|
44
|
-
"@oh-my-pi/pi-utils": "13.
|
|
44
|
+
"@oh-my-pi/pi-utils": "13.4.0",
|
|
45
45
|
"@sinclair/typebox": "^0.34",
|
|
46
46
|
"@smithy/node-http-handler": "^4.4",
|
|
47
47
|
"ajv": "^8.18",
|
|
@@ -117,6 +117,14 @@
|
|
|
117
117
|
"./utils/oauth/*": {
|
|
118
118
|
"types": "./src/utils/oauth/*.ts",
|
|
119
119
|
"import": "./src/utils/oauth/*.ts"
|
|
120
|
+
},
|
|
121
|
+
"./utils/schema": {
|
|
122
|
+
"types": "./src/utils/schema/index.ts",
|
|
123
|
+
"import": "./src/utils/schema/index.ts"
|
|
124
|
+
},
|
|
125
|
+
"./utils/schema/*": {
|
|
126
|
+
"types": "./src/utils/schema/*.ts",
|
|
127
|
+
"import": "./src/utils/schema/*.ts"
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
}
|
package/src/auth-storage.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { googleGeminiCliUsageProvider } from "./providers/google-gemini-cli-usag
|
|
|
15
15
|
import { getEnvApiKey } from "./stream";
|
|
16
16
|
import type { Provider } from "./types";
|
|
17
17
|
import type {
|
|
18
|
+
CredentialRankingStrategy,
|
|
18
19
|
UsageCache,
|
|
19
20
|
UsageCacheEntry,
|
|
20
21
|
UsageCredential,
|
|
@@ -23,11 +24,11 @@ import type {
|
|
|
23
24
|
UsageProvider,
|
|
24
25
|
UsageReport,
|
|
25
26
|
} from "./usage";
|
|
26
|
-
import { claudeUsageProvider } from "./usage/claude";
|
|
27
|
+
import { claudeRankingStrategy, claudeUsageProvider } from "./usage/claude";
|
|
27
28
|
import { githubCopilotUsageProvider } from "./usage/github-copilot";
|
|
28
29
|
import { antigravityUsageProvider } from "./usage/google-antigravity";
|
|
29
30
|
import { kimiUsageProvider } from "./usage/kimi";
|
|
30
|
-
import { openaiCodexUsageProvider } from "./usage/openai-codex";
|
|
31
|
+
import { codexRankingStrategy, openaiCodexUsageProvider } from "./usage/openai-codex";
|
|
31
32
|
import { zaiUsageProvider } from "./usage/zai";
|
|
32
33
|
import { getOAuthApiKey, getOAuthProvider } from "./utils/oauth";
|
|
33
34
|
// Re-export login functions so consumers of AuthStorage.login() have access
|
|
@@ -114,6 +115,7 @@ export interface StoredAuthCredential {
|
|
|
114
115
|
|
|
115
116
|
export type AuthStorageOptions = {
|
|
116
117
|
usageProviderResolver?: (provider: Provider) => UsageProvider | undefined;
|
|
118
|
+
rankingStrategyResolver?: (provider: Provider) => CredentialRankingStrategy | undefined;
|
|
117
119
|
usageCache?: UsageCache;
|
|
118
120
|
usageFetch?: typeof fetch;
|
|
119
121
|
usageNow?: () => number;
|
|
@@ -163,6 +165,15 @@ function resolveDefaultUsageProvider(provider: Provider): UsageProvider | undefi
|
|
|
163
165
|
return DEFAULT_USAGE_PROVIDER_MAP.get(provider);
|
|
164
166
|
}
|
|
165
167
|
|
|
168
|
+
const DEFAULT_RANKING_STRATEGIES = new Map<Provider, CredentialRankingStrategy>([
|
|
169
|
+
["openai-codex", codexRankingStrategy],
|
|
170
|
+
["anthropic", claudeRankingStrategy],
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
function resolveDefaultRankingStrategy(provider: Provider): CredentialRankingStrategy | undefined {
|
|
174
|
+
return DEFAULT_RANKING_STRATEGIES.get(provider);
|
|
175
|
+
}
|
|
176
|
+
|
|
166
177
|
function parseUsageCacheEntry(raw: string): UsageCacheEntry | undefined {
|
|
167
178
|
try {
|
|
168
179
|
const parsed = JSON.parse(raw) as { value?: UsageReport | null; expiresAt?: unknown };
|
|
@@ -228,6 +239,7 @@ export class AuthStorage {
|
|
|
228
239
|
/** Maps provider:type -> credentialIndex -> blockedUntilMs for temporary backoff. */
|
|
229
240
|
#credentialBackoff: Map<string, Map<number, number>> = new Map();
|
|
230
241
|
#usageProviderResolver?: (provider: Provider) => UsageProvider | undefined;
|
|
242
|
+
#rankingStrategyResolver?: (provider: Provider) => CredentialRankingStrategy | undefined;
|
|
231
243
|
#usageCache?: UsageCache;
|
|
232
244
|
#usageFetch: typeof fetch;
|
|
233
245
|
#usageNow: () => number;
|
|
@@ -240,6 +252,7 @@ export class AuthStorage {
|
|
|
240
252
|
this.#store = store;
|
|
241
253
|
this.#configValueResolver = options.configValueResolver ?? defaultConfigValueResolver;
|
|
242
254
|
this.#usageProviderResolver = options.usageProviderResolver ?? resolveDefaultUsageProvider;
|
|
255
|
+
this.#rankingStrategyResolver = options.rankingStrategyResolver ?? resolveDefaultRankingStrategy;
|
|
243
256
|
this.#usageCache = options.usageCache ?? new AuthStorageUsageCache(this.#store);
|
|
244
257
|
this.#usageFetch = options.usageFetch ?? fetch;
|
|
245
258
|
this.#usageNow = options.usageNow ?? Date.now;
|
|
@@ -499,20 +512,25 @@ export class AuthStorage {
|
|
|
499
512
|
return order;
|
|
500
513
|
}
|
|
501
514
|
|
|
502
|
-
/**
|
|
503
|
-
#
|
|
515
|
+
/** Returns block expiry timestamp for a credential, cleaning up expired entries. */
|
|
516
|
+
#getCredentialBlockedUntil(providerKey: string, credentialIndex: number): number | undefined {
|
|
504
517
|
const backoffMap = this.#credentialBackoff.get(providerKey);
|
|
505
|
-
if (!backoffMap) return
|
|
518
|
+
if (!backoffMap) return undefined;
|
|
506
519
|
const blockedUntil = backoffMap.get(credentialIndex);
|
|
507
|
-
if (!blockedUntil) return
|
|
520
|
+
if (!blockedUntil) return undefined;
|
|
508
521
|
if (blockedUntil <= Date.now()) {
|
|
509
522
|
backoffMap.delete(credentialIndex);
|
|
510
523
|
if (backoffMap.size === 0) {
|
|
511
524
|
this.#credentialBackoff.delete(providerKey);
|
|
512
525
|
}
|
|
513
|
-
return
|
|
526
|
+
return undefined;
|
|
514
527
|
}
|
|
515
|
-
return
|
|
528
|
+
return blockedUntil;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Checks if a credential is temporarily blocked due to usage limits. */
|
|
532
|
+
#isCredentialBlocked(providerKey: string, credentialIndex: number): boolean {
|
|
533
|
+
return this.#getCredentialBlockedUntil(providerKey, credentialIndex) !== undefined;
|
|
516
534
|
}
|
|
517
535
|
|
|
518
536
|
/** Marks a credential as blocked until the specified time. */
|
|
@@ -1273,7 +1291,7 @@ export class AuthStorage {
|
|
|
1273
1291
|
const now = this.#usageNow();
|
|
1274
1292
|
let blockedUntil = now + (options?.retryAfterMs ?? AuthStorage.#defaultBackoffMs);
|
|
1275
1293
|
|
|
1276
|
-
if (
|
|
1294
|
+
if (sessionCredential.type === "oauth" && this.#rankingStrategyResolver?.(provider)) {
|
|
1277
1295
|
const credential = this.#getCredentialsForProvider(provider)[sessionCredential.index];
|
|
1278
1296
|
if (credential?.type === "oauth") {
|
|
1279
1297
|
const report = await this.#getUsageReport(provider, credential, options);
|
|
@@ -1298,6 +1316,148 @@ export class AuthStorage {
|
|
|
1298
1316
|
return remainingCredentials.some(candidate => !this.#isCredentialBlocked(providerKey, candidate.index));
|
|
1299
1317
|
}
|
|
1300
1318
|
|
|
1319
|
+
#resolveWindowResetInMs(window: UsageLimit["window"], nowMs: number): number | undefined {
|
|
1320
|
+
if (!window) return undefined;
|
|
1321
|
+
if (typeof window.resetInMs === "number" && Number.isFinite(window.resetInMs)) {
|
|
1322
|
+
return window.resetInMs;
|
|
1323
|
+
}
|
|
1324
|
+
if (typeof window.resetsAt === "number" && Number.isFinite(window.resetsAt)) {
|
|
1325
|
+
return window.resetsAt - nowMs;
|
|
1326
|
+
}
|
|
1327
|
+
return undefined;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
#normalizeUsageFraction(limit: UsageLimit | undefined): number {
|
|
1331
|
+
const usedFraction = limit?.amount.usedFraction;
|
|
1332
|
+
if (typeof usedFraction !== "number" || !Number.isFinite(usedFraction)) {
|
|
1333
|
+
return 0.5;
|
|
1334
|
+
}
|
|
1335
|
+
return Math.min(Math.max(usedFraction, 0), 1);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/** Computes `usedFraction / elapsedHours` — consumption rate per hour within the current window. Lower drain rate = less pressure = preferred. */
|
|
1339
|
+
#computeWindowDrainRate(limit: UsageLimit | undefined, nowMs: number, fallbackDurationMs: number): number {
|
|
1340
|
+
const usedFraction = this.#normalizeUsageFraction(limit);
|
|
1341
|
+
const durationMs = limit?.window?.durationMs ?? fallbackDurationMs;
|
|
1342
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0) {
|
|
1343
|
+
return usedFraction;
|
|
1344
|
+
}
|
|
1345
|
+
const resetInMs = this.#resolveWindowResetInMs(limit?.window, nowMs);
|
|
1346
|
+
if (!Number.isFinite(resetInMs)) {
|
|
1347
|
+
return usedFraction;
|
|
1348
|
+
}
|
|
1349
|
+
const clampedResetInMs = Math.min(Math.max(resetInMs as number, 0), durationMs);
|
|
1350
|
+
const elapsedMs = durationMs - clampedResetInMs;
|
|
1351
|
+
if (elapsedMs <= 0) {
|
|
1352
|
+
return usedFraction;
|
|
1353
|
+
}
|
|
1354
|
+
const elapsedHours = elapsedMs / (60 * 60 * 1000);
|
|
1355
|
+
if (!Number.isFinite(elapsedHours) || elapsedHours <= 0) {
|
|
1356
|
+
return usedFraction;
|
|
1357
|
+
}
|
|
1358
|
+
return usedFraction / elapsedHours;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
async #rankOAuthSelections(args: {
|
|
1362
|
+
providerKey: string;
|
|
1363
|
+
provider: string;
|
|
1364
|
+
order: number[];
|
|
1365
|
+
credentials: Array<{ credential: OAuthCredential; index: number }>;
|
|
1366
|
+
options?: { baseUrl?: string };
|
|
1367
|
+
strategy: CredentialRankingStrategy;
|
|
1368
|
+
}): Promise<
|
|
1369
|
+
Array<{
|
|
1370
|
+
selection: { credential: OAuthCredential; index: number };
|
|
1371
|
+
usage: UsageReport | null;
|
|
1372
|
+
usageChecked: boolean;
|
|
1373
|
+
}>
|
|
1374
|
+
> {
|
|
1375
|
+
const nowMs = this.#usageNow();
|
|
1376
|
+
const { strategy } = args;
|
|
1377
|
+
const ranked: Array<{
|
|
1378
|
+
selection: { credential: OAuthCredential; index: number };
|
|
1379
|
+
usage: UsageReport | null;
|
|
1380
|
+
usageChecked: boolean;
|
|
1381
|
+
blocked: boolean;
|
|
1382
|
+
blockedUntil?: number;
|
|
1383
|
+
hasPriorityBoost: boolean;
|
|
1384
|
+
secondaryUsed: number;
|
|
1385
|
+
secondaryDrainRate: number;
|
|
1386
|
+
primaryUsed: number;
|
|
1387
|
+
primaryDrainRate: number;
|
|
1388
|
+
orderPos: number;
|
|
1389
|
+
}> = [];
|
|
1390
|
+
// Pre-fetch usage reports in parallel for non-blocked credentials
|
|
1391
|
+
const usageResults = await Promise.all(
|
|
1392
|
+
args.order.map(async idx => {
|
|
1393
|
+
const selection = args.credentials[idx];
|
|
1394
|
+
if (!selection) return null;
|
|
1395
|
+
const blockedUntil = this.#getCredentialBlockedUntil(args.providerKey, selection.index);
|
|
1396
|
+
if (blockedUntil !== undefined) return { selection, usage: null, usageChecked: false, blockedUntil };
|
|
1397
|
+
const usage = await this.#getUsageReport(args.provider, selection.credential, args.options);
|
|
1398
|
+
return { selection, usage, usageChecked: true, blockedUntil: undefined as number | undefined };
|
|
1399
|
+
}),
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
for (let orderPos = 0; orderPos < usageResults.length; orderPos += 1) {
|
|
1403
|
+
const result = usageResults[orderPos];
|
|
1404
|
+
if (!result) continue;
|
|
1405
|
+
const { selection, usage, usageChecked } = result;
|
|
1406
|
+
let { blockedUntil } = result;
|
|
1407
|
+
let blocked = blockedUntil !== undefined;
|
|
1408
|
+
if (!blocked && usage && this.#isUsageLimitReached(usage)) {
|
|
1409
|
+
const resetAtMs = this.#getUsageResetAtMs(usage, nowMs);
|
|
1410
|
+
blockedUntil = resetAtMs ?? nowMs + AuthStorage.#defaultBackoffMs;
|
|
1411
|
+
this.#markCredentialBlocked(args.providerKey, selection.index, blockedUntil);
|
|
1412
|
+
blocked = true;
|
|
1413
|
+
}
|
|
1414
|
+
const windows = usage ? strategy.findWindowLimits(usage) : undefined;
|
|
1415
|
+
const primary = windows?.primary;
|
|
1416
|
+
const secondary = windows?.secondary;
|
|
1417
|
+
const secondaryTarget = secondary ?? primary;
|
|
1418
|
+
ranked.push({
|
|
1419
|
+
selection,
|
|
1420
|
+
usage,
|
|
1421
|
+
usageChecked,
|
|
1422
|
+
blocked,
|
|
1423
|
+
blockedUntil,
|
|
1424
|
+
hasPriorityBoost: strategy.hasPriorityBoost?.(primary) ?? false,
|
|
1425
|
+
secondaryUsed: this.#normalizeUsageFraction(secondaryTarget),
|
|
1426
|
+
secondaryDrainRate: this.#computeWindowDrainRate(
|
|
1427
|
+
secondaryTarget,
|
|
1428
|
+
nowMs,
|
|
1429
|
+
strategy.windowDefaults.secondaryMs,
|
|
1430
|
+
),
|
|
1431
|
+
primaryUsed: this.#normalizeUsageFraction(primary),
|
|
1432
|
+
primaryDrainRate: this.#computeWindowDrainRate(primary, nowMs, strategy.windowDefaults.primaryMs),
|
|
1433
|
+
orderPos,
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
ranked.sort((left, right) => {
|
|
1437
|
+
if (left.blocked !== right.blocked) return left.blocked ? 1 : -1;
|
|
1438
|
+
if (left.blocked && right.blocked) {
|
|
1439
|
+
const leftBlockedUntil = left.blockedUntil ?? Number.POSITIVE_INFINITY;
|
|
1440
|
+
const rightBlockedUntil = right.blockedUntil ?? Number.POSITIVE_INFINITY;
|
|
1441
|
+
if (leftBlockedUntil !== rightBlockedUntil) return leftBlockedUntil - rightBlockedUntil;
|
|
1442
|
+
return left.orderPos - right.orderPos;
|
|
1443
|
+
}
|
|
1444
|
+
if (left.hasPriorityBoost !== right.hasPriorityBoost) {
|
|
1445
|
+
return left.hasPriorityBoost ? -1 : 1;
|
|
1446
|
+
}
|
|
1447
|
+
if (left.secondaryDrainRate !== right.secondaryDrainRate)
|
|
1448
|
+
return left.secondaryDrainRate - right.secondaryDrainRate;
|
|
1449
|
+
if (left.secondaryUsed !== right.secondaryUsed) return left.secondaryUsed - right.secondaryUsed;
|
|
1450
|
+
if (left.primaryDrainRate !== right.primaryDrainRate) return left.primaryDrainRate - right.primaryDrainRate;
|
|
1451
|
+
if (left.primaryUsed !== right.primaryUsed) return left.primaryUsed - right.primaryUsed;
|
|
1452
|
+
return left.orderPos - right.orderPos;
|
|
1453
|
+
});
|
|
1454
|
+
return ranked.map(candidate => ({
|
|
1455
|
+
selection: candidate.selection,
|
|
1456
|
+
usage: candidate.usage,
|
|
1457
|
+
usageChecked: candidate.usageChecked,
|
|
1458
|
+
}));
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1301
1461
|
/**
|
|
1302
1462
|
* Resolves an OAuth API key, trying credentials in priority order.
|
|
1303
1463
|
* Skips blocked credentials and checks usage limits for providers with usage data.
|
|
@@ -1316,25 +1476,33 @@ export class AuthStorage {
|
|
|
1316
1476
|
|
|
1317
1477
|
const providerKey = this.#getProviderTypeKey(provider, "oauth");
|
|
1318
1478
|
const order = this.#getCredentialOrder(providerKey, sessionId, credentials.length);
|
|
1319
|
-
const
|
|
1320
|
-
const checkUsage =
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1479
|
+
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
1480
|
+
const checkUsage = strategy !== undefined && credentials.length > 1;
|
|
1481
|
+
const candidates = checkUsage
|
|
1482
|
+
? await this.#rankOAuthSelections({ providerKey, provider, order, credentials, options, strategy })
|
|
1483
|
+
: order
|
|
1484
|
+
.map(idx => credentials[idx])
|
|
1485
|
+
.filter((selection): selection is { credential: OAuthCredential; index: number } => Boolean(selection))
|
|
1486
|
+
.map(selection => ({ selection, usage: null, usageChecked: false }));
|
|
1487
|
+
const fallback = candidates[0];
|
|
1488
|
+
|
|
1489
|
+
for (const candidate of candidates) {
|
|
1490
|
+
const apiKey = await this.#tryOAuthCredential(provider, candidate.selection, providerKey, sessionId, options, {
|
|
1330
1491
|
checkUsage,
|
|
1331
|
-
false,
|
|
1332
|
-
|
|
1492
|
+
allowBlocked: false,
|
|
1493
|
+
prefetchedUsage: candidate.usage,
|
|
1494
|
+
usagePrechecked: candidate.usageChecked,
|
|
1495
|
+
});
|
|
1333
1496
|
if (apiKey) return apiKey;
|
|
1334
1497
|
}
|
|
1335
1498
|
|
|
1336
|
-
if (fallback && this.#isCredentialBlocked(providerKey, fallback.index)) {
|
|
1337
|
-
return this.#tryOAuthCredential(provider, fallback, providerKey, sessionId, options,
|
|
1499
|
+
if (fallback && this.#isCredentialBlocked(providerKey, fallback.selection.index)) {
|
|
1500
|
+
return this.#tryOAuthCredential(provider, fallback.selection, providerKey, sessionId, options, {
|
|
1501
|
+
checkUsage,
|
|
1502
|
+
allowBlocked: true,
|
|
1503
|
+
prefetchedUsage: fallback.usage,
|
|
1504
|
+
usagePrechecked: fallback.usageChecked,
|
|
1505
|
+
});
|
|
1338
1506
|
}
|
|
1339
1507
|
|
|
1340
1508
|
return undefined;
|
|
@@ -1342,14 +1510,19 @@ export class AuthStorage {
|
|
|
1342
1510
|
|
|
1343
1511
|
/** Attempts to use a single OAuth credential, checking usage and refreshing token. */
|
|
1344
1512
|
async #tryOAuthCredential(
|
|
1345
|
-
provider:
|
|
1513
|
+
provider: Provider,
|
|
1346
1514
|
selection: { credential: OAuthCredential; index: number },
|
|
1347
1515
|
providerKey: string,
|
|
1348
1516
|
sessionId: string | undefined,
|
|
1349
1517
|
options: { baseUrl?: string } | undefined,
|
|
1350
|
-
|
|
1351
|
-
|
|
1518
|
+
usageOptions: {
|
|
1519
|
+
checkUsage: boolean;
|
|
1520
|
+
allowBlocked: boolean;
|
|
1521
|
+
prefetchedUsage?: UsageReport | null;
|
|
1522
|
+
usagePrechecked?: boolean;
|
|
1523
|
+
},
|
|
1352
1524
|
): Promise<string | undefined> {
|
|
1525
|
+
const { checkUsage, allowBlocked, prefetchedUsage = null, usagePrechecked = false } = usageOptions;
|
|
1353
1526
|
if (!allowBlocked && this.#isCredentialBlocked(providerKey, selection.index)) {
|
|
1354
1527
|
return undefined;
|
|
1355
1528
|
}
|
|
@@ -1358,8 +1531,13 @@ export class AuthStorage {
|
|
|
1358
1531
|
let usageChecked = false;
|
|
1359
1532
|
|
|
1360
1533
|
if (checkUsage && !allowBlocked) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1534
|
+
if (usagePrechecked) {
|
|
1535
|
+
usage = prefetchedUsage;
|
|
1536
|
+
usageChecked = true;
|
|
1537
|
+
} else {
|
|
1538
|
+
usage = await this.#getUsageReport(provider, selection.credential, options);
|
|
1539
|
+
usageChecked = true;
|
|
1540
|
+
}
|
|
1363
1541
|
if (usage && this.#isUsageLimitReached(usage)) {
|
|
1364
1542
|
const resetAtMs = this.#getUsageResetAtMs(usage, this.#usageNow());
|
|
1365
1543
|
this.#markCredentialBlocked(
|
package/src/index.ts
CHANGED