@ponythewhite/base-context 1.0.0 → 1.0.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 +33 -0
- package/NOTICE +0 -5
- package/README.md +44 -27
- package/dist/NOTICE +0 -5
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/src/rlm/__init__.py +7 -5
- package/dist/base-context-runtime/test/test_subagent_registry.py +28 -1
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/amazon-bedrock.js +21 -0
- package/dist/bundle/{anthropic-YY4S534V.js → anthropic-UV5GZDGU.js} +5 -5
- package/dist/bundle/{azure-openai-responses-JRP6MIM3.js → azure-openai-responses-WSNPXLYC.js} +6 -6
- package/dist/bundle/{bundled-modules-P5HARVZA.js → bundled-modules-TXUMJ2XQ.js} +71 -16
- package/dist/bundle/chunk-27QCJ2AZ.js +2578 -0
- package/dist/bundle/{chunk-MZHTNNVA.js → chunk-4GXPNXOJ.js} +8 -0
- package/dist/bundle/{chunk-SC5KG5R4.js → chunk-DGNX7HQ3.js} +1 -7
- package/dist/bundle/{chunk-A53O363M.js → chunk-GRJFXLBM.js} +0 -1
- package/dist/bundle/chunk-HKINIBP4.js +29 -0
- package/dist/bundle/{chunk-UDSW6FNG.js → chunk-KTNETTHI.js} +1 -1
- package/dist/bundle/{chunk-BYVP7LWV.js → chunk-S3INFPZZ.js} +1861 -4662
- package/dist/bundle/{chunk-73DCPU3D.js → chunk-V3L54UD6.js} +1019 -3427
- package/dist/bundle/{chunk-U7AUVG3B.js → chunk-VSIYFWYI.js} +24 -6
- package/dist/bundle/{chunk-IW77OXFS.js → chunk-W46VZFI6.js} +0 -2099
- package/dist/bundle/{cli-main-O4X7GFGY.js → cli-main-BYMSRPH6.js} +8 -6
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/{google-AUTVGDGZ.js → google-T32FMD4G.js} +6 -6
- package/dist/bundle/{google-vertex-Q75D7RKS.js → google-vertex-56KP6I3B.js} +3 -3
- package/dist/bundle/{main-XKOW2BV6.js → main-ODOYCEQC.js} +7 -5
- package/dist/bundle/{mistral-7ESB7QUD.js → mistral-WGBMYIVU.js} +5 -5
- package/dist/bundle/{openai-codex-responses-LV3QRPN4.js → openai-codex-responses-PIV2RQ3G.js} +6 -6
- package/dist/bundle/{openai-completions-OJECLPXJ.js → openai-completions-64C3JPR4.js} +11 -22
- package/dist/bundle/{openai-responses-FN6HZMBC.js → openai-responses-UI7RSTDV.js} +6 -6
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +108 -59
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/daemon-ps.d.ts +4 -3
- package/dist/cli/daemon-ps.d.ts.map +1 -1
- package/dist/cli/daemon-ps.js +6 -1
- package/dist/cli/daemon-ps.js.map +1 -1
- package/dist/cli/product-doctor.d.ts +0 -1
- package/dist/cli/product-doctor.d.ts.map +1 -1
- package/dist/cli/product-doctor.js +6 -3
- package/dist/cli/product-doctor.js.map +1 -1
- package/dist/config.d.ts +0 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -8
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-config.d.ts +1 -2
- package/dist/core/agent-session-config.d.ts.map +1 -1
- package/dist/core/agent-session-config.js +0 -3
- package/dist/core/agent-session-config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +5 -4
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +5 -3
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -27
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +10 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +107 -107
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +8 -35
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +33 -243
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/cron-jobs.d.ts.map +1 -1
- package/dist/core/cron-jobs.js +7 -1
- package/dist/core/cron-jobs.js.map +1 -1
- package/dist/core/inference-coordinator.d.ts +6 -1
- package/dist/core/inference-coordinator.d.ts.map +1 -1
- package/dist/core/inference-coordinator.js +18 -2
- package/dist/core/inference-coordinator.js.map +1 -1
- package/dist/core/kernel/bootstrap.d.ts.map +1 -1
- package/dist/core/kernel/bootstrap.js +2 -2
- package/dist/core/kernel/bootstrap.js.map +1 -1
- package/dist/core/kernel/repl-manager.d.ts +2 -0
- package/dist/core/kernel/repl-manager.d.ts.map +1 -1
- package/dist/core/kernel/repl-manager.js +44 -1
- package/dist/core/kernel/repl-manager.js.map +1 -1
- package/dist/core/logging.d.ts +4 -3
- package/dist/core/logging.d.ts.map +1 -1
- package/dist/core/logging.js +6 -6
- package/dist/core/logging.js.map +1 -1
- package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
- package/dist/core/mcp/mcp-manager.js +3 -3
- package/dist/core/mcp/mcp-manager.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -16
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +88 -196
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +2 -19
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +36 -266
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/provider-contracts.d.ts +5 -6
- package/dist/core/provider-contracts.d.ts.map +1 -1
- package/dist/core/provider-contracts.js +25 -36
- package/dist/core/provider-contracts.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +0 -2
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/rlm-max-subagents.d.ts +32 -0
- package/dist/core/rlm-max-subagents.d.ts.map +1 -0
- package/dist/core/rlm-max-subagents.js +50 -0
- package/dist/core/rlm-max-subagents.js.map +1 -0
- package/dist/core/rlm-runtime.d.ts +3 -3
- package/dist/core/rlm-runtime.d.ts.map +1 -1
- package/dist/core/rlm-runtime.js.map +1 -1
- package/dist/core/sdk.d.ts +4 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +27 -14
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +12 -44
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +6 -6
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/installer.mjs +2 -3
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +39 -34
- package/dist/main.js.map +1 -1
- package/dist/modes/acp/acp-mode.d.ts.map +1 -1
- package/dist/modes/acp/acp-mode.js +1 -1
- package/dist/modes/acp/acp-mode.js.map +1 -1
- package/dist/modes/agent-connection/daemon-agent-connection.d.ts +6 -2
- package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
- package/dist/modes/agent-connection/daemon-agent-connection.js +13 -2
- package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
- package/dist/modes/agent-connection/in-process-agent-connection.d.ts +2 -0
- package/dist/modes/agent-connection/in-process-agent-connection.d.ts.map +1 -1
- package/dist/modes/agent-connection/in-process-agent-connection.js +6 -0
- package/dist/modes/agent-connection/in-process-agent-connection.js.map +1 -1
- package/dist/modes/agent-connection/types.d.ts +3 -0
- package/dist/modes/agent-connection/types.d.ts.map +1 -1
- package/dist/modes/agent-connection/types.js.map +1 -1
- package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
- package/dist/modes/agents-view/agents-view-mode.js +2 -19
- package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
- package/dist/modes/daemon/daemon-mode.d.ts +5 -0
- package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-mode.js +107 -12
- package/dist/modes/daemon/daemon-mode.js.map +1 -1
- package/dist/modes/daemon/daemon-protocol.d.ts +194 -165
- package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-protocol.js +16 -11
- package/dist/modes/daemon/daemon-protocol.js.map +1 -1
- package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts +19 -0
- package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts.map +1 -0
- package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js +74 -0
- package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js.map +1 -0
- package/dist/modes/daemon/daemon-rlm-capacity.d.ts +19 -0
- package/dist/modes/daemon/daemon-rlm-capacity.d.ts.map +1 -0
- package/dist/modes/daemon/daemon-rlm-capacity.js +85 -0
- package/dist/modes/daemon/daemon-rlm-capacity.js.map +1 -0
- package/dist/modes/daemon/daemon-session-summarizer.d.ts +2 -3
- package/dist/modes/daemon/daemon-session-summarizer.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-session-summarizer.js +32 -23
- package/dist/modes/daemon/daemon-session-summarizer.js.map +1 -1
- package/dist/modes/daemon/daemon-state-root.d.ts +15 -0
- package/dist/modes/daemon/daemon-state-root.d.ts.map +1 -0
- package/dist/modes/daemon/daemon-state-root.js +40 -0
- package/dist/modes/daemon/daemon-state-root.js.map +1 -0
- package/dist/modes/daemon/daemon-supervisor-ownership.d.ts +2 -0
- package/dist/modes/daemon/daemon-supervisor-ownership.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-supervisor-ownership.js +27 -0
- package/dist/modes/daemon/daemon-supervisor-ownership.js.map +1 -1
- package/dist/modes/daemon/daemon-supervisor.d.ts +10 -1
- package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-supervisor.js +213 -20
- package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
- package/dist/modes/daemon/daemon-worker-protocol.d.ts +28 -1
- package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-worker-protocol.js +16 -2
- package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
- package/dist/modes/daemon/saved-session-catalog.d.ts.map +1 -1
- package/dist/modes/daemon/saved-session-catalog.js +2 -0
- package/dist/modes/daemon/saved-session-catalog.js.map +1 -1
- package/dist/modes/interactive/auth-flows.d.ts +0 -6
- package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
- package/dist/modes/interactive/auth-flows.js +6 -126
- package/dist/modes/interactive/auth-flows.js.map +1 -1
- package/dist/modes/interactive/components/configuration-menu.d.ts +3 -0
- package/dist/modes/interactive/components/configuration-menu.d.ts.map +1 -1
- package/dist/modes/interactive/components/configuration-menu.js +13 -2
- package/dist/modes/interactive/components/configuration-menu.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +2 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +0 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +0 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +3 -40
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +2 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +13 -2
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +2 -11
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
- package/dist/modes/interactive/feature-hints.js +0 -4
- package/dist/modes/interactive/feature-hints.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -12
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +81 -507
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/onboarding.d.ts +0 -3
- package/dist/modes/interactive/onboarding.d.ts.map +1 -1
- package/dist/modes/interactive/onboarding.js +0 -17
- package/dist/modes/interactive/onboarding.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +2 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +24 -11
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/node/amazon-bedrock.d.ts +2 -0
- package/dist/node/amazon-bedrock.d.ts.map +1 -0
- package/dist/node/amazon-bedrock.js +6 -0
- package/dist/node/amazon-bedrock.js.map +1 -0
- package/dist/product-identity.d.ts +0 -1
- package/dist/product-identity.d.ts.map +1 -1
- package/dist/product-identity.js +0 -1
- package/dist/product-identity.js.map +1 -1
- package/docs/development.md +1 -1
- package/docs/fork-philosophy.md +4 -8
- package/docs/installation.md +83 -62
- package/docs/providers.md +23 -25
- package/docs/quickstart.md +19 -10
- package/docs/rlm-runtime.md +13 -5
- package/docs/rpc.md +4 -4
- package/docs/sdk.md +20 -11
- package/docs/sessions.md +0 -1
- package/docs/settings.md +9 -42
- package/docs/skills.md +0 -1
- package/docs/upstream-0.9.5.md +171 -0
- package/docs/usage.md +14 -10
- package/package.json +4 -4
- package/dist/core/agent-traces.d.ts +0 -149
- package/dist/core/agent-traces.d.ts.map +0 -1
- package/dist/core/agent-traces.js +0 -904
- package/dist/core/agent-traces.js.map +0 -1
- package/dist/core/prime-inference-auth.d.ts +0 -70
- package/dist/core/prime-inference-auth.d.ts.map +0 -1
- package/dist/core/prime-inference-auth.js +0 -352
- package/dist/core/prime-inference-auth.js.map +0 -1
- package/dist/core/prime-inference-model-selection.d.ts +0 -16
- package/dist/core/prime-inference-model-selection.d.ts.map +0 -1
- package/dist/core/prime-inference-model-selection.js +0 -16
- package/dist/core/prime-inference-model-selection.js.map +0 -1
- package/dist/core/prime-inference-models.d.ts +0 -6
- package/dist/core/prime-inference-models.d.ts.map +0 -1
- package/dist/core/prime-inference-models.js +0 -62
- package/dist/core/prime-inference-models.js.map +0 -1
- package/dist/core/telemetry.d.ts +0 -95
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js +0 -637
- package/dist/core/telemetry.js.map +0 -1
- package/dist/modes/interactive/components/prime-onboarding-splash.d.ts +0 -39
- package/dist/modes/interactive/components/prime-onboarding-splash.d.ts.map +0 -1
- package/dist/modes/interactive/components/prime-onboarding-splash.js +0 -286
- package/dist/modes/interactive/components/prime-onboarding-splash.js.map +0 -1
- package/dist/modes/interactive/components/prime-team-selector.d.ts +0 -30
- package/dist/modes/interactive/components/prime-team-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/prime-team-selector.js +0 -166
- package/dist/modes/interactive/components/prime-team-selector.js.map +0 -1
- package/dist/skills/prime-intellect/SKILL.md +0 -87
- package/dist/skills/prime-intellect/references/compute.md +0 -45
- package/dist/skills/prime-intellect/references/environments.md +0 -73
- package/dist/skills/prime-intellect/references/inference.md +0 -47
- package/dist/skills/prime-intellect/references/sandboxes.md +0 -75
- package/skills/prime-intellect/SKILL.md +0 -87
- package/skills/prime-intellect/references/compute.md +0 -45
- package/skills/prime-intellect/references/environments.md +0 -73
- package/skills/prime-intellect/references/inference.md +0 -47
- package/skills/prime-intellect/references/sandboxes.md +0 -75
|
@@ -6,18 +6,9 @@
|
|
|
6
6
|
* try to refresh tokens simultaneously.
|
|
7
7
|
*/
|
|
8
8
|
import { type OAuthCredentials, type OAuthLoginCallbacks, type OAuthProviderId } from "@ponythewhite/base-context-ai";
|
|
9
|
-
import { type PrimeTeam } from "./prime-inference-auth.js";
|
|
10
|
-
export type PrimeTeamCredential = {
|
|
11
|
-
teamId: string;
|
|
12
|
-
name: string;
|
|
13
|
-
slug?: string;
|
|
14
|
-
role?: string;
|
|
15
|
-
createdAt?: string;
|
|
16
|
-
};
|
|
17
9
|
export type ApiKeyCredential = {
|
|
18
10
|
type: "api_key";
|
|
19
11
|
key: string;
|
|
20
|
-
primeTeam?: PrimeTeamCredential | null;
|
|
21
12
|
};
|
|
22
13
|
export type OAuthCredential = {
|
|
23
14
|
type: "oauth";
|
|
@@ -26,15 +17,9 @@ export type AuthCredential = ApiKeyCredential | OAuthCredential;
|
|
|
26
17
|
export type AuthStorageData = Record<string, AuthCredential>;
|
|
27
18
|
export type AuthStatus = {
|
|
28
19
|
configured: boolean;
|
|
29
|
-
source?: "stored" | "runtime" | "environment" | "
|
|
20
|
+
source?: "stored" | "runtime" | "environment" | "fallback" | "models_json_key" | "models_json_command" | "stale";
|
|
30
21
|
label?: string;
|
|
31
22
|
};
|
|
32
|
-
export type AuthStorageOptions = {
|
|
33
|
-
/** Optional explicit provider config; the default is Base Context prime-inference.json. */
|
|
34
|
-
primeCliConfigPath?: string;
|
|
35
|
-
/** Enable the isolated Prime Inference provider config. */
|
|
36
|
-
usePrimeCliConfig?: boolean;
|
|
37
|
-
};
|
|
38
23
|
type LockResult<T> = {
|
|
39
24
|
result: T;
|
|
40
25
|
next?: string;
|
|
@@ -73,7 +58,6 @@ export declare class InMemoryAuthStorageBackend implements AuthStorageBackend {
|
|
|
73
58
|
*/
|
|
74
59
|
export declare class AuthStorage {
|
|
75
60
|
private storage;
|
|
76
|
-
private options;
|
|
77
61
|
private readonly existingOpenAICodexSubscription;
|
|
78
62
|
private data;
|
|
79
63
|
private runtimeOverrides;
|
|
@@ -82,16 +66,16 @@ export declare class AuthStorage {
|
|
|
82
66
|
private loadError;
|
|
83
67
|
private errors;
|
|
84
68
|
private constructor();
|
|
85
|
-
static create(authPath?: string
|
|
69
|
+
static create(authPath?: string): AuthStorage;
|
|
86
70
|
/** Reuse an existing host login only through an explicitly read-only backend.
|
|
87
|
-
* This
|
|
71
|
+
* This SDK mode never logs in, writes credentials, or refreshes tokens.
|
|
88
72
|
*/
|
|
89
|
-
static fromStorage(storage: AuthStorageBackend, options?:
|
|
73
|
+
static fromStorage(storage: AuthStorageBackend, options?: {
|
|
90
74
|
existingOpenAICodexSubscription?: boolean;
|
|
91
75
|
}): AuthStorage;
|
|
92
76
|
isExistingOpenAICodexSubscription(provider: string): boolean;
|
|
93
77
|
private assertWritableStorage;
|
|
94
|
-
static inMemory(data?: AuthStorageData
|
|
78
|
+
static inMemory(data?: AuthStorageData): AuthStorage;
|
|
95
79
|
/**
|
|
96
80
|
* Set a runtime API key override (not persisted to disk).
|
|
97
81
|
* Used for CLI --api-key flag.
|
|
@@ -111,7 +95,6 @@ export declare class AuthStorage {
|
|
|
111
95
|
private createAuthSourceCandidate;
|
|
112
96
|
private getStoredCredentialValueMaterial;
|
|
113
97
|
private getRuntimeAuthCandidate;
|
|
114
|
-
private getPrimeCliAuthCandidate;
|
|
115
98
|
private getStoredAuthCandidate;
|
|
116
99
|
private getEnvironmentApiKey;
|
|
117
100
|
private getEnvironmentAuthCandidate;
|
|
@@ -188,8 +171,8 @@ export declare class AuthStorage {
|
|
|
188
171
|
* Get API key for a provider.
|
|
189
172
|
* Priority:
|
|
190
173
|
* 1. Runtime override (CLI --api-key)
|
|
191
|
-
* 2.
|
|
192
|
-
* 3.
|
|
174
|
+
* 2. auth.json
|
|
175
|
+
* 3. Environment variable
|
|
193
176
|
* 4. Fallback resolver (models.json custom providers)
|
|
194
177
|
*/
|
|
195
178
|
getApiKeyWithSourceToken(providerId: string, options?: {
|
|
@@ -199,19 +182,9 @@ export declare class AuthStorage {
|
|
|
199
182
|
includeFallback?: boolean;
|
|
200
183
|
}): Promise<string | undefined>;
|
|
201
184
|
/**
|
|
202
|
-
* Get registered OAuth providers
|
|
185
|
+
* Get supported registered OAuth providers. Read-only SDK storage cannot log in.
|
|
203
186
|
*/
|
|
204
187
|
getOAuthProviders(): import("@ponythewhite/base-context-ai").OAuthProviderInterface[];
|
|
205
|
-
setPrimeInferenceTeamSelection(team: PrimeTeam | null): void;
|
|
206
|
-
setPrimeInferenceApiKey(apiKey: string): void;
|
|
207
|
-
getPrimeInferenceTeamSelection(): PrimeTeamCredential | null | undefined;
|
|
208
|
-
getProviderHeaders(providerId: string): Record<string, string> | undefined;
|
|
209
|
-
getPrimeCliConfigPath(): string | undefined;
|
|
210
|
-
private toPrimeTeamCredential;
|
|
211
|
-
private getPrimeCliConfig;
|
|
212
|
-
private getPrimeCliApiKey;
|
|
213
|
-
private getEnabledPrimeCliConfigPath;
|
|
214
|
-
private isPrimeCliConfigEnabled;
|
|
215
188
|
}
|
|
216
189
|
export {};
|
|
217
190
|
//# sourceMappingURL=auth-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\tgetPrimeTeamId,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@ponythewhite/base-context-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.js\";\nimport { assertProductStatePath } from \"../runtime-paths.js\";\nimport {\n\tclearPrimeCliCredentials,\n\tgetPrimeCliConfigPath,\n\tloadPrimeCliConfig,\n\tPRIME_INFERENCE_PROVIDER_ID,\n\ttype PrimeCliConfig,\n\ttype PrimeTeam,\n\tsavePrimeCliApiKey,\n\tsavePrimeCliTeamSelection,\n} from \"./prime-inference-auth.js\";\nimport { getProviderAuthContract, isProviderApiKeyAllowed } from \"./provider-contracts.js\";\nimport { resolveConfigValue, resolveConfigValueUncached } from \"./resolve-config-value.js\";\n\nexport type PrimeTeamCredential = {\n\tteamId: string;\n\tname: string;\n\tslug?: string;\n\trole?: string;\n\tcreatedAt?: string;\n};\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n\tprimeTeam?: PrimeTeamCredential | null;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?:\n\t\t| \"stored\"\n\t\t| \"runtime\"\n\t\t| \"environment\"\n\t\t| \"prime_cli\"\n\t\t| \"fallback\"\n\t\t| \"models_json_key\"\n\t\t| \"models_json_command\"\n\t\t| \"stale\";\n\tlabel?: string;\n};\n\nexport type AuthStorageOptions = {\n\t/** Optional explicit provider config; the default is Base Context prime-inference.json. */\n\tprimeCliConfigPath?: string;\n\t/** Enable the isolated Prime Inference provider config. */\n\tusePrimeCliConfig?: boolean;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\ntype ActiveAuthStatusSource = Exclude<NonNullable<AuthStatus[\"source\"]>, \"stale\">;\n\nexport type AuthSourceToken = {\n\tprovider: string;\n\tsource: ActiveAuthStatusSource;\n\tidentityFingerprint: string;\n\tvalueFingerprint: string;\n};\n\ntype AuthSourceCandidate = {\n\tsource: ActiveAuthStatusSource;\n\tconfigured: boolean;\n\tlabel?: string;\n\tidentityFingerprint: string;\n\tvalueFingerprint?: string;\n\tresolveValueFingerprint?: () => string | undefined;\n};\n\ntype AuthApiKeyResult = {\n\tapiKey?: string;\n\tsourceToken?: AuthSourceToken;\n};\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tconstructor(private authPath: string = join(getAgentDir(), \"auth.json\")) {}\n\n\tprivate ensureParentDir(): void {\n\t\tassertProductStatePath(this.authPath);\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", \"utf-8\");\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\t\tlet compromisedError: Error | undefined;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst release = lockfile.lockSync(path, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tonCompromised: (error) => {\n\t\t\t\t\t\tcompromisedError ??= error;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (compromisedError) {\n\t\t\t\t\trelease();\n\t\t\t\t\tthrow compromisedError;\n\t\t\t\t}\n\t\t\t\treturn release;\n\t\t\t} catch (error) {\n\t\t\t\tif (compromisedError) throw compromisedError;\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, \"utf-8\");\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, \"utf-8\");\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\tif (lockCompromised) await release().catch(() => undefined);\n\t\t\t\telse await release();\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate staleAuthSources: Map<string, AuthSourceToken[]> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\n\tprivate constructor(\n\t\tprivate storage: AuthStorageBackend,\n\t\tprivate options: AuthStorageOptions = {},\n\t\tprivate readonly existingOpenAICodexSubscription = false,\n\t) {\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string, options?: AuthStorageOptions): AuthStorage {\n\t\tconst authOptions = options ?? { usePrimeCliConfig: authPath === undefined };\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")), authOptions);\n\t}\n\n\t/** Reuse an existing host login only through an explicitly read-only backend.\n\t * This does not authorize this distribution's OAuth login or refresh client.\n\t */\n\tstatic fromStorage(\n\t\tstorage: AuthStorageBackend,\n\t\toptions?: AuthStorageOptions & { existingOpenAICodexSubscription?: boolean },\n\t): AuthStorage {\n\t\tconst existing = options?.existingOpenAICodexSubscription === true;\n\t\tif (!existing) return new AuthStorage(storage, options);\n\t\tif (storage instanceof FileAuthStorageBackend)\n\t\t\tthrow new Error(\"Existing OpenAI subscription requires a read-only auth backend\");\n\t\tconst readOnly: AuthStorageBackend = {\n\t\t\twithLock: (read) =>\n\t\t\t\tstorage.withLock((current) => {\n\t\t\t\t\tconst { result, next } = read(current);\n\t\t\t\t\tif (next !== undefined) throw new Error(\"Existing OpenAI subscription storage is read-only\");\n\t\t\t\t\treturn { result };\n\t\t\t\t}),\n\t\t\t// Refresh happens inside this callback, so never invoke it in this mode.\n\t\t\twithLockAsync: async () => {\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription refresh is disabled\");\n\t\t\t},\n\t\t};\n\t\treturn new AuthStorage(readOnly, { ...options, usePrimeCliConfig: false }, true);\n\t}\n\n\tisExistingOpenAICodexSubscription(provider: string): boolean {\n\t\treturn this.existingOpenAICodexSubscription && provider === \"openai-codex\";\n\t}\n\n\tprivate assertWritableStorage(): void {\n\t\tif (this.existingOpenAICodexSubscription) throw new Error(\"Existing OpenAI subscription storage is read-only\");\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}, options?: AuthStorageOptions): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage, options);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.clearStaleAuthSource(provider, \"runtime\");\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.clearStaleAuthSource(provider, \"runtime\");\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate fingerprintAuthSource(source: ActiveAuthStatusSource, material: string): string {\n\t\tconst digest = createHash(\"sha256\").update(source).update(\"\\0\").update(material).digest(\"hex\");\n\t\treturn `${source}:${digest}`;\n\t}\n\n\tprivate createAuthSourceCandidate(options: {\n\t\tsource: ActiveAuthStatusSource;\n\t\tconfigured: boolean;\n\t\tidentityMaterial: string;\n\t\tvalueMaterial?: string;\n\t\tlabel?: string;\n\t\tresolveValueMaterial?: () => string | undefined;\n\t}): AuthSourceCandidate {\n\t\treturn {\n\t\t\tconfigured: options.configured,\n\t\t\tsource: options.source,\n\t\t\t...(options.label ? { label: options.label } : {}),\n\t\t\tidentityFingerprint: this.fingerprintAuthSource(options.source, `identity:${options.identityMaterial}`),\n\t\t\t...(options.valueMaterial !== undefined\n\t\t\t\t? {\n\t\t\t\t\t\tvalueFingerprint: this.fingerprintAuthSource(\n\t\t\t\t\t\t\toptions.source,\n\t\t\t\t\t\t\t`value:${options.identityMaterial}\\0${options.valueMaterial}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...(options.resolveValueMaterial\n\t\t\t\t? {\n\t\t\t\t\t\tresolveValueFingerprint: () => {\n\t\t\t\t\t\t\tconst valueMaterial = options.resolveValueMaterial?.();\n\t\t\t\t\t\t\treturn valueMaterial === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: this.fingerprintAuthSource(\n\t\t\t\t\t\t\t\t\t\toptions.source,\n\t\t\t\t\t\t\t\t\t\t`value:${options.identityMaterial}\\0${valueMaterial}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tprivate getStoredCredentialValueMaterial(providerId: string, credential: AuthCredential): string | undefined {\n\t\tif (credential.type === \"api_key\") {\n\t\t\tconst isCommand = credential.key.startsWith(\"!\");\n\t\t\tconst resolvedKey = isCommand\n\t\t\t\t? resolveConfigValueUncached(credential.key)\n\t\t\t\t: resolveConfigValue(credential.key);\n\t\t\tif (resolvedKey !== undefined && !isProviderApiKeyAllowed(providerId, resolvedKey)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (isCommand) {\n\t\t\t\treturn resolvedKey === undefined ? undefined : `api_key:command:${credential.key}\\0${resolvedKey}`;\n\t\t\t}\n\t\t\treturn `api_key:${credential.key}\\0${resolvedKey ?? \"\"}`;\n\t\t}\n\t\tif (this.isExistingOpenAICodexSubscription(providerId))\n\t\t\treturn `oauth:${credential.access}\\0${credential.expires}`;\n\t\tif (getProviderAuthContract(providerId).oauth !== \"validated\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tconst apiKey = provider?.getApiKey(credential) ?? credential.access;\n\t\treturn `oauth:${apiKey}\\0${credential.refresh}\\0${credential.expires}`;\n\t}\n\n\tprivate getRuntimeAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst apiKey = this.runtimeOverrides.get(provider);\n\t\tif (!apiKey || !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tlabel: \"--api-key\",\n\t\t\t...this.createAuthSourceCandidate({\n\t\t\t\tconfigured: false,\n\t\t\t\tsource: \"runtime\",\n\t\t\t\tidentityMaterial: provider,\n\t\t\t\tvalueMaterial: apiKey,\n\t\t\t}),\n\t\t};\n\t}\n\n\tprivate getPrimeCliAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst apiKey = this.getPrimeCliApiKey(provider);\n\t\tif (!apiKey) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tlabel: \"Base Context provider config\",\n\t\t\t...this.createAuthSourceCandidate({\n\t\t\t\tconfigured: false,\n\t\t\t\tsource: \"prime_cli\",\n\t\t\t\tidentityMaterial: provider,\n\t\t\t\tvalueMaterial: apiKey,\n\t\t\t}),\n\t\t};\n\t}\n\n\tprivate getStoredAuthCandidate(\n\t\tprovider: string,\n\t\toptions?: { resolveCommandValue?: boolean; resolvedCommandValue?: string },\n\t): AuthSourceCandidate | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (\n\t\t\t!credential ||\n\t\t\t(credential.type === \"oauth\" &&\n\t\t\t\tgetProviderAuthContract(provider).oauth !== \"validated\" &&\n\t\t\t\t!this.isExistingOpenAICodexSubscription(provider))\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst isCommandApiKey = credential.type === \"api_key\" && credential.key.startsWith(\"!\");\n\t\tif (credential.type === \"api_key\") {\n\t\t\tconst apiKey =\n\t\t\t\toptions?.resolvedCommandValue ?? (isCommandApiKey ? credential.key : resolveConfigValue(credential.key));\n\t\t\tif (apiKey !== undefined && !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\t\tconst identityMaterial = isCommandApiKey ? `api_key:command:${credential.key}` : `${provider}:${credential.type}`;\n\t\tconst commandValueMaterial =\n\t\t\tisCommandApiKey && options?.resolvedCommandValue !== undefined\n\t\t\t\t? `api_key:command:${credential.key}\\0${options.resolvedCommandValue}`\n\t\t\t\t: undefined;\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: true,\n\t\t\tsource: \"stored\",\n\t\t\tidentityMaterial,\n\t\t\tvalueMaterial:\n\t\t\t\tcommandValueMaterial ??\n\t\t\t\t(isCommandApiKey && !options?.resolveCommandValue\n\t\t\t\t\t? undefined\n\t\t\t\t\t: this.getStoredCredentialValueMaterial(provider, credential)),\n\t\t\tresolveValueMaterial: isCommandApiKey\n\t\t\t\t? () => this.getStoredCredentialValueMaterial(provider, credential)\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tprivate getEnvironmentApiKey(provider: string): string | undefined {\n\t\t// The generic resolver prefers ANTHROPIC_OAUTH_TOKEN, an unavailable subscription route.\n\t\tconst apiKey = provider === \"anthropic\" ? process.env.ANTHROPIC_API_KEY : getEnvApiKey(provider);\n\t\treturn apiKey && isProviderApiKeyAllowed(provider, apiKey) ? apiKey : undefined;\n\t}\n\n\tprivate getEnvironmentAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst envKeys = findEnvKeys(provider)?.filter((key) => key !== \"ANTHROPIC_OAUTH_TOKEN\");\n\t\tconst envKey = envKeys?.[0];\n\t\tconst apiKey = this.getEnvironmentApiKey(provider);\n\t\tif (!apiKey) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst label = envKey ?? \"ambient credentials\";\n\t\tconst identityMaterial = envKey ?? this.getAmbientEnvironmentIdentityMaterial(provider);\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: false,\n\t\t\tsource: \"environment\",\n\t\t\tlabel,\n\t\t\tidentityMaterial,\n\t\t\tvalueMaterial: `${identityMaterial}\\0${apiKey}`,\n\t\t});\n\t}\n\n\tprivate getAmbientEnvironmentIdentityMaterial(provider: string): string {\n\t\tif (provider === \"amazon-bedrock\") {\n\t\t\tif (process.env.AWS_PROFILE) return `amazon-bedrock:profile:${process.env.AWS_PROFILE}`;\n\t\t\tif (process.env.AWS_ACCESS_KEY_ID) {\n\t\t\t\treturn `amazon-bedrock:access-key:${process.env.AWS_ACCESS_KEY_ID}:${process.env.AWS_SECRET_ACCESS_KEY ?? \"\"}:${process.env.AWS_SESSION_TOKEN ?? \"\"}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_BEARER_TOKEN_BEDROCK) {\n\t\t\t\treturn `amazon-bedrock:bearer:${process.env.AWS_BEARER_TOKEN_BEDROCK}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {\n\t\t\t\treturn `amazon-bedrock:ecs-relative:${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI) {\n\t\t\t\treturn `amazon-bedrock:ecs-full:${process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_WEB_IDENTITY_TOKEN_FILE) {\n\t\t\t\treturn `amazon-bedrock:web-identity:${process.env.AWS_WEB_IDENTITY_TOKEN_FILE}`;\n\t\t\t}\n\t\t}\n\t\tif (provider === \"google-vertex\") {\n\t\t\tconst project = process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCLOUD_PROJECT ?? \"\";\n\t\t\tconst location = process.env.GOOGLE_CLOUD_LOCATION ?? \"\";\n\t\t\tconst credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS ?? \"application-default\";\n\t\t\treturn `google-vertex:${project}:${location}:${credentialsPath}`;\n\t\t}\n\t\treturn provider;\n\t}\n\n\tprivate getFallbackAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst apiKey = this.fallbackResolver?.(provider);\n\t\tif (!apiKey || !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: false,\n\t\t\tsource: \"fallback\",\n\t\t\tlabel: \"custom provider config\",\n\t\t\tidentityMaterial: provider,\n\t\t\tvalueMaterial: apiKey,\n\t\t});\n\t}\n\n\tprivate getAuthSourceCandidates(provider: string, options?: { includeFallback?: boolean }): AuthSourceCandidate[] {\n\t\tconst fallbackCandidate =\n\t\t\toptions?.includeFallback === false ? undefined : this.getFallbackAuthCandidate(provider);\n\t\tconst candidates =\n\t\t\tprovider === PRIME_INFERENCE_PROVIDER_ID\n\t\t\t\t? [\n\t\t\t\t\t\tthis.getRuntimeAuthCandidate(provider),\n\t\t\t\t\t\tthis.getEnvironmentAuthCandidate(provider),\n\t\t\t\t\t\tthis.getPrimeCliAuthCandidate(provider),\n\t\t\t\t\t\tthis.getStoredAuthCandidate(provider),\n\t\t\t\t\t\tfallbackCandidate,\n\t\t\t\t\t]\n\t\t\t\t: [\n\t\t\t\t\t\tthis.getRuntimeAuthCandidate(provider),\n\t\t\t\t\t\tthis.getStoredAuthCandidate(provider),\n\t\t\t\t\t\tthis.getEnvironmentAuthCandidate(provider),\n\t\t\t\t\t\tfallbackCandidate,\n\t\t\t\t\t];\n\t\treturn candidates.filter((candidate): candidate is AuthSourceCandidate => candidate !== undefined);\n\t}\n\n\tprivate isAuthSourceStale(provider: string, candidate: AuthSourceCandidate): boolean {\n\t\tconst matchingStale = this.getMatchingStaleAuthSources(provider, candidate);\n\t\tif (matchingStale.length === 0) {\n\t\t\treturn false;\n\t\t}\n\t\tconst valueFingerprint = candidate.valueFingerprint ?? candidate.resolveValueFingerprint?.();\n\t\treturn Boolean(valueFingerprint && matchingStale.some((token) => token.valueFingerprint === valueFingerprint));\n\t}\n\n\tprivate getMatchingStaleAuthSources(provider: string, candidate: AuthSourceCandidate): AuthSourceToken[] {\n\t\tconst stale = this.staleAuthSources.get(provider);\n\t\tif (!stale) {\n\t\t\treturn [];\n\t\t}\n\t\treturn stale.filter(\n\t\t\t(token) => token.source === candidate.source && token.identityFingerprint === candidate.identityFingerprint,\n\t\t);\n\t}\n\n\tprivate getAvailableAuthCandidate(\n\t\tprovider: string,\n\t\toptions?: { includeFallback?: boolean },\n\t): { candidate?: AuthSourceCandidate; hasStaleCandidate: boolean } {\n\t\tlet hasStaleCandidate = false;\n\t\tfor (const candidate of this.getAuthSourceCandidates(provider, options)) {\n\t\t\tif (this.isAuthSourceStale(provider, candidate)) {\n\t\t\t\thasStaleCandidate = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn { candidate, hasStaleCandidate };\n\t\t}\n\t\treturn { hasStaleCandidate };\n\t}\n\n\tprivate toAuthStatus(candidate: AuthSourceCandidate): AuthStatus {\n\t\treturn {\n\t\t\tconfigured: candidate.configured,\n\t\t\tsource: candidate.source,\n\t\t\t...(candidate.label ? { label: candidate.label } : {}),\n\t\t};\n\t}\n\n\tprivate getAuthStatusFromCandidates(provider: string): AuthStatus {\n\t\tconst { candidate, hasStaleCandidate } = this.getAvailableAuthCandidate(provider);\n\t\tif (candidate) {\n\t\t\treturn this.toAuthStatus(candidate);\n\t\t}\n\t\tif (hasStaleCandidate) {\n\t\t\treturn { configured: false, source: \"stale\", label: \"expired\" };\n\t\t}\n\t\treturn { configured: false };\n\t}\n\n\tmarkAuthStale(provider: string): boolean {\n\t\tconst token = this.getCurrentAuthSourceToken(provider);\n\t\treturn token ? this.markAuthSourceStale(token) : false;\n\t}\n\n\tprivate getAuthSourceTokenForCandidate(\n\t\tprovider: string,\n\t\tcandidate: AuthSourceCandidate,\n\t): AuthSourceToken | undefined {\n\t\tconst valueFingerprint = candidate.valueFingerprint ?? candidate.resolveValueFingerprint?.();\n\t\tif (!valueFingerprint) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tprovider,\n\t\t\tsource: candidate.source,\n\t\t\tidentityFingerprint: candidate.identityFingerprint,\n\t\t\tvalueFingerprint,\n\t\t};\n\t}\n\n\tgetCurrentAuthSourceToken(provider: string): AuthSourceToken | undefined {\n\t\tconst { candidate } = this.getAvailableAuthCandidate(provider);\n\t\tif (!candidate) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.getAuthSourceTokenForCandidate(provider, candidate);\n\t}\n\n\tmarkAuthSourceStale(token: AuthSourceToken): boolean {\n\t\tif (token.provider.length === 0) {\n\t\t\treturn false;\n\t\t}\n\t\tconst stale = this.staleAuthSources.get(token.provider) ?? [];\n\t\tif (\n\t\t\t!stale.some(\n\t\t\t\t(existing) =>\n\t\t\t\t\texisting.source === token.source &&\n\t\t\t\t\texisting.identityFingerprint === token.identityFingerprint &&\n\t\t\t\t\texisting.valueFingerprint === token.valueFingerprint,\n\t\t\t)\n\t\t) {\n\t\t\tstale.push(token);\n\t\t}\n\t\tthis.staleAuthSources.set(token.provider, stale);\n\t\treturn true;\n\t}\n\n\tprivate clearStaleAuthSource(provider: string, source: ActiveAuthStatusSource): void {\n\t\tconst stale = this.staleAuthSources.get(provider);\n\t\tif (!stale) {\n\t\t\treturn;\n\t\t}\n\t\tconst next = stale.filter((token) => token.source !== source);\n\t\tif (next.length === 0) {\n\t\t\tthis.staleAuthSources.delete(provider);\n\t\t} else {\n\t\t\tthis.staleAuthSources.set(provider, next);\n\t\t}\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * Remove a provider's credential with the disk write verified: throws on any\n\t * load or write failure instead of recording it, so callers can refuse to\n\t * proceed while the credential may still exist on disk. Disk-authoritative\n\t * and idempotent — in-memory state is only updated after the write succeeds.\n\t */\n\tremoveVerified(provider: string): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tif (!(provider in currentData)) return { result: undefined };\n\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\tdelete merged[provider];\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t\tdelete this.data[provider];\n\t\t// Post-success only: a failed removal must not make a stale-marked credential selectable again.\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\treturn this.getAvailableAuthCandidate(provider).candidate !== undefined;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\treturn this.getAuthStatusFromCandidates(provider);\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tthis.assertWritableStorage();\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"validated\") {\n\t\t\tthrow new Error(contract.guidance);\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tif (provider === PRIME_INFERENCE_PROVIDER_ID && this.isPrimeCliConfigEnabled()) {\n\t\t\ttry {\n\t\t\t\tclearPrimeCliCredentials(this.getEnabledPrimeCliConfigPath());\n\t\t\t\tthis.clearStaleAuthSource(provider, \"prime_cli\");\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tif (getProviderAuthContract(providerId).oauth !== \"validated\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\" || getProviderAuthContract(providerId).oauth !== \"validated\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. Prime Inference: environment variable, Base Context provider config, auth.json\n\t * 3. Other providers: auth.json, environment variable\n\t * 4. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKeyWithSourceToken(\n\t\tproviderId: string,\n\t\toptions?: { includeFallback?: boolean },\n\t): Promise<AuthApiKeyResult> {\n\t\tif (this.isExistingOpenAICodexSubscription(providerId)) {\n\t\t\tif (this.runtimeOverrides.has(providerId))\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription does not accept API-key overrides\");\n\t\t\tconst credential = this.data[providerId];\n\t\t\tif (\n\t\t\t\t!credential ||\n\t\t\t\tcredential.type !== \"oauth\" ||\n\t\t\t\ttypeof credential.access !== \"string\" ||\n\t\t\t\t!credential.access ||\n\t\t\t\t!Number.isFinite(credential.expires)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription credential is unavailable\");\n\t\t\tif (Date.now() >= credential.expires) throw new Error(\"Existing OpenAI subscription credential has expired\");\n\t\t\tconst candidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (!candidate || this.isAuthSourceStale(providerId, candidate))\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription credential is unavailable\");\n\t\t\treturn {\n\t\t\t\tapiKey: credential.access,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, candidate),\n\t\t\t};\n\t\t}\n\n\t\t// Runtime overrides take precedence over stored credentials and environment keys.\n\t\tconst runtimeCandidate = this.getRuntimeAuthCandidate(providerId);\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey && runtimeCandidate && !this.isAuthSourceStale(providerId, runtimeCandidate)) {\n\t\t\treturn {\n\t\t\t\tapiKey: runtimeKey,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, runtimeCandidate),\n\t\t\t};\n\t\t}\n\n\t\tconst envCandidate = this.getEnvironmentAuthCandidate(providerId);\n\t\tconst envKey = this.getEnvironmentApiKey(providerId);\n\t\tif (\n\t\t\tproviderId === PRIME_INFERENCE_PROVIDER_ID &&\n\t\t\tenvKey &&\n\t\t\tenvCandidate &&\n\t\t\t!this.isAuthSourceStale(providerId, envCandidate)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tapiKey: envKey,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, envCandidate),\n\t\t\t};\n\t\t}\n\n\t\tif (providerId === PRIME_INFERENCE_PROVIDER_ID) {\n\t\t\tconst primeCliCandidate = this.getPrimeCliAuthCandidate(providerId);\n\t\t\tconst primeCliKey = this.getPrimeCliApiKey(providerId);\n\t\t\tif (primeCliKey && primeCliCandidate && !this.isAuthSourceStale(providerId, primeCliCandidate)) {\n\t\t\t\treturn {\n\t\t\t\t\tapiKey: primeCliKey,\n\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, primeCliCandidate),\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\tconst storedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (storedCandidate && !this.isAuthSourceStale(providerId, storedCandidate)) {\n\t\t\t\tconst hasStaleRecord = this.getMatchingStaleAuthSources(providerId, storedCandidate).length > 0;\n\t\t\t\tconst apiKey =\n\t\t\t\t\tcred.key.startsWith(\"!\") && hasStaleRecord\n\t\t\t\t\t\t? resolveConfigValueUncached(cred.key)\n\t\t\t\t\t\t: resolveConfigValue(cred.key);\n\t\t\t\tif (apiKey === undefined || isProviderApiKeyAllowed(providerId, apiKey)) {\n\t\t\t\t\tconst sourceToken =\n\t\t\t\t\t\tapiKey === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: this.getAuthSourceTokenForCandidate(\n\t\t\t\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\t\t\t\tcred.key.startsWith(\"!\")\n\t\t\t\t\t\t\t\t\t\t? (this.getStoredAuthCandidate(providerId, { resolvedCommandValue: apiKey }) ??\n\t\t\t\t\t\t\t\t\t\t\t\tstoredCandidate)\n\t\t\t\t\t\t\t\t\t\t: storedCandidate,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\treturn { apiKey, sourceToken };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (cred?.type === \"oauth\" && getProviderAuthContract(providerId).oauth === \"validated\") {\n\t\t\tconst storedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (storedCandidate && !this.isAuthSourceStale(providerId, storedCandidate)) {\n\t\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\t\tif (!provider) {\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\t\t\t\t// Lock refreshes so concurrent instances cannot race on the credential file.\n\t\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\t\tif (needsRefresh) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\tconst refreshedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tapiKey: result.apiKey,\n\t\t\t\t\t\t\t\tsourceToken: refreshedCandidate\n\t\t\t\t\t\t\t\t\t? this.getAuthSourceTokenForCandidate(providerId, refreshedCandidate)\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t\t// A peer may have refreshed successfully; reload before treating this refresh as failed.\n\t\t\t\t\t\tthis.reload();\n\t\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tupdatedCred?.type === \"oauth\" &&\n\t\t\t\t\t\t\tgetProviderAuthContract(providerId).oauth === \"validated\" &&\n\t\t\t\t\t\t\tDate.now() < updatedCred.expires\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst updatedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tapiKey: provider.getApiKey(updatedCred),\n\t\t\t\t\t\t\t\tsourceToken: updatedCandidate\n\t\t\t\t\t\t\t\t\t? this.getAuthSourceTokenForCandidate(providerId, updatedCandidate)\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Preserve credentials for a later /login retry while discovery skips this provider.\n\t\t\t\t\t\treturn {};\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tapiKey: provider.getApiKey(cred),\n\t\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, storedCandidate),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Stored auth wins over environment variables for non-Prime-Inference providers.\n\t\tif (\n\t\t\tproviderId !== PRIME_INFERENCE_PROVIDER_ID &&\n\t\t\tenvKey &&\n\t\t\tenvCandidate &&\n\t\t\t!this.isAuthSourceStale(providerId, envCandidate)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tapiKey: envKey,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, envCandidate),\n\t\t\t};\n\t\t}\n\t\tif (options?.includeFallback !== false) {\n\t\t\tconst fallbackCandidate = this.getFallbackAuthCandidate(providerId);\n\t\t\tif (fallbackCandidate && !this.isAuthSourceStale(providerId, fallbackCandidate)) {\n\t\t\t\tconst apiKey = this.fallbackResolver?.(providerId) ?? undefined;\n\t\t\t\tif (apiKey === undefined || isProviderApiKeyAllowed(providerId, apiKey)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tapiKey,\n\t\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, fallbackCandidate),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {};\n\t}\n\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\tconst result = await this.getApiKeyWithSourceToken(providerId, options);\n\t\treturn result.apiKey;\n\t}\n\n\t/**\n\t * Get registered OAuth providers validated for this distribution.\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders().filter((provider) => getProviderAuthContract(provider.id).oauth === \"validated\");\n\t}\n\n\tsetPrimeInferenceTeamSelection(team: PrimeTeam | null): void {\n\t\tif (this.isPrimeCliConfigEnabled()) {\n\t\t\ttry {\n\t\t\t\tsavePrimeCliTeamSelection(team, this.getEnabledPrimeCliConfigPath());\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst credential = this.data[PRIME_INFERENCE_PROVIDER_ID];\n\t\tif (credential?.type !== \"api_key\") {\n\t\t\treturn;\n\t\t}\n\t\tthis.set(PRIME_INFERENCE_PROVIDER_ID, {\n\t\t\t...credential,\n\t\t\tprimeTeam: team ? this.toPrimeTeamCredential(team) : null,\n\t\t});\n\t}\n\n\tsetPrimeInferenceApiKey(apiKey: string): void {\n\t\tif (this.isPrimeCliConfigEnabled()) {\n\t\t\ttry {\n\t\t\t\tconst configPath = this.getEnabledPrimeCliConfigPath();\n\t\t\t\tconst config = loadPrimeCliConfig(configPath);\n\t\t\t\tconst existingCredential = this.data[PRIME_INFERENCE_PROVIDER_ID];\n\t\t\t\tconst legacyPrimeTeam = existingCredential?.type === \"api_key\" ? existingCredential.primeTeam : undefined;\n\t\t\t\tif (config.apiKey !== apiKey) {\n\t\t\t\t\tsavePrimeCliApiKey(apiKey, configPath);\n\t\t\t\t} else if (!config.teamIdFromEnv && (legacyPrimeTeam === null || (!config.teamId && legacyPrimeTeam))) {\n\t\t\t\t\tsavePrimeCliTeamSelection(legacyPrimeTeam, configPath);\n\t\t\t\t}\n\t\t\t\tthis.clearStaleAuthSource(PRIME_INFERENCE_PROVIDER_ID, \"prime_cli\");\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (this.data[PRIME_INFERENCE_PROVIDER_ID]) {\n\t\t\t\tthis.remove(PRIME_INFERENCE_PROVIDER_ID);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst existingCredential = this.data[PRIME_INFERENCE_PROVIDER_ID];\n\t\tconst existingPrimeTeam = existingCredential?.type === \"api_key\" ? existingCredential.primeTeam : undefined;\n\t\tthis.set(PRIME_INFERENCE_PROVIDER_ID, {\n\t\t\ttype: \"api_key\",\n\t\t\tkey: apiKey,\n\t\t\t...(existingPrimeTeam !== undefined ? { primeTeam: existingPrimeTeam } : {}),\n\t\t});\n\t}\n\n\tgetPrimeInferenceTeamSelection(): PrimeTeamCredential | null | undefined {\n\t\tif (getPrimeTeamId()) {\n\t\t\treturn undefined;\n\t\t}\n\t\tlet config: PrimeCliConfig | undefined;\n\t\tif (this.isPrimeCliConfigEnabled()) {\n\t\t\tconfig = this.getPrimeCliConfig(PRIME_INFERENCE_PROVIDER_ID);\n\t\t\tif (config?.teamIdFromEnv) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tconst credential = this.data[PRIME_INFERENCE_PROVIDER_ID];\n\t\tconst authSource = this.getAuthStatus(PRIME_INFERENCE_PROVIDER_ID).source;\n\t\tif (authSource === \"runtime\" || authSource === \"environment\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (authSource === \"prime_cli\") {\n\t\t\tif (credential?.type === \"api_key\" && credential.primeTeam === null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (config?.teamId) {\n\t\t\t\treturn this.toPrimeTeamCredential({\n\t\t\t\t\tteamId: config.teamId,\n\t\t\t\t\tname: config.teamName ?? \"Prime Inference team\",\n\t\t\t\t\t...(config.teamRole ? { role: config.teamRole } : {}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (credential?.type === \"api_key\" && credential.primeTeam) {\n\t\t\t\treturn credential.primeTeam;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (credential?.type === \"api_key\" && credential.primeTeam !== undefined) {\n\t\t\treturn credential.primeTeam;\n\t\t}\n\t\tif (!config?.apiKey && config?.teamId) {\n\t\t\treturn this.toPrimeTeamCredential({\n\t\t\t\tteamId: config.teamId,\n\t\t\t\tname: config.teamName ?? \"Prime Inference team\",\n\t\t\t\t...(config.teamRole ? { role: config.teamRole } : {}),\n\t\t\t});\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tgetProviderHeaders(providerId: string): Record<string, string> | undefined {\n\t\tif (providerId !== PRIME_INFERENCE_PROVIDER_ID) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst environmentTeamId = getPrimeTeamId();\n\t\tif (environmentTeamId) {\n\t\t\treturn { \"X-Prime-Team-ID\": environmentTeamId };\n\t\t}\n\n\t\tconst teamId = this.getPrimeInferenceTeamSelection()?.teamId;\n\t\treturn teamId ? { \"X-Prime-Team-ID\": teamId } : undefined;\n\t}\n\n\tgetPrimeCliConfigPath(): string | undefined {\n\t\tif (!this.isPrimeCliConfigEnabled()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn getPrimeCliConfigPath(this.options.primeCliConfigPath);\n\t}\n\n\tprivate toPrimeTeamCredential(team: PrimeTeam): PrimeTeamCredential {\n\t\tconst credential: PrimeTeamCredential = {\n\t\t\tteamId: team.teamId,\n\t\t\tname: team.name,\n\t\t};\n\t\tif (team.slug) {\n\t\t\tcredential.slug = team.slug;\n\t\t}\n\t\tif (team.role) {\n\t\t\tcredential.role = team.role;\n\t\t}\n\t\tif (team.createdAt) {\n\t\t\tcredential.createdAt = team.createdAt;\n\t\t}\n\t\treturn credential;\n\t}\n\n\tprivate getPrimeCliConfig(providerId: string): PrimeCliConfig | undefined {\n\t\tif (providerId !== PRIME_INFERENCE_PROVIDER_ID) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!this.isPrimeCliConfigEnabled()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn loadPrimeCliConfig(this.options.primeCliConfigPath);\n\t}\n\n\tprivate getPrimeCliApiKey(providerId: string): string | undefined {\n\t\treturn this.getPrimeCliConfig(providerId)?.apiKey;\n\t}\n\n\tprivate getEnabledPrimeCliConfigPath(): string {\n\t\tconst configPath = this.getPrimeCliConfigPath();\n\t\tif (!configPath) {\n\t\t\tthrow new Error(\"Base Context Prime Inference config is not enabled\");\n\t\t}\n\t\treturn configPath;\n\t}\n\n\tprivate isPrimeCliConfigEnabled(): boolean {\n\t\treturn Boolean(this.options.usePrimeCliConfig || this.options.primeCliConfigPath);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@ponythewhite/base-context-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.js\";\nimport { assertProductStatePath } from \"../runtime-paths.js\";\nimport { getProviderAuthContract, isProviderApiKeyAllowed } from \"./provider-contracts.js\";\nimport { resolveConfigValue, resolveConfigValueUncached } from \"./resolve-config-value.js\";\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\" | \"stale\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\ntype ActiveAuthStatusSource = Exclude<NonNullable<AuthStatus[\"source\"]>, \"stale\">;\n\nexport type AuthSourceToken = {\n\tprovider: string;\n\tsource: ActiveAuthStatusSource;\n\tidentityFingerprint: string;\n\tvalueFingerprint: string;\n};\n\ntype AuthSourceCandidate = {\n\tsource: ActiveAuthStatusSource;\n\tconfigured: boolean;\n\tlabel?: string;\n\tidentityFingerprint: string;\n\tvalueFingerprint?: string;\n\tresolveValueFingerprint?: () => string | undefined;\n};\n\ntype AuthApiKeyResult = {\n\tapiKey?: string;\n\tsourceToken?: AuthSourceToken;\n};\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tconstructor(private authPath: string = join(getAgentDir(), \"auth.json\")) {}\n\n\tprivate ensureParentDir(): void {\n\t\tassertProductStatePath(this.authPath);\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", \"utf-8\");\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\t\tlet compromisedError: Error | undefined;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst release = lockfile.lockSync(path, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tonCompromised: (error) => {\n\t\t\t\t\t\tcompromisedError ??= error;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (compromisedError) {\n\t\t\t\t\trelease();\n\t\t\t\t\tthrow compromisedError;\n\t\t\t\t}\n\t\t\t\treturn release;\n\t\t\t} catch (error) {\n\t\t\t\tif (compromisedError) throw compromisedError;\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, \"utf-8\");\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, \"utf-8\");\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\tif (lockCompromised) await release().catch(() => undefined);\n\t\t\t\telse await release();\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate staleAuthSources: Map<string, AuthSourceToken[]> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\n\tprivate constructor(\n\t\tprivate storage: AuthStorageBackend,\n\t\tprivate readonly existingOpenAICodexSubscription = false,\n\t) {\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\t/** Reuse an existing host login only through an explicitly read-only backend.\n\t * This SDK mode never logs in, writes credentials, or refreshes tokens.\n\t */\n\tstatic fromStorage(\n\t\tstorage: AuthStorageBackend,\n\t\toptions?: { existingOpenAICodexSubscription?: boolean },\n\t): AuthStorage {\n\t\tconst existing = options?.existingOpenAICodexSubscription === true;\n\t\tif (!existing) return new AuthStorage(storage);\n\t\tif (storage instanceof FileAuthStorageBackend)\n\t\t\tthrow new Error(\"Existing OpenAI subscription requires a read-only auth backend\");\n\t\tconst readOnly: AuthStorageBackend = {\n\t\t\twithLock: (read) =>\n\t\t\t\tstorage.withLock((current) => {\n\t\t\t\t\tconst { result, next } = read(current);\n\t\t\t\t\tif (next !== undefined) throw new Error(\"Existing OpenAI subscription storage is read-only\");\n\t\t\t\t\treturn { result };\n\t\t\t\t}),\n\t\t\t// Refresh happens inside this callback, so never invoke it in this mode.\n\t\t\twithLockAsync: async () => {\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription refresh is disabled\");\n\t\t\t},\n\t\t};\n\t\treturn new AuthStorage(readOnly, true);\n\t}\n\n\tisExistingOpenAICodexSubscription(provider: string): boolean {\n\t\treturn this.existingOpenAICodexSubscription && provider === \"openai-codex\";\n\t}\n\n\tprivate assertWritableStorage(): void {\n\t\tif (this.existingOpenAICodexSubscription) throw new Error(\"Existing OpenAI subscription storage is read-only\");\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.clearStaleAuthSource(provider, \"runtime\");\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.clearStaleAuthSource(provider, \"runtime\");\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate fingerprintAuthSource(source: ActiveAuthStatusSource, material: string): string {\n\t\tconst digest = createHash(\"sha256\").update(source).update(\"\\0\").update(material).digest(\"hex\");\n\t\treturn `${source}:${digest}`;\n\t}\n\n\tprivate createAuthSourceCandidate(options: {\n\t\tsource: ActiveAuthStatusSource;\n\t\tconfigured: boolean;\n\t\tidentityMaterial: string;\n\t\tvalueMaterial?: string;\n\t\tlabel?: string;\n\t\tresolveValueMaterial?: () => string | undefined;\n\t}): AuthSourceCandidate {\n\t\treturn {\n\t\t\tconfigured: options.configured,\n\t\t\tsource: options.source,\n\t\t\t...(options.label ? { label: options.label } : {}),\n\t\t\tidentityFingerprint: this.fingerprintAuthSource(options.source, `identity:${options.identityMaterial}`),\n\t\t\t...(options.valueMaterial !== undefined\n\t\t\t\t? {\n\t\t\t\t\t\tvalueFingerprint: this.fingerprintAuthSource(\n\t\t\t\t\t\t\toptions.source,\n\t\t\t\t\t\t\t`value:${options.identityMaterial}\\0${options.valueMaterial}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...(options.resolveValueMaterial\n\t\t\t\t? {\n\t\t\t\t\t\tresolveValueFingerprint: () => {\n\t\t\t\t\t\t\tconst valueMaterial = options.resolveValueMaterial?.();\n\t\t\t\t\t\t\treturn valueMaterial === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: this.fingerprintAuthSource(\n\t\t\t\t\t\t\t\t\t\toptions.source,\n\t\t\t\t\t\t\t\t\t\t`value:${options.identityMaterial}\\0${valueMaterial}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tprivate getStoredCredentialValueMaterial(providerId: string, credential: AuthCredential): string | undefined {\n\t\tif (credential.type === \"api_key\") {\n\t\t\tconst isCommand = credential.key.startsWith(\"!\");\n\t\t\tconst resolvedKey = isCommand\n\t\t\t\t? resolveConfigValueUncached(credential.key)\n\t\t\t\t: resolveConfigValue(credential.key);\n\t\t\tif (resolvedKey !== undefined && !isProviderApiKeyAllowed(providerId, resolvedKey)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (isCommand) {\n\t\t\t\treturn resolvedKey === undefined ? undefined : `api_key:command:${credential.key}\\0${resolvedKey}`;\n\t\t\t}\n\t\t\treturn `api_key:${credential.key}\\0${resolvedKey ?? \"\"}`;\n\t\t}\n\t\tif (this.isExistingOpenAICodexSubscription(providerId))\n\t\t\treturn `oauth:${credential.access}\\0${credential.expires}`;\n\t\tif (getProviderAuthContract(providerId).oauth !== \"supported\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tconst apiKey = provider?.getApiKey(credential) ?? credential.access;\n\t\treturn `oauth:${apiKey}\\0${credential.refresh}\\0${credential.expires}`;\n\t}\n\n\tprivate getRuntimeAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst apiKey = this.runtimeOverrides.get(provider);\n\t\tif (!apiKey || !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tlabel: \"--api-key\",\n\t\t\t...this.createAuthSourceCandidate({\n\t\t\t\tconfigured: false,\n\t\t\t\tsource: \"runtime\",\n\t\t\t\tidentityMaterial: provider,\n\t\t\t\tvalueMaterial: apiKey,\n\t\t\t}),\n\t\t};\n\t}\n\n\tprivate getStoredAuthCandidate(\n\t\tprovider: string,\n\t\toptions?: { resolveCommandValue?: boolean; resolvedCommandValue?: string },\n\t): AuthSourceCandidate | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (\n\t\t\t!credential ||\n\t\t\t(credential.type === \"oauth\" &&\n\t\t\t\tgetProviderAuthContract(provider).oauth !== \"supported\" &&\n\t\t\t\t!this.isExistingOpenAICodexSubscription(provider))\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst isCommandApiKey = credential.type === \"api_key\" && credential.key.startsWith(\"!\");\n\t\tif (credential.type === \"api_key\") {\n\t\t\tconst apiKey =\n\t\t\t\toptions?.resolvedCommandValue ?? (isCommandApiKey ? credential.key : resolveConfigValue(credential.key));\n\t\t\tif (apiKey !== undefined && !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\t\tconst identityMaterial = isCommandApiKey ? `api_key:command:${credential.key}` : `${provider}:${credential.type}`;\n\t\tconst commandValueMaterial =\n\t\t\tisCommandApiKey && options?.resolvedCommandValue !== undefined\n\t\t\t\t? `api_key:command:${credential.key}\\0${options.resolvedCommandValue}`\n\t\t\t\t: undefined;\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: true,\n\t\t\tsource: \"stored\",\n\t\t\tidentityMaterial,\n\t\t\tvalueMaterial:\n\t\t\t\tcommandValueMaterial ??\n\t\t\t\t(isCommandApiKey && !options?.resolveCommandValue\n\t\t\t\t\t? undefined\n\t\t\t\t\t: this.getStoredCredentialValueMaterial(provider, credential)),\n\t\t\tresolveValueMaterial: isCommandApiKey\n\t\t\t\t? () => this.getStoredCredentialValueMaterial(provider, credential)\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tprivate getEnvironmentApiKey(provider: string): string | undefined {\n\t\tconst apiKey = getEnvApiKey(provider);\n\t\treturn apiKey && isProviderApiKeyAllowed(provider, apiKey) ? apiKey : undefined;\n\t}\n\n\tprivate getEnvironmentAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tconst envKey = envKeys?.[0];\n\t\tconst apiKey = this.getEnvironmentApiKey(provider);\n\t\tif (!apiKey) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst label = envKey ?? \"ambient credentials\";\n\t\tconst identityMaterial = envKey ?? this.getAmbientEnvironmentIdentityMaterial(provider);\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: false,\n\t\t\tsource: \"environment\",\n\t\t\tlabel,\n\t\t\tidentityMaterial,\n\t\t\tvalueMaterial: `${identityMaterial}\\0${apiKey}`,\n\t\t});\n\t}\n\n\tprivate getAmbientEnvironmentIdentityMaterial(provider: string): string {\n\t\tif (provider === \"amazon-bedrock\") {\n\t\t\tif (process.env.AWS_PROFILE) return `amazon-bedrock:profile:${process.env.AWS_PROFILE}`;\n\t\t\tif (process.env.AWS_ACCESS_KEY_ID) {\n\t\t\t\treturn `amazon-bedrock:access-key:${process.env.AWS_ACCESS_KEY_ID}:${process.env.AWS_SECRET_ACCESS_KEY ?? \"\"}:${process.env.AWS_SESSION_TOKEN ?? \"\"}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_BEARER_TOKEN_BEDROCK) {\n\t\t\t\treturn `amazon-bedrock:bearer:${process.env.AWS_BEARER_TOKEN_BEDROCK}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {\n\t\t\t\treturn `amazon-bedrock:ecs-relative:${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI) {\n\t\t\t\treturn `amazon-bedrock:ecs-full:${process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI}`;\n\t\t\t}\n\t\t\tif (process.env.AWS_WEB_IDENTITY_TOKEN_FILE) {\n\t\t\t\treturn `amazon-bedrock:web-identity:${process.env.AWS_WEB_IDENTITY_TOKEN_FILE}`;\n\t\t\t}\n\t\t}\n\t\tif (provider === \"google-vertex\") {\n\t\t\tconst project = process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCLOUD_PROJECT ?? \"\";\n\t\t\tconst location = process.env.GOOGLE_CLOUD_LOCATION ?? \"\";\n\t\t\tconst credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS ?? \"application-default\";\n\t\t\treturn `google-vertex:${project}:${location}:${credentialsPath}`;\n\t\t}\n\t\treturn provider;\n\t}\n\n\tprivate getFallbackAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst apiKey = this.fallbackResolver?.(provider);\n\t\tif (!apiKey || !isProviderApiKeyAllowed(provider, apiKey)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.createAuthSourceCandidate({\n\t\t\tconfigured: false,\n\t\t\tsource: \"fallback\",\n\t\t\tlabel: \"custom provider config\",\n\t\t\tidentityMaterial: provider,\n\t\t\tvalueMaterial: apiKey,\n\t\t});\n\t}\n\n\tprivate getAuthSourceCandidates(provider: string, options?: { includeFallback?: boolean }): AuthSourceCandidate[] {\n\t\tconst fallbackCandidate =\n\t\t\toptions?.includeFallback === false ? undefined : this.getFallbackAuthCandidate(provider);\n\t\tconst candidates = [\n\t\t\tthis.getRuntimeAuthCandidate(provider),\n\t\t\tthis.getStoredAuthCandidate(provider),\n\t\t\tthis.getEnvironmentAuthCandidate(provider),\n\t\t\tfallbackCandidate,\n\t\t];\n\t\treturn candidates.filter((candidate): candidate is AuthSourceCandidate => candidate !== undefined);\n\t}\n\n\tprivate isAuthSourceStale(provider: string, candidate: AuthSourceCandidate): boolean {\n\t\tconst matchingStale = this.getMatchingStaleAuthSources(provider, candidate);\n\t\tif (matchingStale.length === 0) {\n\t\t\treturn false;\n\t\t}\n\t\tconst valueFingerprint = candidate.valueFingerprint ?? candidate.resolveValueFingerprint?.();\n\t\treturn Boolean(valueFingerprint && matchingStale.some((token) => token.valueFingerprint === valueFingerprint));\n\t}\n\n\tprivate getMatchingStaleAuthSources(provider: string, candidate: AuthSourceCandidate): AuthSourceToken[] {\n\t\tconst stale = this.staleAuthSources.get(provider);\n\t\tif (!stale) {\n\t\t\treturn [];\n\t\t}\n\t\treturn stale.filter(\n\t\t\t(token) => token.source === candidate.source && token.identityFingerprint === candidate.identityFingerprint,\n\t\t);\n\t}\n\n\tprivate getAvailableAuthCandidate(\n\t\tprovider: string,\n\t\toptions?: { includeFallback?: boolean },\n\t): { candidate?: AuthSourceCandidate; hasStaleCandidate: boolean } {\n\t\tlet hasStaleCandidate = false;\n\t\tfor (const candidate of this.getAuthSourceCandidates(provider, options)) {\n\t\t\tif (this.isAuthSourceStale(provider, candidate)) {\n\t\t\t\thasStaleCandidate = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn { candidate, hasStaleCandidate };\n\t\t}\n\t\treturn { hasStaleCandidate };\n\t}\n\n\tprivate toAuthStatus(candidate: AuthSourceCandidate): AuthStatus {\n\t\treturn {\n\t\t\tconfigured: candidate.configured,\n\t\t\tsource: candidate.source,\n\t\t\t...(candidate.label ? { label: candidate.label } : {}),\n\t\t};\n\t}\n\n\tprivate getAuthStatusFromCandidates(provider: string): AuthStatus {\n\t\tconst { candidate, hasStaleCandidate } = this.getAvailableAuthCandidate(provider);\n\t\tif (candidate) {\n\t\t\treturn this.toAuthStatus(candidate);\n\t\t}\n\t\tif (hasStaleCandidate) {\n\t\t\treturn { configured: false, source: \"stale\", label: \"expired\" };\n\t\t}\n\t\treturn { configured: false };\n\t}\n\n\tmarkAuthStale(provider: string): boolean {\n\t\tconst token = this.getCurrentAuthSourceToken(provider);\n\t\treturn token ? this.markAuthSourceStale(token) : false;\n\t}\n\n\tprivate getAuthSourceTokenForCandidate(\n\t\tprovider: string,\n\t\tcandidate: AuthSourceCandidate,\n\t): AuthSourceToken | undefined {\n\t\tconst valueFingerprint = candidate.valueFingerprint ?? candidate.resolveValueFingerprint?.();\n\t\tif (!valueFingerprint) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tprovider,\n\t\t\tsource: candidate.source,\n\t\t\tidentityFingerprint: candidate.identityFingerprint,\n\t\t\tvalueFingerprint,\n\t\t};\n\t}\n\n\tgetCurrentAuthSourceToken(provider: string): AuthSourceToken | undefined {\n\t\tconst { candidate } = this.getAvailableAuthCandidate(provider);\n\t\tif (!candidate) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.getAuthSourceTokenForCandidate(provider, candidate);\n\t}\n\n\tmarkAuthSourceStale(token: AuthSourceToken): boolean {\n\t\tif (token.provider.length === 0) {\n\t\t\treturn false;\n\t\t}\n\t\tconst stale = this.staleAuthSources.get(token.provider) ?? [];\n\t\tif (\n\t\t\t!stale.some(\n\t\t\t\t(existing) =>\n\t\t\t\t\texisting.source === token.source &&\n\t\t\t\t\texisting.identityFingerprint === token.identityFingerprint &&\n\t\t\t\t\texisting.valueFingerprint === token.valueFingerprint,\n\t\t\t)\n\t\t) {\n\t\t\tstale.push(token);\n\t\t}\n\t\tthis.staleAuthSources.set(token.provider, stale);\n\t\treturn true;\n\t}\n\n\tprivate clearStaleAuthSource(provider: string, source: ActiveAuthStatusSource): void {\n\t\tconst stale = this.staleAuthSources.get(provider);\n\t\tif (!stale) {\n\t\t\treturn;\n\t\t}\n\t\tconst next = stale.filter((token) => token.source !== source);\n\t\tif (next.length === 0) {\n\t\t\tthis.staleAuthSources.delete(provider);\n\t\t} else {\n\t\t\tthis.staleAuthSources.set(provider, next);\n\t\t}\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * Remove a provider's credential with the disk write verified: throws on any\n\t * load or write failure instead of recording it, so callers can refuse to\n\t * proceed while the credential may still exist on disk. Disk-authoritative\n\t * and idempotent — in-memory state is only updated after the write succeeds.\n\t */\n\tremoveVerified(provider: string): void {\n\t\tthis.assertWritableStorage();\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tif (!(provider in currentData)) return { result: undefined };\n\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\tdelete merged[provider];\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t\tdelete this.data[provider];\n\t\t// Post-success only: a failed removal must not make a stale-marked credential selectable again.\n\t\tthis.clearStaleAuthSource(provider, \"stored\");\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\treturn this.getAvailableAuthCandidate(provider).candidate !== undefined;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\treturn this.getAuthStatusFromCandidates(provider);\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tthis.assertWritableStorage();\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthrow new Error(contract.guidance);\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tif (getProviderAuthContract(providerId).oauth !== \"supported\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\" || getProviderAuthContract(providerId).oauth !== \"supported\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. auth.json\n\t * 3. Environment variable\n\t * 4. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKeyWithSourceToken(\n\t\tproviderId: string,\n\t\toptions?: { includeFallback?: boolean },\n\t): Promise<AuthApiKeyResult> {\n\t\tif (this.isExistingOpenAICodexSubscription(providerId)) {\n\t\t\tif (this.runtimeOverrides.has(providerId))\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription does not accept API-key overrides\");\n\t\t\tconst credential = this.data[providerId];\n\t\t\tif (\n\t\t\t\t!credential ||\n\t\t\t\tcredential.type !== \"oauth\" ||\n\t\t\t\ttypeof credential.access !== \"string\" ||\n\t\t\t\t!credential.access ||\n\t\t\t\t!Number.isFinite(credential.expires)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription credential is unavailable\");\n\t\t\tif (Date.now() >= credential.expires) throw new Error(\"Existing OpenAI subscription credential has expired\");\n\t\t\tconst candidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (!candidate || this.isAuthSourceStale(providerId, candidate))\n\t\t\t\tthrow new Error(\"Existing OpenAI subscription credential is unavailable\");\n\t\t\treturn {\n\t\t\t\tapiKey: credential.access,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, candidate),\n\t\t\t};\n\t\t}\n\n\t\t// Runtime overrides take precedence over stored credentials and environment keys.\n\t\tconst runtimeCandidate = this.getRuntimeAuthCandidate(providerId);\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey && runtimeCandidate && !this.isAuthSourceStale(providerId, runtimeCandidate)) {\n\t\t\treturn {\n\t\t\t\tapiKey: runtimeKey,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, runtimeCandidate),\n\t\t\t};\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\tconst storedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (storedCandidate && !this.isAuthSourceStale(providerId, storedCandidate)) {\n\t\t\t\tconst hasStaleRecord = this.getMatchingStaleAuthSources(providerId, storedCandidate).length > 0;\n\t\t\t\tconst apiKey =\n\t\t\t\t\tcred.key.startsWith(\"!\") && hasStaleRecord\n\t\t\t\t\t\t? resolveConfigValueUncached(cred.key)\n\t\t\t\t\t\t: resolveConfigValue(cred.key);\n\t\t\t\tif (apiKey === undefined || isProviderApiKeyAllowed(providerId, apiKey)) {\n\t\t\t\t\tconst sourceToken =\n\t\t\t\t\t\tapiKey === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: this.getAuthSourceTokenForCandidate(\n\t\t\t\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\t\t\t\tcred.key.startsWith(\"!\")\n\t\t\t\t\t\t\t\t\t\t? (this.getStoredAuthCandidate(providerId, { resolvedCommandValue: apiKey }) ??\n\t\t\t\t\t\t\t\t\t\t\t\tstoredCandidate)\n\t\t\t\t\t\t\t\t\t\t: storedCandidate,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\treturn { apiKey, sourceToken };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (cred?.type === \"oauth\" && getProviderAuthContract(providerId).oauth === \"supported\") {\n\t\t\tconst storedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\tif (storedCandidate && !this.isAuthSourceStale(providerId, storedCandidate)) {\n\t\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\t\tif (!provider) {\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\t\t\t\t// Lock refreshes so concurrent instances cannot race on the credential file.\n\t\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\t\tif (needsRefresh) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\tconst refreshedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tapiKey: result.apiKey,\n\t\t\t\t\t\t\t\tsourceToken: refreshedCandidate\n\t\t\t\t\t\t\t\t\t? this.getAuthSourceTokenForCandidate(providerId, refreshedCandidate)\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t\t// A peer may have refreshed successfully; reload before treating this refresh as failed.\n\t\t\t\t\t\tthis.reload();\n\t\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tupdatedCred?.type === \"oauth\" &&\n\t\t\t\t\t\t\tgetProviderAuthContract(providerId).oauth === \"supported\" &&\n\t\t\t\t\t\t\tDate.now() < updatedCred.expires\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst updatedCandidate = this.getStoredAuthCandidate(providerId);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tapiKey: provider.getApiKey(updatedCred),\n\t\t\t\t\t\t\t\tsourceToken: updatedCandidate\n\t\t\t\t\t\t\t\t\t? this.getAuthSourceTokenForCandidate(providerId, updatedCandidate)\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Preserve credentials for a later /login retry while discovery skips this provider.\n\t\t\t\t\t\treturn {};\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tapiKey: provider.getApiKey(cred),\n\t\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, storedCandidate),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst envCandidate = this.getEnvironmentAuthCandidate(providerId);\n\t\tconst envKey = this.getEnvironmentApiKey(providerId);\n\t\tif (envKey && envCandidate && !this.isAuthSourceStale(providerId, envCandidate)) {\n\t\t\treturn {\n\t\t\t\tapiKey: envKey,\n\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, envCandidate),\n\t\t\t};\n\t\t}\n\t\tif (options?.includeFallback !== false) {\n\t\t\tconst fallbackCandidate = this.getFallbackAuthCandidate(providerId);\n\t\t\tif (fallbackCandidate && !this.isAuthSourceStale(providerId, fallbackCandidate)) {\n\t\t\t\tconst apiKey = this.fallbackResolver?.(providerId) ?? undefined;\n\t\t\t\tif (apiKey === undefined || isProviderApiKeyAllowed(providerId, apiKey)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tapiKey,\n\t\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, fallbackCandidate),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {};\n\t}\n\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\tconst result = await this.getApiKeyWithSourceToken(providerId, options);\n\t\treturn result.apiKey;\n\t}\n\n\t/**\n\t * Get supported registered OAuth providers. Read-only SDK storage cannot log in.\n\t */\n\tgetOAuthProviders() {\n\t\tif (this.existingOpenAICodexSubscription) return [];\n\t\treturn getOAuthProviders().filter((provider) => getProviderAuthContract(provider.id).oauth === \"supported\");\n\t}\n}\n"]}
|