@asm-agent/coding-agent 0.9.3 → 0.9.5
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 +4 -0
- package/bundled-extensions/00-COMECE-AQUI.txt +233 -0
- package/bundled-extensions/00-INDEX.md +267 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-IMPLEMENTATION.md +323 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-QUICK-START.txt +113 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-TESTS.md +217 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING.md +155 -0
- package/bundled-extensions/README.txt +178 -0
- package/bundled-extensions/ollama-tool-forcing.ts +90 -0
- package/dist/asm-agent-runtime/run_test.py +19 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/test/test_bash.py +5 -3
- package/dist/bundle/{bundled-modules-ZKJOKVMT.js → bundled-modules-QDPN6BGG.js} +2 -2
- package/dist/bundle/{chunk-YWSYGTH7.js → chunk-JY5SSN7V.js} +2 -2
- package/dist/bundle/{chunk-IP6ZYRWR.js → chunk-VWSFBIFE.js} +13 -58
- package/dist/bundle/{cli-main-YZ2VGIP6.js → cli-main-OBM57S4L.js} +2 -2
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{main-JTKNY7OB.js → main-NV3C3DIQ.js} +2 -2
- package/dist/core/auth-storage.d.ts +0 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +0 -49
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
- package/dist/modes/interactive/auth-flows.js +1 -1
- package/dist/modes/interactive/auth-flows.js.map +1 -1
- package/dist/postinstall.d.ts.map +1 -1
- package/dist/postinstall.js +40 -3
- package/dist/postinstall.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +7 -6
|
@@ -329,7 +329,7 @@ import {
|
|
|
329
329
|
waitForPromptAdmission,
|
|
330
330
|
watchWithErrorHandler,
|
|
331
331
|
wrapTextWithAnsi
|
|
332
|
-
} from "./chunk-
|
|
332
|
+
} from "./chunk-JY5SSN7V.js";
|
|
333
333
|
import {
|
|
334
334
|
array,
|
|
335
335
|
boolean,
|
|
@@ -10216,7 +10216,6 @@ __export(oauth_exports, {
|
|
|
10216
10216
|
// dist/core/auth-storage.js
|
|
10217
10217
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
10218
10218
|
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10219
|
-
import { homedir as homedir2 } from "os";
|
|
10220
10219
|
import { dirname as dirname3, join as join3 } from "path";
|
|
10221
10220
|
|
|
10222
10221
|
// dist/core/prime-inference-auth.js
|
|
@@ -10895,31 +10894,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
10895
10894
|
})
|
|
10896
10895
|
};
|
|
10897
10896
|
}
|
|
10898
|
-
getAntigravityAuthCandidate(provider) {
|
|
10899
|
-
if (provider !== "google" && provider !== "gemini") {
|
|
10900
|
-
return void 0;
|
|
10901
|
-
}
|
|
10902
|
-
const credsPath = join3(homedir2(), ".gemini", "oauth_creds.json");
|
|
10903
|
-
if (!existsSync2(credsPath)) {
|
|
10904
|
-
return void 0;
|
|
10905
|
-
}
|
|
10906
|
-
try {
|
|
10907
|
-
const data = JSON.parse(readFileSync2(credsPath, "utf-8"));
|
|
10908
|
-
if (data && data.access_token) {
|
|
10909
|
-
return {
|
|
10910
|
-
label: "Antigravity CLI",
|
|
10911
|
-
...this.createAuthSourceCandidate({
|
|
10912
|
-
configured: false,
|
|
10913
|
-
source: "antigravity",
|
|
10914
|
-
identityMaterial: provider,
|
|
10915
|
-
valueMaterial: data.access_token
|
|
10916
|
-
})
|
|
10917
|
-
};
|
|
10918
|
-
}
|
|
10919
|
-
} catch {
|
|
10920
|
-
}
|
|
10921
|
-
return void 0;
|
|
10922
|
-
}
|
|
10923
10897
|
getStoredAuthCandidate(provider, options) {
|
|
10924
10898
|
const credential = this.data[provider] ?? (provider === "google" ? this.data.gemini : provider === "gemini" ? this.data.google : void 0);
|
|
10925
10899
|
if (!credential) {
|
|
@@ -11004,7 +10978,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
11004
10978
|
fallbackCandidate
|
|
11005
10979
|
] : [
|
|
11006
10980
|
this.getRuntimeAuthCandidate(provider),
|
|
11007
|
-
this.getAntigravityAuthCandidate(provider),
|
|
11008
10981
|
this.getStoredAuthCandidate(provider),
|
|
11009
10982
|
this.getEnvironmentAuthCandidate(provider),
|
|
11010
10983
|
fallbackCandidate
|
|
@@ -11327,24 +11300,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
11327
11300
|
};
|
|
11328
11301
|
}
|
|
11329
11302
|
}
|
|
11330
|
-
if (providerId === "google" || providerId === "gemini") {
|
|
11331
|
-
const agyCandidate = this.getAntigravityAuthCandidate(providerId);
|
|
11332
|
-
if (agyCandidate && !this.isAuthSourceStale(providerId, agyCandidate)) {
|
|
11333
|
-
const credsPath = join3(homedir2(), ".gemini", "oauth_creds.json");
|
|
11334
|
-
if (existsSync2(credsPath)) {
|
|
11335
|
-
try {
|
|
11336
|
-
const data = JSON.parse(readFileSync2(credsPath, "utf-8"));
|
|
11337
|
-
if (data && data.access_token) {
|
|
11338
|
-
return {
|
|
11339
|
-
apiKey: data.access_token,
|
|
11340
|
-
sourceToken: this.getAuthSourceTokenForCandidate(providerId, agyCandidate)
|
|
11341
|
-
};
|
|
11342
|
-
}
|
|
11343
|
-
} catch {
|
|
11344
|
-
}
|
|
11345
|
-
}
|
|
11346
|
-
}
|
|
11347
|
-
}
|
|
11348
11303
|
const cred = this.get(providerId);
|
|
11349
11304
|
if (cred?.type === "api_key") {
|
|
11350
11305
|
const storedCandidate = this.getStoredAuthCandidate(providerId);
|
|
@@ -11776,7 +11731,7 @@ function requireCount(args, count, usage) {
|
|
|
11776
11731
|
// dist/core/settings-manager.js
|
|
11777
11732
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
11778
11733
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
11779
|
-
import { homedir as
|
|
11734
|
+
import { homedir as homedir2 } from "os";
|
|
11780
11735
|
import { dirname as dirname4, join as join4 } from "path";
|
|
11781
11736
|
var RECENT_MODELS_LIMIT = 20;
|
|
11782
11737
|
var DEFAULT_IDLE_EVICTION_MINUTES = 90;
|
|
@@ -12141,10 +12096,10 @@ var SettingsManager = class _SettingsManager {
|
|
|
12141
12096
|
return sessionDir;
|
|
12142
12097
|
}
|
|
12143
12098
|
if (sessionDir === "~") {
|
|
12144
|
-
return
|
|
12099
|
+
return homedir2();
|
|
12145
12100
|
}
|
|
12146
12101
|
if (sessionDir.startsWith("~/")) {
|
|
12147
|
-
return join4(
|
|
12102
|
+
return join4(homedir2(), sessionDir.slice(2));
|
|
12148
12103
|
}
|
|
12149
12104
|
return sessionDir;
|
|
12150
12105
|
}
|
|
@@ -13542,7 +13497,7 @@ async function launchDaemonUpdateRestartCoordinator(options) {
|
|
|
13542
13497
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
13543
13498
|
import { createHash as createHash3 } from "node:crypto";
|
|
13544
13499
|
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync5, rmSync as rmSync3, statSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13545
|
-
import { homedir as
|
|
13500
|
+
import { homedir as homedir3, tmpdir } from "node:os";
|
|
13546
13501
|
import { basename as basename3, dirname as dirname6, join as join6, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
13547
13502
|
|
|
13548
13503
|
// ../../node_modules/glob/dist/esm/index.min.js
|
|
@@ -18495,7 +18450,7 @@ function toPosixPath(p) {
|
|
|
18495
18450
|
return p.split(sep2).join("/");
|
|
18496
18451
|
}
|
|
18497
18452
|
function getHomeDir() {
|
|
18498
|
-
return process.env.HOME ||
|
|
18453
|
+
return process.env.HOME || homedir3();
|
|
18499
18454
|
}
|
|
18500
18455
|
function prefixIgnorePattern(line, prefix) {
|
|
18501
18456
|
const trimmed = line.trim();
|
|
@@ -20320,7 +20275,7 @@ var DefaultPackageManager = class {
|
|
|
20320
20275
|
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
20321
20276
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
20322
20277
|
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
20323
|
-
import { homedir as
|
|
20278
|
+
import { homedir as homedir4 } from "node:os";
|
|
20324
20279
|
import { basename as basename4, dirname as dirname7, join as join7, resolve as resolve4 } from "node:path";
|
|
20325
20280
|
var DAEMON_SUPERVISOR_REGISTRY_DIR_ENV = "ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_REGISTRY_DIR";
|
|
20326
20281
|
var OWNER_VERSION = 1;
|
|
@@ -20509,7 +20464,7 @@ var DaemonShutdownAdmission = class {
|
|
|
20509
20464
|
}
|
|
20510
20465
|
};
|
|
20511
20466
|
function defaultDaemonSupervisorRegistryDir(environment = process.env) {
|
|
20512
|
-
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ?? join7(
|
|
20467
|
+
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ?? join7(homedir4(), ".asm-agent", "supervisor-owners");
|
|
20513
20468
|
}
|
|
20514
20469
|
function legacyDaemonSupervisorRegistryDir(environment = process.env) {
|
|
20515
20470
|
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ? void 0 : resolve4(defaultDaemonSocketDir(), "supervisor-owners");
|
|
@@ -26930,7 +26885,7 @@ File: ${modelsJsonPath}`);
|
|
|
26930
26885
|
|
|
26931
26886
|
// dist/core/resource-loader.js
|
|
26932
26887
|
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
|
|
26933
|
-
import { homedir as
|
|
26888
|
+
import { homedir as homedir5 } from "node:os";
|
|
26934
26889
|
import { join as join10, resolve as resolve8, sep as sep4 } from "node:path";
|
|
26935
26890
|
function resolvePromptInput(input, description) {
|
|
26936
26891
|
if (!input) {
|
|
@@ -27406,11 +27361,11 @@ var DefaultResourceLoader = class {
|
|
|
27406
27361
|
const trimmed = p.trim();
|
|
27407
27362
|
let expanded = trimmed;
|
|
27408
27363
|
if (trimmed === "~") {
|
|
27409
|
-
expanded =
|
|
27364
|
+
expanded = homedir5();
|
|
27410
27365
|
} else if (trimmed.startsWith("~/")) {
|
|
27411
|
-
expanded = join10(
|
|
27366
|
+
expanded = join10(homedir5(), trimmed.slice(2));
|
|
27412
27367
|
} else if (trimmed.startsWith("~")) {
|
|
27413
|
-
expanded = join10(
|
|
27368
|
+
expanded = join10(homedir5(), trimmed.slice(1));
|
|
27414
27369
|
}
|
|
27415
27370
|
return resolve8(this.cwd, expanded);
|
|
27416
27371
|
}
|
|
@@ -40328,7 +40283,7 @@ var PrimeTeamSelectorComponent = class extends Container {
|
|
|
40328
40283
|
|
|
40329
40284
|
// dist/modes/interactive/auth-flows.js
|
|
40330
40285
|
var BEDROCK_PROVIDER_ID = "amazon-bedrock";
|
|
40331
|
-
var ASM_AGENT_LOGIN_PROVIDER_IDS = /* @__PURE__ */ new Set(["openai-codex", "anthropic"
|
|
40286
|
+
var ASM_AGENT_LOGIN_PROVIDER_IDS = /* @__PURE__ */ new Set(["openai-codex", "anthropic"]);
|
|
40332
40287
|
var ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";
|
|
40333
40288
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
40334
40289
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isOwnedSessionWorkerProcess,
|
|
8
8
|
maybeRunOwnedSessionWorkerFrontend,
|
|
9
9
|
maybeStartDaemonEarly
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JY5SSN7V.js";
|
|
11
11
|
import "./chunk-5QCJ5DQU.js";
|
|
12
12
|
import "./chunk-CMMSLBAW.js";
|
|
13
13
|
import "./chunk-2T7M7VJ4.js";
|
|
@@ -182,7 +182,7 @@ async function runCli() {
|
|
|
182
182
|
}
|
|
183
183
|
const [{ EnvHttpProxyAgent, setGlobalDispatcher }, { main }] = await Promise.all([
|
|
184
184
|
import("undici"),
|
|
185
|
-
import("./main-
|
|
185
|
+
import("./main-NV3C3DIQ.js")
|
|
186
186
|
]);
|
|
187
187
|
setGlobalDispatcher(new EnvHttpProxyAgent({ bodyTimeout: 0, headersTimeout: 0 }));
|
|
188
188
|
try {
|
package/dist/bundle/cli.js
CHANGED
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
shouldUseDaemonClientRuntime,
|
|
18
18
|
shouldUseDaemonInteractive,
|
|
19
19
|
shouldUseEphemeralSessionManagerForDaemonInteractive
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-VWSFBIFE.js";
|
|
21
|
+
import "./chunk-JY5SSN7V.js";
|
|
22
22
|
import "./chunk-VWR3YFK5.js";
|
|
23
23
|
import "./chunk-N2CSAETN.js";
|
|
24
24
|
import "./chunk-5QCJ5DQU.js";
|
|
@@ -102,7 +102,6 @@ export declare class AuthStorage {
|
|
|
102
102
|
private getStoredCredentialValueMaterial;
|
|
103
103
|
private getRuntimeAuthCandidate;
|
|
104
104
|
private getPrimeCliAuthCandidate;
|
|
105
|
-
private getAntigravityAuthCandidate;
|
|
106
105
|
private getStoredAuthCandidate;
|
|
107
106
|
private getEnvironmentAuthCandidate;
|
|
108
107
|
private getAmbientEnvironmentIdentityMaterial;
|
|
@@ -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\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@asm-agent/ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@asm-agent/ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.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 { 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| \"antigravity\"\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\tprimeCliConfigPath?: string;\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\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\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\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\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\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) {\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\tstatic fromStorage(storage: AuthStorageBackend, options?: AuthStorageOptions): AuthStorage {\n\t\treturn new AuthStorage(storage, options);\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\tif (credential.key.startsWith(\"!\")) {\n\t\t\t\tconst resolvedKey = resolveConfigValueUncached(credential.key);\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${resolveConfigValue(credential.key) ?? \"\"}`;\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) {\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: \"Prime CLI\",\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 getAntigravityAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tif (provider !== \"google\" && provider !== \"gemini\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst credsPath = join(homedir(), \".gemini\", \"oauth_creds.json\");\n\t\tif (!existsSync(credsPath)) {\n\t\t\treturn undefined;\n\t\t}\n\t\ttry {\n\t\t\tconst data = JSON.parse(readFileSync(credsPath, \"utf-8\"));\n\t\t\tif (data && data.access_token) {\n\t\t\t\treturn {\n\t\t\t\t\tlabel: \"Antigravity CLI\",\n\t\t\t\t\t...this.createAuthSourceCandidate({\n\t\t\t\t\t\tconfigured: false,\n\t\t\t\t\t\tsource: \"antigravity\",\n\t\t\t\t\t\tidentityMaterial: provider,\n\t\t\t\t\t\tvalueMaterial: data.access_token,\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore parse errors\n\t\t}\n\t\treturn undefined;\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 =\n\t\t\tthis.data[provider] ??\n\t\t\t(provider === \"google\" ? this.data.gemini : provider === \"gemini\" ? this.data.google : undefined);\n\t\tif (!credential) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst isCommandApiKey = credential.type === \"api_key\" && credential.key.startsWith(\"!\");\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 getEnvironmentAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tconst envKey = envKeys?.[0];\n\t\tconst apiKey = getEnvApiKey(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) {\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.getAntigravityAuthCandidate(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\tconst direct = this.data[provider];\n\t\tif (direct) return direct;\n\t\tif (provider === \"google\") return this.data.gemini;\n\t\tif (provider === \"gemini\") return this.data.google;\n\t\treturn 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.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.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.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 (\n\t\t\tprovider in this.data ||\n\t\t\t(provider === \"google\" && \"gemini\" in this.data) ||\n\t\t\t(provider === \"gemini\" && \"google\" in this.data)\n\t\t);\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\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\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 storageKey = currentData[providerId]\n\t\t\t\t? providerId\n\t\t\t\t: providerId === \"google\" && currentData.gemini\n\t\t\t\t\t? \"gemini\"\n\t\t\t\t\t: providerId === \"gemini\" && currentData.google\n\t\t\t\t\t\t? \"google\"\n\t\t\t\t\t\t: providerId;\n\t\t\tconst cred = currentData[storageKey];\n\t\t\tif (cred?.type !== \"oauth\") {\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, Prime CLI 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\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 = getEnvApiKey(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\tif (providerId === \"google\" || providerId === \"gemini\") {\n\t\t\tconst agyCandidate = this.getAntigravityAuthCandidate(providerId);\n\t\t\tif (agyCandidate && !this.isAuthSourceStale(providerId, agyCandidate)) {\n\t\t\t\tconst credsPath = join(homedir(), \".gemini\", \"oauth_creds.json\");\n\t\t\t\tif (existsSync(credsPath)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst data = JSON.parse(readFileSync(credsPath, \"utf-8\"));\n\t\t\t\t\t\tif (data && data.access_token) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tapiKey: data.access_token,\n\t\t\t\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, agyCandidate),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst cred = this.get(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\tconst sourceToken =\n\t\t\t\t\tapiKey === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: this.getAuthSourceTokenForCandidate(\n\t\t\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\t\t\tcred.key.startsWith(\"!\")\n\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\tstoredCandidate)\n\t\t\t\t\t\t\t\t\t: storedCandidate,\n\t\t\t\t\t\t\t);\n\t\t\t\treturn { apiKey, sourceToken };\n\t\t\t}\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\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 (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\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\treturn {\n\t\t\t\t\tapiKey: this.fallbackResolver?.(providerId) ?? undefined,\n\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, fallbackCandidate),\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 all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\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\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 CLI 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 CLI 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 primeCliConfig = this.getPrimeCliConfig(providerId);\n\t\tif (primeCliConfig?.teamIdFromEnv) {\n\t\t\treturn primeCliConfig.teamId ? { \"X-Prime-Team-ID\": primeCliConfig.teamId } : undefined;\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(\"Prime CLI 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":"AAAA;;;;;;GAMG;AAGH,OAAO,EAGN,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,MAAM,eAAe,CAAC;AAMvB,OAAO,EAMN,KAAK,SAAS,EAGd,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,OAAO,CAAC;CACd,GAAG,gBAAgB,CAAC;AAErB,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAE7D,MAAM,MAAM,UAAU,GAAG;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EACJ,QAAQ,GACR,SAAS,GACT,aAAa,GACb,WAAW,GACX,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,qBAAqB,GACrB,OAAO,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,KAAK,UAAU,CAAC,CAAC,IAAI;IACpB,MAAM,EAAE,CAAC,CAAC;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAElF,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CACzB,CAAC;AAWF,KAAK,gBAAgB,GAAG;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,eAAe,CAAC;CAC9B,CAAC;AAEF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACnE,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC1F;AAED,qBAAa,sBAAuB,YAAW,kBAAkB;IACpD,OAAO,CAAC,QAAQ;IAA5B,YAAoB,QAAQ,GAAE,MAAyC,EAAI;IAE3E,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,wBAAwB;IA2BhC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAmBjE;IAEK,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAgD9F;CACD;AAED,qBAAa,0BAA2B,YAAW,kBAAkB;IACpE,OAAO,CAAC,KAAK,CAAqB;IAElC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAMjE;IAEK,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAM9F;CACD;AAED;;GAEG;AACH,qBAAa,WAAW;IAStB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IAThB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,gBAAgB,CAAC,CAA2C;IACpE,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,MAAM,CAAe;IAE7B,OAAO,eAKN;IAED,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAG1E;IAED,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAEzF;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAE,eAAoB,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAIrF;IAED;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGvD;IAED;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG1C;IAED;;;OAGG;IACH,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAE5E;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,yBAAyB;IAqCjC,OAAO,CAAC,gCAAgC;IAaxC,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,sBAAsB;IA+B9B,OAAO,CAAC,2BAA2B;IAkBnC,OAAO,CAAC,qCAAqC;IA4B7C,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,yBAAyB;IAejC,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;IAWnC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGvC;IAED,OAAO,CAAC,8BAA8B;IAgBtC,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAMvE;IAED,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAiBnD;IAED,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,gBAAgB;IAOxB;;OAEG;IACH,MAAM,IAAI,IAAI,CAab;IAED,OAAO,CAAC,qBAAqB;IAqB7B;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAMhD;IAED;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,GAAG,IAAI,CAItD;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAI7B;IAED;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAWrC;IAED;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE,CAEf;IAED;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAM7B;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEjC;IAED;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAE1C;IAED;;OAEG;IACH,MAAM,IAAI,eAAe,CAExB;IAED,WAAW,IAAI,KAAK,EAAE,CAIrB;IAED;;OAEG;IACG,KAAK,CAAC,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAW7B;YAMa,yBAAyB;IAqDvC;;;;;;;OAOG;IACG,wBAAwB,CAC7B,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACrC,OAAO,CAAC,gBAAgB,CAAC,CAoI3B;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGxG;IAED;;OAEG;IACH,iBAAiB,qDAEhB;IAED,8BAA8B,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAmB3D;IAED,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA8B5C;IAED,8BAA8B,IAAI,mBAAmB,GAAG,IAAI,GAAG,SAAS,CAyCvE;IAED,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAYzE;IAED,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAK1C;IAED,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,uBAAuB;CAG/B","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 \"@asm-agent/ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@asm-agent/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 {\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 { 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| \"antigravity\"\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\tprimeCliConfigPath?: string;\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\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\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\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\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\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) {\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\tstatic fromStorage(storage: AuthStorageBackend, options?: AuthStorageOptions): AuthStorage {\n\t\treturn new AuthStorage(storage, options);\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\tif (credential.key.startsWith(\"!\")) {\n\t\t\t\tconst resolvedKey = resolveConfigValueUncached(credential.key);\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${resolveConfigValue(credential.key) ?? \"\"}`;\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) {\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: \"Prime CLI\",\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 =\n\t\t\tthis.data[provider] ??\n\t\t\t(provider === \"google\" ? this.data.gemini : provider === \"gemini\" ? this.data.google : undefined);\n\t\tif (!credential) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst isCommandApiKey = credential.type === \"api_key\" && credential.key.startsWith(\"!\");\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 getEnvironmentAuthCandidate(provider: string): AuthSourceCandidate | undefined {\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tconst envKey = envKeys?.[0];\n\t\tconst apiKey = getEnvApiKey(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) {\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\tconst direct = this.data[provider];\n\t\tif (direct) return direct;\n\t\tif (provider === \"google\") return this.data.gemini;\n\t\tif (provider === \"gemini\") return this.data.google;\n\t\treturn 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.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.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.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 (\n\t\t\tprovider in this.data ||\n\t\t\t(provider === \"google\" && \"gemini\" in this.data) ||\n\t\t\t(provider === \"gemini\" && \"google\" in this.data)\n\t\t);\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\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\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 storageKey = currentData[providerId]\n\t\t\t\t? providerId\n\t\t\t\t: providerId === \"google\" && currentData.gemini\n\t\t\t\t\t? \"gemini\"\n\t\t\t\t\t: providerId === \"gemini\" && currentData.google\n\t\t\t\t\t\t? \"google\"\n\t\t\t\t\t\t: providerId;\n\t\t\tconst cred = currentData[storageKey];\n\t\t\tif (cred?.type !== \"oauth\") {\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, Prime CLI 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\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 = getEnvApiKey(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.get(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\tconst sourceToken =\n\t\t\t\t\tapiKey === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: this.getAuthSourceTokenForCandidate(\n\t\t\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\t\t\tcred.key.startsWith(\"!\")\n\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\tstoredCandidate)\n\t\t\t\t\t\t\t\t\t: storedCandidate,\n\t\t\t\t\t\t\t);\n\t\t\t\treturn { apiKey, sourceToken };\n\t\t\t}\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\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 (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\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\treturn {\n\t\t\t\t\tapiKey: this.fallbackResolver?.(providerId) ?? undefined,\n\t\t\t\t\tsourceToken: this.getAuthSourceTokenForCandidate(providerId, fallbackCandidate),\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 all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\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\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 CLI 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 CLI 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 primeCliConfig = this.getPrimeCliConfig(providerId);\n\t\tif (primeCliConfig?.teamIdFromEnv) {\n\t\t\treturn primeCliConfig.teamId ? { \"X-Prime-Team-ID\": primeCliConfig.teamId } : undefined;\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(\"Prime CLI 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"]}
|
|
@@ -9,7 +9,6 @@ import { createHash } from "node:crypto";
|
|
|
9
9
|
import { findEnvKeys, getEnvApiKey, } from "@asm-agent/ai";
|
|
10
10
|
import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@asm-agent/ai/oauth";
|
|
11
11
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
12
|
-
import { homedir } from "os";
|
|
13
12
|
import { dirname, join } from "path";
|
|
14
13
|
import lockfile from "proper-lockfile";
|
|
15
14
|
import { getAgentDir } from "../config.js";
|
|
@@ -266,33 +265,6 @@ export class AuthStorage {
|
|
|
266
265
|
}),
|
|
267
266
|
};
|
|
268
267
|
}
|
|
269
|
-
getAntigravityAuthCandidate(provider) {
|
|
270
|
-
if (provider !== "google" && provider !== "gemini") {
|
|
271
|
-
return undefined;
|
|
272
|
-
}
|
|
273
|
-
const credsPath = join(homedir(), ".gemini", "oauth_creds.json");
|
|
274
|
-
if (!existsSync(credsPath)) {
|
|
275
|
-
return undefined;
|
|
276
|
-
}
|
|
277
|
-
try {
|
|
278
|
-
const data = JSON.parse(readFileSync(credsPath, "utf-8"));
|
|
279
|
-
if (data && data.access_token) {
|
|
280
|
-
return {
|
|
281
|
-
label: "Antigravity CLI",
|
|
282
|
-
...this.createAuthSourceCandidate({
|
|
283
|
-
configured: false,
|
|
284
|
-
source: "antigravity",
|
|
285
|
-
identityMaterial: provider,
|
|
286
|
-
valueMaterial: data.access_token,
|
|
287
|
-
}),
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
// Ignore parse errors
|
|
293
|
-
}
|
|
294
|
-
return undefined;
|
|
295
|
-
}
|
|
296
268
|
getStoredAuthCandidate(provider, options) {
|
|
297
269
|
const credential = this.data[provider] ??
|
|
298
270
|
(provider === "google" ? this.data.gemini : provider === "gemini" ? this.data.google : undefined);
|
|
@@ -387,7 +359,6 @@ export class AuthStorage {
|
|
|
387
359
|
]
|
|
388
360
|
: [
|
|
389
361
|
this.getRuntimeAuthCandidate(provider),
|
|
390
|
-
this.getAntigravityAuthCandidate(provider),
|
|
391
362
|
this.getStoredAuthCandidate(provider),
|
|
392
363
|
this.getEnvironmentAuthCandidate(provider),
|
|
393
364
|
fallbackCandidate,
|
|
@@ -730,26 +701,6 @@ export class AuthStorage {
|
|
|
730
701
|
};
|
|
731
702
|
}
|
|
732
703
|
}
|
|
733
|
-
if (providerId === "google" || providerId === "gemini") {
|
|
734
|
-
const agyCandidate = this.getAntigravityAuthCandidate(providerId);
|
|
735
|
-
if (agyCandidate && !this.isAuthSourceStale(providerId, agyCandidate)) {
|
|
736
|
-
const credsPath = join(homedir(), ".gemini", "oauth_creds.json");
|
|
737
|
-
if (existsSync(credsPath)) {
|
|
738
|
-
try {
|
|
739
|
-
const data = JSON.parse(readFileSync(credsPath, "utf-8"));
|
|
740
|
-
if (data && data.access_token) {
|
|
741
|
-
return {
|
|
742
|
-
apiKey: data.access_token,
|
|
743
|
-
sourceToken: this.getAuthSourceTokenForCandidate(providerId, agyCandidate),
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
catch {
|
|
748
|
-
// Ignore
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
704
|
const cred = this.get(providerId);
|
|
754
705
|
if (cred?.type === "api_key") {
|
|
755
706
|
const storedCandidate = this.getStoredAuthCandidate(providerId);
|