@linxiraos/pi-ai 1.1.27 → 1.2.1
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 +2 -24
- package/README.md +1 -1
- package/dist/types/auth/affinity.d.ts +33 -6
- package/dist/types/auth/cascade.d.ts +25 -3
- package/dist/types/auth/oauth.d.ts +9 -6
- package/dist/types/auth/policy.d.ts +1 -0
- package/dist/types/auth/refresh.d.ts +1 -2
- package/dist/types/auth/select.d.ts +6 -4
- package/dist/types/auth/sqlite-credential-store.d.ts +21 -1
- package/dist/types/auth/store.d.ts +8 -2
- package/dist/types/auth/types.d.ts +87 -14
- package/dist/types/auth-broker/client.d.ts +3 -3
- package/dist/types/auth-broker/discover.d.ts +1 -1
- package/dist/types/auth-broker/remote-store.d.ts +6 -4
- package/dist/types/auth-broker/server.d.ts +2 -0
- package/dist/types/auth-broker/snapshot-cache.d.ts +12 -0
- package/dist/types/auth-gateway/dispatch.d.ts +7 -3
- package/dist/types/auth-gateway/http.d.ts +0 -2
- package/dist/types/auth-gateway/server.d.ts +3 -3
- package/dist/types/auth-gateway/session-state.d.ts +6 -0
- package/dist/types/auth-storage.d.ts +7 -6
- package/dist/types/dialect/terminator-wait.d.ts +19 -0
- package/dist/types/env-api-key.d.ts +21 -0
- package/dist/types/error/auth.d.ts +10 -0
- package/dist/types/error/flags.d.ts +23 -0
- package/dist/types/images/index.d.ts +2 -3
- package/dist/types/images/shared.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/judgment/index.d.ts +2 -1
- package/dist/types/judgment/native.d.ts +80 -0
- package/dist/types/judgment/openai-decisions.d.ts +3 -0
- package/dist/types/judgment/question-file.d.ts +52 -0
- package/dist/types/judgment/systemone.d.ts +7 -0
- package/dist/types/judgment/types.d.ts +2 -2
- package/dist/types/judgment/wire.d.ts +36 -0
- package/dist/types/provider-details.d.ts +1 -1
- package/dist/types/provider-session-state.d.ts +2 -2
- package/dist/types/providers/anthropic-client.d.ts +10 -0
- package/dist/types/providers/anthropic-state.d.ts +2 -0
- package/dist/types/providers/anthropic.d.ts +23 -1
- package/dist/types/providers/connect-frame.d.ts +44 -0
- package/dist/types/providers/cursor/interaction-query.d.ts +0 -2
- package/dist/types/providers/cursor.d.ts +8 -8
- package/dist/types/providers/google-auth.d.ts +4 -1
- package/dist/types/providers/google-shared.d.ts +24 -4
- package/dist/types/providers/inference-headers.d.ts +6 -0
- package/dist/types/providers/mock.d.ts +3 -1
- package/dist/types/providers/openai-codex/request-transformer.d.ts +2 -0
- package/dist/types/providers/openai-codex-responses.d.ts +1 -1
- package/dist/types/providers/openai-configuration-update.d.ts +20 -11
- package/dist/types/providers/openai-responses-wire.d.ts +1 -55
- package/dist/types/providers/openai-responses.d.ts +8 -0
- package/dist/types/providers/openai-shared.d.ts +16 -4
- package/dist/types/providers/pi-native-client.d.ts +1 -1
- package/dist/types/providers/transform-messages.d.ts +14 -0
- package/dist/types/providers/vision-guard.d.ts +1 -1
- package/dist/types/registry/oauth/cloudflare-workers-ai.d.ts +3 -0
- package/dist/types/registry/oauth/cursor.d.ts +10 -2
- package/dist/types/registry/oauth/index.d.ts +2 -0
- package/dist/types/stream.d.ts +0 -21
- package/dist/types/transcription/types.d.ts +2 -1
- package/dist/types/types.d.ts +76 -11
- package/dist/types/usage.d.ts +2 -0
- package/dist/types/utils/anthropic-auth.d.ts +21 -7
- package/dist/types/utils/aws-profile.d.ts +5 -0
- package/dist/types/utils/block-symbols.d.ts +14 -0
- package/dist/types/utils/glyph-codec.d.ts +1 -1
- package/dist/types/utils/leaked-thinking-stream.d.ts +1 -1
- package/dist/types/utils/resolve-peer.d.ts +2 -0
- package/dist/types/utils/retry-wait.d.ts +9 -0
- package/dist/types/utils/schema/normalize.d.ts +8 -3
- package/package.json +7 -7
- package/src/auth/affinity.ts +130 -37
- package/src/auth/blocks.ts +90 -14
- package/src/auth/cascade.ts +68 -9
- package/src/auth/health.ts +2 -2
- package/src/auth/oauth.ts +25 -10
- package/src/auth/policy.ts +10 -1
- package/src/auth/pool.ts +0 -5
- package/src/auth/refresh.ts +17 -12
- package/src/auth/resets.ts +1 -1
- package/src/auth/rotation.ts +8 -2
- package/src/auth/select.ts +99 -29
- package/src/auth/sqlite-credential-store.ts +119 -49
- package/src/auth/store.ts +8 -2
- package/src/auth/types.ts +96 -15
- package/src/auth/usage-cache.ts +16 -5
- package/src/auth/usage.ts +2 -2
- package/src/auth-broker/client.ts +7 -4
- package/src/auth-broker/discover.ts +6 -5
- package/src/auth-broker/remote-store.ts +200 -62
- package/src/auth-broker/server.ts +316 -130
- package/src/auth-broker/snapshot-cache.ts +142 -2
- package/src/auth-gateway/dispatch.ts +9 -5
- package/src/auth-gateway/http.ts +0 -8
- package/src/auth-gateway/routes/systemone.ts +16 -12
- package/src/auth-gateway/server.ts +34 -19
- package/src/auth-gateway/session-state.ts +51 -9
- package/src/auth-storage.ts +23 -9
- package/src/dialect/deepseek.ts +10 -2
- package/src/dialect/gemini.ts +8 -2
- package/src/dialect/hermes.ts +8 -2
- package/src/dialect/kimi.ts +8 -2
- package/src/dialect/qwen-xml.ts +8 -2
- package/src/dialect/qwen3.ts +8 -2
- package/src/dialect/terminator-wait.ts +42 -0
- package/src/env-api-key.ts +78 -0
- package/src/error/auth.ts +20 -0
- package/src/error/flags.ts +93 -0
- package/src/error/rate-limit.ts +8 -6
- package/src/images/index.ts +3 -17
- package/src/images/openai-hosted.ts +13 -4
- package/src/images/openai-images.ts +19 -13
- package/src/images/shared.ts +10 -2
- package/src/index.ts +1 -0
- package/src/judgment/index.ts +2 -1
- package/src/judgment/native.ts +333 -0
- package/src/judgment/openai-decisions-predicate.md +9 -0
- package/src/judgment/openai-decisions.ts +129 -0
- package/src/judgment/question-file.ts +90 -0
- package/src/judgment/systemone.ts +65 -0
- package/src/judgment/text.ts +9 -1
- package/src/judgment/types.ts +2 -2
- package/src/judgment/wire.ts +42 -0
- package/src/provider-details.ts +1 -1
- package/src/provider-session-state.ts +2 -2
- package/src/providers/amazon-bedrock.ts +19 -8
- package/src/providers/anthropic-client.ts +16 -1
- package/src/providers/anthropic-state.ts +6 -0
- package/src/providers/anthropic.ts +857 -202
- package/src/providers/apple-foundation-models.ts +10 -4
- package/src/providers/aws-credentials.ts +2 -2
- package/src/providers/azure-openai-responses.ts +1 -1
- package/src/providers/connect-frame.ts +107 -0
- package/src/providers/cursor/interaction-query.ts +1 -9
- package/src/providers/cursor.ts +510 -101
- package/src/providers/devin.ts +65 -57
- package/src/providers/factory-droid/gemini.ts +38 -51
- package/src/providers/gitlab-duo-workflow.ts +5 -2
- package/src/providers/google-auth.ts +10 -3
- package/src/providers/google-shared.ts +134 -105
- package/src/providers/google-vertex.ts +0 -1
- package/src/providers/google.ts +1 -1
- package/src/providers/inference-headers.ts +19 -0
- package/src/providers/mock.ts +4 -0
- package/src/providers/ollama.ts +1 -1
- package/src/providers/openai-codex/request-transformer.ts +16 -11
- package/src/providers/openai-codex-responses.ts +143 -70
- package/src/providers/openai-completions.ts +9 -2
- package/src/providers/openai-configuration-update.ts +54 -15
- package/src/providers/openai-responses-wire.ts +1 -55
- package/src/providers/openai-responses.ts +90 -29
- package/src/providers/openai-shared.ts +154 -43
- package/src/providers/pi-native-client.ts +2 -2
- package/src/providers/systemone-server.ts +1 -1
- package/src/providers/transcriptions-server.ts +1 -1
- package/src/providers/transform-messages.ts +238 -82
- package/src/providers/vision-guard.ts +1 -1
- package/src/registry/aws.ts +51 -14
- package/src/registry/cloudflare-ai-gateway.ts +26 -3
- package/src/registry/hooks/api-key.ts +1 -0
- package/src/registry/hooks/env.ts +2 -5
- package/src/registry/oauth/callback-server.ts +6 -1
- package/src/registry/oauth/cloudflare-ai-gateway.ts +2 -2
- package/src/registry/oauth/cloudflare-workers-ai.ts +32 -0
- package/src/registry/oauth/cursor.ts +80 -17
- package/src/registry/oauth/index.ts +5 -0
- package/src/registry/oauth/snowflake.ts +1 -1
- package/src/stream.ts +5 -72
- package/src/transcription/types.ts +2 -1
- package/src/types.ts +101 -12
- package/src/usage/claude.ts +16 -12
- package/src/usage/commandcode.ts +1 -1
- package/src/usage/cursor.ts +14 -25
- package/src/usage/github-copilot.ts +1 -1
- package/src/usage/kimi.ts +1 -1
- package/src/usage/minimax-code.ts +8 -1
- package/src/usage.ts +8 -0
- package/src/utils/anthropic-auth.ts +34 -10
- package/src/utils/aws-profile.ts +10 -4
- package/src/utils/block-symbols.ts +17 -0
- package/src/utils/empty-completion-retry.ts +2 -3
- package/src/utils/glyph-codec.ts +200 -7
- package/src/utils/leaked-thinking-stream.ts +47 -25
- package/src/utils/resolve-peer.ts +7 -0
- package/src/utils/retry-wait.ts +19 -0
- package/src/utils/schema/CONSTRAINTS.md +9 -7
- package/src/utils/schema/fields.ts +20 -0
- package/src/utils/schema/normalize.ts +65 -23
- package/src/utils/schema/wire.ts +3 -3
- package/src/utils/thinking-loop.ts +18 -3
- package/src/utils/transport-fetch.ts +1 -1
- package/src/utils.ts +22 -6
- package/dist/types/judgment/typesafe.d.ts +0 -49
- package/src/judgment/typesafe.ts +0 -183
package/CHANGELOG.md
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
1
|
## [Unreleased]
|
|
4
2
|
|
|
5
|
-
## [1.
|
|
6
|
-
|
|
7
|
-
- 版本线推进至 1.1.27;本版无独立用户可见变化。
|
|
8
|
-
|
|
9
|
-
### Changed
|
|
10
|
-
|
|
11
|
-
- Reduced CPU spent on thinking-loop detection while streaming long reasoning ([#14284](https://github.com/can1357/oh-my-pi/pull/14284) by [@abilliontokens](https://github.com/abilliontokens)).
|
|
12
|
-
- Added `recordAffinity: false` to credential resolution options, selecting as the session would without pinning the choice to that session ([#14512](https://github.com/can1357/oh-my-pi/pull/14512) by [@will-bogusz](https://github.com/will-bogusz))
|
|
13
|
-
|
|
14
|
-
### Fixed
|
|
15
|
-
|
|
16
|
-
- Fixed replayed Responses and Codex history, including persisted Codex user/developer and assistant items, sending `detail: "original"` images to endpoints whose `supportsImageDetailOriginal` is off ([#13687](https://github.com/can1357/oh-my-pi/pull/13687) by [@alphastorm](https://github.com/alphastorm)).
|
|
17
|
-
- Fixed thinking in turns kept after Anthropic native compaction being rejected or dropped on the next request ([#14251](https://github.com/can1357/oh-my-pi/pull/14251) by [@will-bogusz](https://github.com/will-bogusz))
|
|
18
|
-
- Fixed Claude usage being re-polled every 10 seconds while Anthropic rate-limits the account; a failed refresh now waits a minute before trying again ([#14515](https://github.com/can1357/oh-my-pi/pull/14515) by [@will-bogusz](https://github.com/will-bogusz))
|
|
19
|
-
- Fixed Devin requests skipping the `onPayload` hook, so payload capture now sees each Devin chat request and a returned replacement is what gets sent ([#14506](https://github.com/can1357/oh-my-pi/pull/14506) by [@will-bogusz](https://github.com/will-bogusz))
|
|
20
|
-
- An OpenAI Responses turn whose connection drops mid-stream can now recover the finished answer from the provider (on hosts that store results, such as Muse Code) instead of re-running the whole turn and discarding the reasoning already done. Storage is opt-in: pass `storeResponses: true`, set `PI_MUSE_STORE_RESPONSES=1`, or set a process-wide default with `configureProviderStoreResponses` ([#14293](https://github.com/can1357/oh-my-pi/pull/14293) and [#14534](https://github.com/can1357/oh-my-pi/pull/14534) by [@abilliontokens](https://github.com/abilliontokens)).
|
|
21
|
-
- Fixed the auth broker exiting when a background OAuth refresh sweep cannot read the credential store; the failure is now logged and the next sweep retries ([#14538](https://github.com/can1357/oh-my-pi/issues/14538))
|
|
22
|
-
- Fixed Anthropic OAuth billing headers changing during developer-first sessions and side turns, preserving the prompt-cache prefix ([#14495](https://github.com/can1357/oh-my-pi/issues/14495)).
|
|
23
|
-
- Fixed OpenAI-compatible chat-completions gateways recording completed turns as client-cancelled because the connection closed before their `[DONE]` sentinel arrived ([#14481](https://github.com/can1357/oh-my-pi/issues/14481)).
|
|
24
|
-
|
|
25
|
-
## [1.1.25] - 2026-10-03
|
|
3
|
+
## [1.2.0] - 2026-10-12
|
|
26
4
|
|
|
27
|
-
- 版本线推进至 1.
|
|
5
|
+
- 版本线推进至 1.2.0;本版无独立用户可见变化。
|
|
28
6
|
|
|
29
7
|
## [1.1.24] - 2026-10-03
|
|
30
8
|
|
package/README.md
CHANGED
|
@@ -1043,7 +1043,7 @@ The default inference role must hold either `SNOWFLAKE.CORTEX_USER` or the narro
|
|
|
1043
1043
|
|
|
1044
1044
|
For PAT authentication, set both `SNOWFLAKE_ACCOUNT` and `SNOWFLAKE_PAT`. Prefer a short-lived PAT restricted to a dedicated inference role and the required user-scoped network allowlist; do not disable network-policy enforcement. See [Snowflake PAT setup](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens) and [local-application OAuth](https://docs.snowflake.com/en/user-guide/oauth-local-applications).
|
|
1045
1045
|
|
|
1046
|
-
Claude models use `/api/v2/cortex/v1/messages`; OpenAI models use `/api/v2/cortex/v1/chat/completions`. Both reuse omp's streaming transports and local tool execution. The 15 bundled models are a documented roster, not an account entitlement check: availability still depends on Cortex REST API enablement, role grants, model allowlists, and region. If authentication succeeds but Cortex returns `003001` ("This account is not allowed to access this endpoint"), Snowflake has not enabled Cortex REST for the account. [Self-service trial accounts disable AI features until a credit card is added](https://docs.snowflake.com/en/user-guide/admin-trial-account#label-trial-account-ai-features) (adding one does not end the trial); REST access can remain disabled even after AI SQL functions work, in which case Snowflake support must enable it. Cross-region inference requires an account administrator's explicit decision. `
|
|
1046
|
+
Claude models use `/api/v2/cortex/v1/messages`; OpenAI models use `/api/v2/cortex/v1/chat/completions`. Both reuse omp's streaming transports and local tool execution. The 15 bundled models are a documented roster, not an account entitlement check: availability still depends on Cortex REST API enablement, role grants, model allowlists, and region. If authentication succeeds but Cortex returns `003001` ("This account is not allowed to access this endpoint"), Snowflake has not enabled Cortex REST for the account. [Self-service trial accounts disable AI features until a credit card is added](https://docs.snowflake.com/en/user-guide/admin-trial-account#label-trial-account-ai-features) (adding one does not end the trial); REST access can remain disabled even after AI SQL functions work, in which case Snowflake support must enable it. Cross-region inference requires an account administrator's explicit decision. `zetacode models snowflake --json` lists available models only after credentials are configured.
|
|
1047
1047
|
|
|
1048
1048
|
This provider uses the [public, account-billed Cortex REST API](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api). CoCo-subscription coverage is unverified. Catalog prices are per-million-token estimates from [Consumption Table 6(b)](https://www.snowflake.com/legal-files/CreditConsumptionTable.pdf) at [$2 per AI Credit](https://docs.snowflake.com/en/user-guide/snowflake-cortex/pricing) for on-demand global routing, not your contract price. Output is capped at the REST API's documented 16,384-token maximum; context windows are conservative AI_COMPLETE limits, not verified REST maxima. Cortex compatibility rules limit explicit cache retention to five minutes. Paste an account identifier or current Snowsight link; legacy `app.snowflake.com/<region>/<locator>` links are rejected because they don't map uniformly to an account host.
|
|
1049
1049
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthCredential, OAuthCredential, SessionsApi } from "./types.js";
|
|
1
|
+
import type { AuthCredential, OAuthCredential, SessionRestrictionLease, SessionsApi } from "./types.js";
|
|
2
2
|
import type { AuthCredentialStore } from "./store.js";
|
|
3
3
|
import type { CredentialPool } from "./pool.js";
|
|
4
4
|
import type { KeyOverrides } from "./cascade.js";
|
|
@@ -13,10 +13,31 @@ export type SessionCredential = {
|
|
|
13
13
|
/** Set only by the public user-facing pin API; automatic warm affinity leaves it absent. */
|
|
14
14
|
explicit?: true;
|
|
15
15
|
};
|
|
16
|
+
/** One installed restriction: the allowed OAuth identity keys and the lease that lifts it. */
|
|
17
|
+
type SessionRestriction = {
|
|
18
|
+
readonly allowed: ReadonlySet<string>;
|
|
19
|
+
readonly lease: SessionRestrictionLease;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Provider → session id → installed restriction. Owned by `AuthStorage` so
|
|
23
|
+
* restrictions outlive store replacement, which rebuilds every store-bound
|
|
24
|
+
* module (pins included) around the same identities.
|
|
25
|
+
*/
|
|
26
|
+
export type SessionRestrictions = Map<string, Map<string, SessionRestriction>>;
|
|
16
27
|
/** Session → credential affinity (pins), persisted in the store cache. */
|
|
17
28
|
export declare class SessionAffinity implements SessionsApi {
|
|
18
29
|
#private;
|
|
19
|
-
constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides);
|
|
30
|
+
constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides, restrictions: SessionRestrictions);
|
|
31
|
+
restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
|
|
32
|
+
unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
|
|
33
|
+
/** True when {@link restrict} limits `sessionId` for `provider`. */
|
|
34
|
+
isRestricted(provider: string, sessionId: string | undefined): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* True when `credential` may serve `sessionId`: always for an unrestricted
|
|
37
|
+
* session, otherwise only an OAuth credential whose identity key is allowed.
|
|
38
|
+
*/
|
|
39
|
+
allows(provider: string, sessionId: string | undefined, credential: AuthCredential): boolean;
|
|
40
|
+
permits(provider: string, sessionId: string, credentialId: number): boolean;
|
|
20
41
|
/** Drop every pin for a provider, both in memory and in the persisted cache. */
|
|
21
42
|
clearProvider(provider: string): void;
|
|
22
43
|
/** Drop only this in-memory session pin after OAuth selection falls through. */
|
|
@@ -33,7 +54,11 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
33
54
|
* rewrites were pure database churn.
|
|
34
55
|
*/
|
|
35
56
|
record(provider: string, sessionId: string | undefined, type: AuthCredential["type"], index: number, lastUsedAtMs?: number, explicit?: boolean): void;
|
|
36
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves the last credential used by a session. A restricted session
|
|
59
|
+
* never sees a pin outside its allowlist — inherited, restored, or recorded
|
|
60
|
+
* before the restriction — so it re-ranks inside the allowlist instead.
|
|
61
|
+
*/
|
|
37
62
|
get(provider: string, sessionId: string | undefined): SessionCredential | undefined;
|
|
38
63
|
/** Clears the last credential used by a session for a provider. */
|
|
39
64
|
clear(provider: string, sessionId: string | undefined): void;
|
|
@@ -55,14 +80,15 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
55
80
|
restoredAtMs?: number;
|
|
56
81
|
}): boolean;
|
|
57
82
|
/**
|
|
58
|
-
* Copy
|
|
83
|
+
* Copy stored credential affinities from one live session to another.
|
|
59
84
|
*
|
|
60
85
|
* The target receives its own sticky entries, so request resolution, usage
|
|
61
86
|
* blocking, credential rotation, metadata, and persisted pins all continue
|
|
62
87
|
* through the target session id without retaining a live dependency on the
|
|
63
|
-
* source session.
|
|
88
|
+
* source session. `include` limits the copy to the providers it accepts,
|
|
89
|
+
* given whether the source's affinity is an explicit user pin.
|
|
64
90
|
*/
|
|
65
|
-
inherit(sourceSessionId: string, targetSessionId: string): number;
|
|
91
|
+
inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
|
|
66
92
|
/**
|
|
67
93
|
* Release a session's sticky credential so its next `KeyCascade.get` call
|
|
68
94
|
* re-runs native pool ranking. This never blocks or penalizes the released
|
|
@@ -71,3 +97,4 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
71
97
|
*/
|
|
72
98
|
release(provider: string, sessionId: string): boolean;
|
|
73
99
|
}
|
|
100
|
+
export {};
|
|
@@ -2,12 +2,20 @@ import { type ApiKeyResolver, type ResolvedApiKey } from "../auth-retry.js";
|
|
|
2
2
|
import type { SessionAffinity } from "./affinity.js";
|
|
3
3
|
import type { CredentialPool } from "./pool.js";
|
|
4
4
|
import type { CredentialSelector } from "./select.js";
|
|
5
|
-
import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi
|
|
5
|
+
import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi } from "./types.js";
|
|
6
6
|
/** Runtime (--api-key) and config (models.yml) key overrides plus the config-value resolver. */
|
|
7
7
|
export declare class KeyOverrides {
|
|
8
8
|
#private;
|
|
9
9
|
constructor(resolver?: (config: string) => Promise<string | undefined>);
|
|
10
10
|
has(provider: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether an override replaces OAuth for a session. Any override does for an
|
|
13
|
+
* unrestricted session. A restricted session skips the runtime key, but a
|
|
14
|
+
* config key still blocks OAuth: it marks the provider's endpoint (often a
|
|
15
|
+
* proxy) as taking that key, so the session fails closed instead of sending
|
|
16
|
+
* a pooled OAuth token there (see `KeyCascade.get`).
|
|
17
|
+
*/
|
|
18
|
+
suppressesOAuth(provider: string, restricted: boolean): boolean;
|
|
11
19
|
runtimeKey(provider: string): string | undefined;
|
|
12
20
|
configKey(provider: string): string | undefined;
|
|
13
21
|
/** Config value consulted only after stored OAuth/login credentials. */
|
|
@@ -121,15 +129,29 @@ export declare class KeyCascade implements KeysApi {
|
|
|
121
129
|
* credential to reach the correct host.
|
|
122
130
|
*/
|
|
123
131
|
peek(provider: string): Promise<string | undefined>;
|
|
124
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Resolve a bearer together with the stored row that supplied it, for a request.
|
|
134
|
+
* Same precedence as {@link KeyCascade.get}, but rejects with
|
|
135
|
+
* {@link AIError.OAuthRefreshUnavailableError} (transient, retryable) when OAuth
|
|
136
|
+
* refresh failed with a retryable error and no other permitted source could serve
|
|
137
|
+
* the request, so request paths retry instead of reporting a missing key.
|
|
138
|
+
*/
|
|
125
139
|
getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
|
|
126
140
|
/**
|
|
127
141
|
* Get API key for a provider.
|
|
128
142
|
* Priority (first match wins): runtime override, config override, OAuth,
|
|
129
143
|
* login API key, environment variable, another stored API key, then the
|
|
130
144
|
* lowest-tier OMP compat mirror.
|
|
145
|
+
* login API key, environment variable, then another stored API key.
|
|
146
|
+
* A session restricted by `sessions.restrict` resolves only its allowed
|
|
147
|
+
* OAuth accounts and throws {@link AIError.MissingApiKeyError} when none
|
|
148
|
+
* can serve or a config key (a models.yml `apiKey`, often for a proxy
|
|
149
|
+
* `baseUrl`) owns the provider, rather than using any other credential.
|
|
150
|
+
* A transient OAuth refresh failure resolves `undefined`, so availability
|
|
151
|
+
* probes move on to their next candidate; request paths use
|
|
152
|
+
* {@link KeyCascade.getWithCredential}, which surfaces it.
|
|
131
153
|
*/
|
|
132
|
-
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions
|
|
154
|
+
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
|
|
133
155
|
/**
|
|
134
156
|
* Build an {@link ApiKeyResolver} backed by this storage, implementing the
|
|
135
157
|
* central a/b/c auth-retry policy:
|
|
@@ -37,9 +37,11 @@ export declare class OAuthAccounts implements OAuthApi {
|
|
|
37
37
|
* `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
|
|
38
38
|
* scenarios, prefer API-key resolution.
|
|
39
39
|
*
|
|
40
|
-
* Returns `undefined` when no OAuth credential is available
|
|
41
|
-
*
|
|
42
|
-
* OAuth with an explicit API key.
|
|
40
|
+
* Returns `undefined` when no usable OAuth credential is available
|
|
41
|
+
* (none stored, or every one definitively failed to refresh) or
|
|
42
|
+
* runtime/config overrides have replaced OAuth with an explicit API key.
|
|
43
|
+
* Rejects with {@link AIError.OAuthRefreshUnavailableError} (transient,
|
|
44
|
+
* retryable) when a retryable refresh failure left no usable credential.
|
|
43
45
|
*/
|
|
44
46
|
access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
|
|
45
47
|
/**
|
|
@@ -65,9 +67,10 @@ export declare class OAuthAccounts implements OAuthApi {
|
|
|
65
67
|
* Resolve one stored OAuth credential by its durable storage row id.
|
|
66
68
|
*
|
|
67
69
|
* Unlike the normal session resolver, this method never ranks, rotates, or
|
|
68
|
-
* falls back to sibling credentials. A forced refresh re-mints only the
|
|
69
|
-
* requested row
|
|
70
|
-
*
|
|
70
|
+
* falls back to sibling credentials. A generic forced refresh re-mints only the
|
|
71
|
+
* requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
|
|
72
|
+
* still-usable recent mint of that row. Either way exact-account affinity holds
|
|
73
|
+
* for operations whose provenance and policy boundary are tied to one workspace.
|
|
71
74
|
*
|
|
72
75
|
* Returns `undefined` when the row does not exist for `provider` or an
|
|
73
76
|
* explicit runtime/config API-key override suppresses OAuth.
|
|
@@ -14,6 +14,7 @@ export declare class AccountPolicies {
|
|
|
14
14
|
*/
|
|
15
15
|
replace(policies: AuthAccountPolicies, defaultReservePct: number | undefined, storedCredentials?: ReadonlyMap<string, readonly AuthCredential[]>): void;
|
|
16
16
|
validateUsageCapability(provider: string, canFetchUsage: boolean): void;
|
|
17
|
+
/** Throw on ambiguous selectors for `provider`; a selector whose account is absent is inert and warns once. */
|
|
17
18
|
validateFor(provider: string, credentials: readonly AuthCredential[]): void;
|
|
18
19
|
/**
|
|
19
20
|
* Return the configured account policy matching an OAuth identity.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { OAuthCredentials } from "../registry/oauth/types.js";
|
|
2
2
|
import type { Provider } from "../types.js";
|
|
3
3
|
import { type CredentialPool } from "./pool.js";
|
|
4
|
-
import type { AccountPolicies } from "./policy.js";
|
|
5
4
|
import { type AuthCredentialStore } from "./store.js";
|
|
6
5
|
import { type AuthCredentialSnapshotEntry, type AuthStorageOptions, type OAuthCredential, type OAuthRefreshByIdOptions, type OAuthRefreshReason, type StoredOAuthRefreshOptions, type StoredOAuthRefreshResult } from "./types.js";
|
|
7
6
|
/**
|
|
@@ -30,8 +29,8 @@ export declare function mergeRefreshedCredential<T extends OAuthCredential>(curr
|
|
|
30
29
|
export interface OAuthRefresherDeps {
|
|
31
30
|
store: AuthCredentialStore;
|
|
32
31
|
pool: CredentialPool;
|
|
33
|
-
policies: AccountPolicies;
|
|
34
32
|
override?: AuthStorageOptions["refreshOAuthCredential"];
|
|
33
|
+
overrideMints?: AuthStorageOptions["refreshOAuthCredentialMints"];
|
|
35
34
|
}
|
|
36
35
|
/** Single-flighted, lease-guarded OAuth refresh with compare-and-set persistence. */
|
|
37
36
|
export declare class OAuthRefresher {
|
|
@@ -8,7 +8,7 @@ import { type CredentialPool } from "./pool.js";
|
|
|
8
8
|
import { type ApiKeySelection, type OAuthSelection } from "./rank.js";
|
|
9
9
|
import { type OAuthRefresher } from "./refresh.js";
|
|
10
10
|
import type { AuthCredentialStore } from "./store.js";
|
|
11
|
-
import type
|
|
11
|
+
import { type ApiKeyCredential, type AuthApiKeyOptions, type AuthCredential, type OAuthCredential } from "./types.js";
|
|
12
12
|
import type { UsageService } from "./usage.js";
|
|
13
13
|
/** Temporary block after a transient OAuth refresh failure. */
|
|
14
14
|
export declare const OAUTH_REFRESH_FAILURE_BACKOFF_MS: number;
|
|
@@ -32,6 +32,8 @@ export type TryOAuthOptions = {
|
|
|
32
32
|
blockScopes?: readonly string[];
|
|
33
33
|
/** When false, a definitive failure of THIS credential returns undefined instead of falling back to the ranked/round-robin selector (target-only resolution). */
|
|
34
34
|
allowFallback?: boolean;
|
|
35
|
+
/** Receives a non-definitive refresh failure that left this credential unusable; the caller filters for retryable ones. */
|
|
36
|
+
onTransientRefreshFailure?: (error: unknown) => void;
|
|
35
37
|
};
|
|
36
38
|
/** Services consulted by CredentialSelector for policy, usage, blocks, refresh, and session affinity. */
|
|
37
39
|
export interface CredentialSelectorDeps {
|
|
@@ -69,9 +71,9 @@ export declare class CredentialSelector {
|
|
|
69
71
|
* 1. strict: unblocked credentials only, usage limits respected, plan
|
|
70
72
|
* filter enforced (when any account is confirmed eligible);
|
|
71
73
|
* 2. plan-fitting last resort: same plan filter, but blocked/exhausted
|
|
72
|
-
* accounts are allowed (
|
|
73
|
-
* first) so the caller gets real
|
|
74
|
-
* instead of a missing key;
|
|
74
|
+
* accounts are allowed (an explicit session pin first, then blocked
|
|
75
|
+
* candidates earliest-unblocking first) so the caller gets real
|
|
76
|
+
* usage-limit semantics from the wire instead of a missing key;
|
|
75
77
|
* 3. unfiltered last resort: the plan filter matched nothing usable —
|
|
76
78
|
* skip it and try every account once; the server is the final arbiter
|
|
77
79
|
* of model access.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { Database } from "bun:sqlite";
|
|
8
8
|
import { isSqliteBusyError, isSqliteCorruptionError } from "@linxiraos/pi-utils";
|
|
9
9
|
import type { AuthCredentialStore, CredentialRefreshLeaseFence } from "./store.js";
|
|
10
|
-
import type { AuthCredential, DisabledCredentialSummary, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
|
|
10
|
+
import type { AuthCredential, DisabledCredentialSummary, OAuthCredential, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
|
|
11
11
|
import type { OAuthCredentials } from "../registry/oauth/types.js";
|
|
12
12
|
import type { ClientUsageReport, ClientUsageSummary, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
|
|
13
13
|
export declare const USAGE_REPORT_TTL_MS: number;
|
|
@@ -18,7 +18,26 @@ type SerializedCredentialRecord = {
|
|
|
18
18
|
};
|
|
19
19
|
export { isSqliteBusyError, isSqliteCorruptionError };
|
|
20
20
|
export declare function serializeCredential(provider: string, credential: AuthCredential): SerializedCredentialRecord | null;
|
|
21
|
+
/**
|
|
22
|
+
* Identity key of a credential: the `identityKey` that broker snapshots carry
|
|
23
|
+
* and that broker account pools and `sessions.restrict` match, such as
|
|
24
|
+
* `email:<address>|org:<id>` for org-scoped providers. `null` for an API key or
|
|
25
|
+
* an OAuth credential without any account, email, project, or org identity.
|
|
26
|
+
*/
|
|
21
27
|
export declare function resolveCredentialIdentityKey(provider: string, credential: AuthCredential): string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Whether upserting `incoming` replaces the active row holding `existing` (stored
|
|
30
|
+
* under `existingIdentityKey`) instead of inserting a new row beside it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function matchesReplacementCredential(provider: string, existing: AuthCredential | null, existingIdentityKey: string | null, incoming: AuthCredential): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether two OAuth credentials belong to the same account of the same organization,
|
|
35
|
+
* whichever identity key each is stored under. The email decides when both carry one;
|
|
36
|
+
* otherwise a matching account id does, unless it is the organization's own id (a
|
|
37
|
+
* ChatGPT workspace id shared by every member). Project ids are shared between users
|
|
38
|
+
* (every consumer Antigravity login has the same one) and never count.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isSameOAuthAccount(a: OAuthCredential, b: OAuthCredential): boolean;
|
|
22
41
|
/**
|
|
23
42
|
* Default SQLite-backed implementation of {@link AuthCredentialStore}.
|
|
24
43
|
*
|
|
@@ -60,6 +79,7 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
60
79
|
deleteCachePrefix(prefix: string): void;
|
|
61
80
|
cleanExpiredCache(): void;
|
|
62
81
|
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
82
|
+
getCredentialBlockScopes(credentialId: number, providerKey: string): Map<string, number>;
|
|
63
83
|
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
64
84
|
upsertCredentialBlock(block: StoredCredentialBlock): void;
|
|
65
85
|
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
|
|
@@ -71,6 +71,12 @@ export interface CredentialCacheStore {
|
|
|
71
71
|
export interface CredentialBlockStore {
|
|
72
72
|
/** Non-expired block for one (credential, providerKey, scope) key, or undefined. */
|
|
73
73
|
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Non-expired blocks for one credential and providerKey, keyed by block
|
|
76
|
+
* scope (`""` is unscoped), in one read. Optional: selectors fall back to
|
|
77
|
+
* {@link CredentialBlockStore.getCredentialBlock} per scope when absent.
|
|
78
|
+
*/
|
|
79
|
+
getCredentialBlockScopes?(credentialId: number, providerKey: string): ReadonlyMap<string, number>;
|
|
74
80
|
/** Earliest time a shared-store block should be eligible for live-usage reconciliation. */
|
|
75
81
|
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
76
82
|
/** Upsert with MAX semantics: keep the later blockedUntilMs on conflict. */
|
|
@@ -116,8 +122,8 @@ export interface UsageLedgerStore {
|
|
|
116
122
|
}
|
|
117
123
|
/** Broker-delegated OAuth refresh and usage-report operations. */
|
|
118
124
|
export interface CredentialUpstream {
|
|
119
|
-
/** Optional hook to notify the underlying store that usage
|
|
120
|
-
invalidateUsageCache(signal?: AbortSignal): Promise<void>;
|
|
125
|
+
/** Optional hook to notify the underlying store that usage reports (one provider's, when given) are stale. */
|
|
126
|
+
invalidateUsageCache(provider?: string, signal?: AbortSignal): Promise<void>;
|
|
121
127
|
/**
|
|
122
128
|
* Optional store-supplied OAuth refresh. When present, `AuthStorage` uses
|
|
123
129
|
* it before the per-provider local refresh path. `RemoteAuthCredentialStore`
|
|
@@ -316,6 +316,12 @@ export type AuthStorageOptions = {
|
|
|
316
316
|
* the actual refresh token never leaves the broker.
|
|
317
317
|
*/
|
|
318
318
|
refreshOAuthCredential?: (provider: Provider, credentialId: number, credential: OAuthCredential, signal?: AbortSignal, reason?: OAuthRefreshReason) => Promise<OAuthCredentials>;
|
|
319
|
+
/**
|
|
320
|
+
* Set when {@link refreshOAuthCredential} exchanges the refresh token with the
|
|
321
|
+
* provider itself instead of delegating (e.g. to a broker), so its tokens count
|
|
322
|
+
* as this process's mints for auth-recovery reuse.
|
|
323
|
+
*/
|
|
324
|
+
refreshOAuthCredentialMints?: boolean;
|
|
319
325
|
/**
|
|
320
326
|
* Human-readable description of the credential store backing this
|
|
321
327
|
* AuthStorage instance. Surfaced through {@link AuthStorage.keys.describe}
|
|
@@ -409,7 +415,7 @@ export interface ModelUsageHealthOptions {
|
|
|
409
415
|
export type AuthApiKeyOptions = {
|
|
410
416
|
baseUrl?: string;
|
|
411
417
|
modelId?: string;
|
|
412
|
-
/**
|
|
418
|
+
/** {@link oauthAccountKey}s of accounts known to serve `modelId` from multi-account discovery; OAuth selection prefers them and tries other accounts only as a last resort. */
|
|
413
419
|
accountIds?: readonly string[];
|
|
414
420
|
/**
|
|
415
421
|
* Caller's cancel signal. Threaded into any broker-bound OAuth refresh so
|
|
@@ -429,6 +435,19 @@ export type AuthApiKeyOptions = {
|
|
|
429
435
|
/** When false, select as `sessionId` would without recording the choice as that session's sticky credential. */
|
|
430
436
|
recordAffinity?: boolean;
|
|
431
437
|
};
|
|
438
|
+
/**
|
|
439
|
+
* Identity that multi-account discovery records in `Model.accountAccess` and
|
|
440
|
+
* that {@link AuthApiKeyOptions.accountIds} selection matches: the provider
|
|
441
|
+
* account id (Codex `chatgpt_account_id`), else the login email for providers
|
|
442
|
+
* whose credentials carry no account id (Antigravity), else the project id for
|
|
443
|
+
* a login whose email lookup failed. Same precedence as the credential store's
|
|
444
|
+
* identity key.
|
|
445
|
+
*/
|
|
446
|
+
export declare function oauthAccountKey(identity: {
|
|
447
|
+
accountId?: string;
|
|
448
|
+
email?: string;
|
|
449
|
+
projectId?: string;
|
|
450
|
+
}): string | undefined;
|
|
432
451
|
/** Non-secret identity bound to the OAuth credential selected for one request attempt. */
|
|
433
452
|
export interface OAuthRequestIdentity {
|
|
434
453
|
orgId?: string;
|
|
@@ -814,9 +833,20 @@ export interface KeysApi {
|
|
|
814
833
|
* 6. Stored API key (e.g. a broker-migrated copy) — last resort, so an explicit env var wins
|
|
815
834
|
* 7. OMP compatibility mirror — below every stored credential, so a key the
|
|
816
835
|
* user saves locally always wins over the upstream fallback
|
|
836
|
+
*
|
|
837
|
+
* Resolves `undefined` when no permitted credential can serve, including when
|
|
838
|
+
* OAuth refresh failed transiently (network, timeout, 5xx), so availability
|
|
839
|
+
* probes move on to their next candidate. Request paths use
|
|
840
|
+
* {@link KeysApi.getWithCredential}, which surfaces that failure.
|
|
817
841
|
*/
|
|
818
842
|
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
|
|
819
|
-
/**
|
|
843
|
+
/**
|
|
844
|
+
* Resolve a bearer together with its durable stored credential row id, when known.
|
|
845
|
+
* Same precedence as {@link KeysApi.get}, but rejects with
|
|
846
|
+
* `OAuthRefreshUnavailableError` (transient, retryable) when OAuth refresh failed
|
|
847
|
+
* with a retryable error and no other permitted source could serve the request;
|
|
848
|
+
* the stored credential is kept.
|
|
849
|
+
*/
|
|
820
850
|
getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
|
|
821
851
|
/**
|
|
822
852
|
* Peek at API key for a provider without refreshing OAuth tokens.
|
|
@@ -913,7 +943,7 @@ export interface KeysApi {
|
|
|
913
943
|
modelId?: string;
|
|
914
944
|
}): ApiKeyResolver;
|
|
915
945
|
}
|
|
916
|
-
/** Controls whether a row-id refresh may reuse a
|
|
946
|
+
/** Controls whether a row-id refresh may reuse a recently minted token. */
|
|
917
947
|
export interface OAuthRefreshByIdOptions {
|
|
918
948
|
/**
|
|
919
949
|
* Return the stored credential when it still holds a fresh access token this
|
|
@@ -921,6 +951,12 @@ export interface OAuthRefreshByIdOptions {
|
|
|
921
951
|
* refresh tokens repeatedly when a provider rejects every valid bearer.
|
|
922
952
|
*/
|
|
923
953
|
reuseRecentMint?: boolean;
|
|
954
|
+
/**
|
|
955
|
+
* Provider-401 recovery intent. Forwarded to a delegated refresh (an auth
|
|
956
|
+
* broker), which may then return a token it minted in the last five minutes.
|
|
957
|
+
* Local reuse still requires {@link reuseRecentMint}.
|
|
958
|
+
*/
|
|
959
|
+
reason?: OAuthRefreshReason;
|
|
924
960
|
}
|
|
925
961
|
/** OAuth login, access, account identity, and refresh operations. */
|
|
926
962
|
export interface OAuthApi {
|
|
@@ -942,9 +978,11 @@ export interface OAuthApi {
|
|
|
942
978
|
* `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
|
|
943
979
|
* scenarios, prefer {@link AuthStorage.keys.get}.
|
|
944
980
|
*
|
|
945
|
-
* Returns `undefined` when no OAuth credential is available
|
|
946
|
-
*
|
|
947
|
-
* OAuth with an explicit API key.
|
|
981
|
+
* Returns `undefined` when no usable OAuth credential is available (none
|
|
982
|
+
* stored, or every one definitively failed to refresh) or runtime/config
|
|
983
|
+
* overrides have replaced OAuth with an explicit API key. Rejects with
|
|
984
|
+
* `OAuthRefreshUnavailableError` (transient, retryable) when a retryable
|
|
985
|
+
* refresh failure left no usable credential.
|
|
948
986
|
*/
|
|
949
987
|
access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
|
|
950
988
|
/**
|
|
@@ -960,9 +998,10 @@ export interface OAuthApi {
|
|
|
960
998
|
* Resolve one stored OAuth credential by its durable storage row id.
|
|
961
999
|
*
|
|
962
1000
|
* Unlike the normal session resolver, this method never ranks, rotates, or
|
|
963
|
-
* falls back to sibling credentials. A forced refresh re-mints only the
|
|
964
|
-
* requested row
|
|
965
|
-
*
|
|
1001
|
+
* falls back to sibling credentials. A generic forced refresh re-mints only the
|
|
1002
|
+
* requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
|
|
1003
|
+
* still-usable recent mint of that row. Either way exact-account affinity holds
|
|
1004
|
+
* for operations whose provenance and policy boundary are tied to one workspace.
|
|
966
1005
|
*
|
|
967
1006
|
* Returns `undefined` when the row does not exist for `provider` or an
|
|
968
1007
|
* explicit runtime/config API-key override suppresses OAuth.
|
|
@@ -996,8 +1035,9 @@ export interface OAuthApi {
|
|
|
996
1035
|
* Refresh the OAuth credential with the given id through a per-credential
|
|
997
1036
|
* single-flight. Concurrent callers for the same row await the same upstream
|
|
998
1037
|
* refresh attempt, which is required for providers that rotate refresh tokens
|
|
999
|
-
* on every successful refresh. Mints
|
|
1000
|
-
*
|
|
1038
|
+
* on every successful refresh. Mints unless {@link OAuthRefreshByIdOptions.reuseRecentMint}
|
|
1039
|
+
* finds a recent local mint, or a delegated refresh given
|
|
1040
|
+
* {@link OAuthRefreshByIdOptions.reason} `"auth-recovery"` returns its recent mint.
|
|
1001
1041
|
*/
|
|
1002
1042
|
refresh(id: number, signal?: AbortSignal, options?: OAuthRefreshByIdOptions): Promise<AuthCredentialSnapshotEntry>;
|
|
1003
1043
|
/**
|
|
@@ -1005,6 +1045,11 @@ export interface OAuthApi {
|
|
|
1005
1045
|
*/
|
|
1006
1046
|
refreshStored<T extends OAuthCredential = OAuthCredential>(provider: string, options: StoredOAuthRefreshOptions<T>): Promise<StoredOAuthRefreshResult<T>>;
|
|
1007
1047
|
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Handle for one {@link SessionsApi.restrict} call. {@link SessionsApi.unrestrict}
|
|
1050
|
+
* lifts a restriction only with the lease of the call that installed it.
|
|
1051
|
+
*/
|
|
1052
|
+
export type SessionRestrictionLease = symbol;
|
|
1008
1053
|
/** Session credential affinity operations. */
|
|
1009
1054
|
export interface SessionsApi {
|
|
1010
1055
|
/**
|
|
@@ -1024,14 +1069,42 @@ export interface SessionsApi {
|
|
|
1024
1069
|
restoredAtMs?: number;
|
|
1025
1070
|
}): boolean;
|
|
1026
1071
|
/**
|
|
1027
|
-
* Copy
|
|
1072
|
+
* Copy stored credential affinities from one live session to another.
|
|
1028
1073
|
*
|
|
1029
1074
|
* The target receives its own sticky entries, so request resolution, usage
|
|
1030
1075
|
* blocking, credential rotation, metadata, and persisted pins all continue
|
|
1031
1076
|
* through the target session id without retaining a live dependency on the
|
|
1032
|
-
* source session.
|
|
1077
|
+
* source session. `include` limits the copy to the providers it accepts,
|
|
1078
|
+
* given whether the source's affinity is an explicit user pin.
|
|
1079
|
+
*/
|
|
1080
|
+
inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
|
|
1081
|
+
/**
|
|
1082
|
+
* Restrict one session's credentials for `provider` to the OAuth accounts
|
|
1083
|
+
* whose identity key (`email:<address>|org:<id>` for org-scoped providers;
|
|
1084
|
+
* otherwise `account:`, `email:`, or `project:` — the keys broker account
|
|
1085
|
+
* pools use) is listed. Selection, session pins, inherited affinity,
|
|
1086
|
+
* fallback passes, and rotation never leave the list, and runtime, config,
|
|
1087
|
+
* environment, and stored API keys are not used for that session; when no
|
|
1088
|
+
* listed account can serve, key resolution fails instead of borrowing
|
|
1089
|
+
* another account. An empty list allows no account. Replaces any earlier
|
|
1090
|
+
* restriction for the same provider and session; {@link inherit} does not
|
|
1091
|
+
* copy restrictions. Restrictions are never evicted, because that would
|
|
1092
|
+
* widen a live session; the owner passes the returned lease to
|
|
1093
|
+
* {@link unrestrict} when the session ends.
|
|
1094
|
+
*/
|
|
1095
|
+
restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
|
|
1096
|
+
/**
|
|
1097
|
+
* Lift the restriction that `lease` installed. Once a later {@link restrict}
|
|
1098
|
+
* call has replaced it for the same provider and session, this does nothing,
|
|
1099
|
+
* so a stale owner never lifts the restriction a newer owner relies on.
|
|
1100
|
+
*/
|
|
1101
|
+
unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
|
|
1102
|
+
/**
|
|
1103
|
+
* Whether stored credential `credentialId` may serve `sessionId`: always for
|
|
1104
|
+
* an unrestricted session, otherwise only an OAuth account its
|
|
1105
|
+
* {@link restrict} list names.
|
|
1033
1106
|
*/
|
|
1034
|
-
|
|
1107
|
+
permits(provider: string, sessionId: string, credentialId: number): boolean;
|
|
1035
1108
|
/**
|
|
1036
1109
|
* Release a session's sticky credential so its next {@link getApiKey} call
|
|
1037
1110
|
* re-runs native pool ranking. This never blocks or penalizes the released
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP client for the
|
|
2
|
+
* HTTP client for the zetacode auth-broker server.
|
|
3
3
|
*
|
|
4
4
|
* Used by {@link RemoteAuthCredentialStore} (snapshot pulls) and by
|
|
5
|
-
* `
|
|
5
|
+
* `zetacode auth-broker status` (liveness checks). All endpoints except
|
|
6
6
|
* `/v1/healthz` require a bearer token.
|
|
7
7
|
*/
|
|
8
8
|
import type { AuthCredential, DisabledCredentialSummary, OAuthRefreshReason } from "../auth-storage.js";
|
|
@@ -85,7 +85,7 @@ export declare class AuthBrokerClient {
|
|
|
85
85
|
fetchClientUsageSummary(query?: {
|
|
86
86
|
sinceMs?: number;
|
|
87
87
|
}, signal?: AbortSignal): Promise<ClientUsageSummaryResponse>;
|
|
88
|
-
notifyUsageStale(signal?: AbortSignal): Promise<UsageStaleResponse>;
|
|
88
|
+
notifyUsageStale(provider?: string, signal?: AbortSignal): Promise<UsageStaleResponse>;
|
|
89
89
|
refreshCredential(id: number, signal?: AbortSignal, reason?: OAuthRefreshReason): Promise<CredentialRefreshResponse>;
|
|
90
90
|
disableCredential(id: number, cause: string, signal?: AbortSignal): Promise<CredentialDisableResponse>;
|
|
91
91
|
/**
|
|
@@ -18,7 +18,7 @@ export interface DiscoverAuthStorageOptions {
|
|
|
18
18
|
accountPolicies?: AuthAccountPolicies;
|
|
19
19
|
authStorageOptions?: Omit<AuthStorageOptions, "accountPolicies" | "configValueResolver" | "sourceLabel">;
|
|
20
20
|
}
|
|
21
|
-
/** Path to the local bearer token file. Created by `
|
|
21
|
+
/** Path to the local bearer token file. Created by `zetacode auth-broker token`. */
|
|
22
22
|
export declare function getAuthBrokerTokenFilePath(): string;
|
|
23
23
|
export interface AuthAccountPolicyConfig {
|
|
24
24
|
accountPolicies: AuthAccountPolicies;
|
|
@@ -73,6 +73,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
73
73
|
/** Broker-backed disabled tombstones; empty against brokers predating the endpoint. */
|
|
74
74
|
listDisabledCredentials(provider?: string, signal?: AbortSignal): Promise<DisabledCredentialSummary[]>;
|
|
75
75
|
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
76
|
+
getCredentialBlockScopes(credentialId: number, providerKey: string): Map<string, number>;
|
|
76
77
|
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
77
78
|
listCredentialBlocks(credentialIds: readonly number[]): StoredCredentialBlock[];
|
|
78
79
|
upsertCredentialBlock(block: StoredCredentialBlock): void;
|
|
@@ -121,7 +122,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
121
122
|
/** Drop all cache rows whose keys start with the supplied prefix. */
|
|
122
123
|
deleteCachePrefix(prefix: string): void;
|
|
123
124
|
cleanExpiredCache(): void;
|
|
124
|
-
invalidateUsageCache(signal?: AbortSignal): Promise<void>;
|
|
125
|
+
invalidateUsageCache(provider?: string, signal?: AbortSignal): Promise<void>;
|
|
125
126
|
/**
|
|
126
127
|
* Store-level hook consumed by `AuthStorage` — routes refresh through the
|
|
127
128
|
* broker so the actual refresh token never leaves the broker host. Returns
|
|
@@ -148,9 +149,10 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
148
149
|
ingestUsageReport(provider: Provider, credential: OAuthCredential, report: UsageReport): boolean;
|
|
149
150
|
/**
|
|
150
151
|
* Fold locally observed request usage into the pending report and schedule
|
|
151
|
-
* a flush. One `POST /v1/usage/observed` at most per flush interval
|
|
152
|
-
*
|
|
153
|
-
* (pre-endpoint broker) disables
|
|
152
|
+
* a flush. One `POST /v1/usage/observed` at most per flush interval, plus a
|
|
153
|
+
* final one when the process exits; on failure the batch is retained and
|
|
154
|
+
* retried with the next flush. A 404 (pre-endpoint broker) disables
|
|
155
|
+
* reporting for the life of this store.
|
|
154
156
|
*
|
|
155
157
|
* `client` overrides the reporting identity — the auth-gateway attributes
|
|
156
158
|
* each request to the originating install/app instead of the gateway host.
|
|
@@ -17,6 +17,8 @@ export interface AuthBrokerServerOptions {
|
|
|
17
17
|
bind?: string;
|
|
18
18
|
/** Accept any of these bearer tokens. Empty disables auth (loopback only). */
|
|
19
19
|
bearerTokens: string[];
|
|
20
|
+
/** Honor forwarded peer headers only when the connecting proxy is trusted. Default false. */
|
|
21
|
+
trustProxyHeaders?: boolean;
|
|
20
22
|
/** Broker version string surfaced on `/v1/healthz`. */
|
|
21
23
|
version?: string;
|
|
22
24
|
/** Refresh credentials expiring within this window. Default 5 min. */
|
|
@@ -15,3 +15,15 @@ export interface WriteAuthBrokerSnapshotCacheOptions {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function readAuthBrokerSnapshotCache(opts: ReadAuthBrokerSnapshotCacheOptions): Promise<SnapshotResponse | null>;
|
|
17
17
|
export declare function writeAuthBrokerSnapshotCache(opts: WriteAuthBrokerSnapshotCacheOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Fire-and-forget cache write for snapshot callbacks. The first snapshot after
|
|
20
|
+
* a quiet period is written immediately; later ones within
|
|
21
|
+
* {@link WRITE_COALESCE_MS} collapse into a single trailing write of the
|
|
22
|
+
* newest. A snapshot whose generation and credential content match the last
|
|
23
|
+
* write is skipped (see {@link UNCHANGED_REWRITE_MS}). Pending writes are
|
|
24
|
+
* flushed on process shutdown and by {@link flushAuthBrokerSnapshotCacheWrites};
|
|
25
|
+
* failures are logged, never thrown.
|
|
26
|
+
*/
|
|
27
|
+
export declare function scheduleAuthBrokerSnapshotCacheWrite(opts: WriteAuthBrokerSnapshotCacheOptions): void;
|
|
28
|
+
/** Write every pending scheduled snapshot now and wait for in-flight writes to settle. */
|
|
29
|
+
export declare function flushAuthBrokerSnapshotCacheWrites(): Promise<void>;
|