open-multi-agent-kit 0.98.0 → 0.98.2
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 +15 -1
- package/README.md +2 -2
- package/dist/cli/mcp-attach.d.ts +15 -0
- package/dist/cli/mcp-attach.d.ts.map +1 -0
- package/dist/cli/mcp-attach.js +14 -0
- package/dist/cli/mcp-attach.js.map +1 -0
- package/dist/core/agent-session.d.ts +7 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +66 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +19 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +57 -6
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/oauth-recovery.d.ts +29 -0
- package/dist/core/compaction/oauth-recovery.d.ts.map +1 -0
- package/dist/core/compaction/oauth-recovery.js +40 -0
- package/dist/core/compaction/oauth-recovery.js.map +1 -0
- package/dist/core/model-registry.d.ts +10 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +16 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/provider-resilience.d.ts +8 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +17 -0
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-usage.d.ts +1 -1
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +3 -2
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts.map +1 -1
- package/dist/core/session-failure-cause.js +7 -1
- package/dist/core/session-failure-cause.js.map +1 -1
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +3 -1
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/verified-bash-runtime.d.ts.map +1 -1
- package/dist/core/verified-bash-runtime.js +6 -4
- package/dist/core/verified-bash-runtime.js.map +1 -1
- package/dist/guardrails/workspace-fingerprint.d.ts +2 -0
- package/dist/guardrails/workspace-fingerprint.d.ts.map +1 -1
- package/dist/guardrails/workspace-fingerprint.js +12 -10
- package/dist/guardrails/workspace-fingerprint.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/docs/book-to-skill.md +17 -0
- package/docs/docs.json +4 -0
- package/docs/index.md +2 -0
- package/docs/mcp.md +10 -2
- package/docs/security.md +4 -9
- package/examples/extensions/custom-provider-anthropic/index.ts +4 -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/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/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/npm-shrinkwrap.json +18 -18
- package/package.json +6 -6
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses file locking to prevent race conditions when multiple OMK instances
|
|
6
6
|
* try to refresh tokens simultaneously.
|
|
7
7
|
*/
|
|
8
|
-
import { type OAuthCredentials, type OAuthLoginCallbacks, type OAuthProviderId } from "omk-ai";
|
|
8
|
+
import { type OAuthCredentials, type OAuthLoginCallbacks, type OAuthProviderId, type OAuthProviderInterface } from "omk-ai";
|
|
9
9
|
export type ApiKeyCredential = {
|
|
10
10
|
type: "api_key";
|
|
11
11
|
key: string;
|
|
@@ -145,6 +145,22 @@ export declare class AuthStorage {
|
|
|
145
145
|
*/
|
|
146
146
|
logout(provider: string): void;
|
|
147
147
|
private resolveOAuthTokenWithLock;
|
|
148
|
+
/**
|
|
149
|
+
* Force-refresh the selected account after the provider rejected `rejectedApiKey`.
|
|
150
|
+
*
|
|
151
|
+
* Local expiry is not the last word: ChatGPT/Codex answers `401 token_expired`
|
|
152
|
+
* for a token whose JWT `exp` and our stored `expires` still lie days ahead
|
|
153
|
+
* (server-side revocation, rotation from another login). Refreshing only on
|
|
154
|
+
* the local clock would keep replaying the dead token until that date.
|
|
155
|
+
*
|
|
156
|
+
* Several omk processes share auth.json. If the stored token no longer equals
|
|
157
|
+
* the rejected one, another process already rotated it and the current token
|
|
158
|
+
* is returned without a second refresh, which would only burn the new refresh
|
|
159
|
+
* token. A failed refresh propagates so the caller can ask for `/login`; the
|
|
160
|
+
* credential is left in place for that login to replace.
|
|
161
|
+
*/
|
|
162
|
+
refreshRejectedOAuthToken(providerId: OAuthProviderId, rejectedApiKey: string): Promise<string | undefined>;
|
|
163
|
+
private resolveAccountToken;
|
|
148
164
|
/**
|
|
149
165
|
* Get API key for a provider.
|
|
150
166
|
* Priority:
|
|
@@ -156,11 +172,12 @@ export declare class AuthStorage {
|
|
|
156
172
|
*/
|
|
157
173
|
getApiKey(providerId: string, options?: {
|
|
158
174
|
includeFallback?: boolean;
|
|
175
|
+
minRemainingMs?: number;
|
|
159
176
|
}): Promise<string | undefined>;
|
|
160
177
|
/**
|
|
161
178
|
* Get all registered OAuth providers
|
|
162
179
|
*/
|
|
163
|
-
getOAuthProviders():
|
|
180
|
+
getOAuthProviders(): OAuthProviderInterface[];
|
|
164
181
|
}
|
|
165
182
|
export {};
|
|
166
183
|
//# sourceMappingURL=auth-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple OMK instances\n * try to refresh tokens simultaneously.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"omk-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"omk-ai/oauth\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nconst RETIRED_GROK_OAUTH_PROXY = \"grok-oauth-proxy\";\n\nfunction omitRetiredGrokOAuthProxy(data: AuthStorageData): AuthStorageData {\n\tif (!(RETIRED_GROK_OAUTH_PROXY in data)) return data;\n\tconst { [RETIRED_GROK_OAUTH_PROXY]: _retired, ...kept } = data;\n\treturn kept;\n}\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n\t/** Complete account list when more than one subscription account is configured. */\n\taccounts?: OAuthCredentials[];\n\t/** Index of the account explicitly selected for this provider. */\n\tactiveAccount?: number;\n\t/** Legacy cursor accepted when migrating older auth.json files. */\n\tnextAccount?: number;\n} & OAuthCredentials;\n\nexport interface OAuthAccountSummary {\n\tindex: number;\n\tlabel: string;\n\tselected: boolean;\n}\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nconst OAUTH_ACCOUNT_ID_FIELDS = [\"accountId\", \"email\", \"userId\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_FIELDS = [\"email\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_MAX_LENGTH = 96;\nconst UNSAFE_ACCOUNT_LABEL_PATTERN =\n\t/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f\\ufeff]/g;\n\nfunction sanitizeOAuthAccountLabel(value: string): string | undefined {\n\tconst sanitized = stripAnsi(value)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(UNSAFE_ACCOUNT_LABEL_PATTERN, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, OAUTH_ACCOUNT_LABEL_MAX_LENGTH);\n\treturn sanitized || undefined;\n}\n\nfunction stripOAuthStorageMetadata(credential: OAuthCredential): OAuthCredentials {\n\tconst {\n\t\ttype: _type,\n\t\taccounts: _accounts,\n\t\tactiveAccount: _activeAccount,\n\t\tnextAccount: _nextAccount,\n\t\t...credentials\n\t} = credential;\n\treturn credentials;\n}\n\nfunction getOAuthAccounts(credential: OAuthCredential): OAuthCredentials[] {\n\tif (Array.isArray(credential.accounts) && credential.accounts.length > 0) {\n\t\treturn credential.accounts;\n\t}\n\treturn [stripOAuthStorageMetadata(credential)];\n}\n\nfunction normalizeOAuthAccountIndex(index: unknown, accountCount: number): number {\n\tif (accountCount <= 0 || typeof index !== \"number\" || !Number.isInteger(index)) {\n\t\treturn 0;\n\t}\n\treturn ((index % accountCount) + accountCount) % accountCount;\n}\n\nfunction getSelectedOAuthAccountIndex(credential: OAuthCredential, accountCount: number): number {\n\treturn normalizeOAuthAccountIndex(credential.activeAccount ?? credential.nextAccount, accountCount);\n}\n\nfunction createOAuthCredential(accounts: OAuthCredentials[], activeAccount = 0): OAuthCredential {\n\tconst selectedIndex = normalizeOAuthAccountIndex(activeAccount, accounts.length);\n\tconst selected = accounts[selectedIndex];\n\tif (!selected) {\n\t\tthrow new Error(\"OAuth credential must contain at least one account\");\n\t}\n\tif (accounts.length === 1) {\n\t\treturn { ...selected, type: \"oauth\" };\n\t}\n\treturn {\n\t\t...selected,\n\t\ttype: \"oauth\",\n\t\taccounts,\n\t\tactiveAccount: selectedIndex,\n\t};\n}\n\nfunction getOAuthAccountIdentity(credentials: OAuthCredentials): string | undefined {\n\tconst accountId = credentials.accountId;\n\tconst orgId = credentials.orgId;\n\tif (typeof accountId === \"string\" && accountId.trim() && typeof orgId === \"string\" && orgId.trim()) {\n\t\treturn `accountId:${accountId.trim().toLowerCase()}:orgId:${orgId.trim().toLowerCase()}`;\n\t}\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\treturn `${field}:${value.trim().toLowerCase()}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction getOAuthAccountDisplayLabel(providerId: string, credentials: OAuthCredentials): string | undefined {\n\tfor (const field of OAUTH_ACCOUNT_LABEL_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\tconst label = sanitizeOAuthAccountLabel(value);\n\t\t\tif (!label) continue;\n\t\t\tconst orgName =\n\t\t\t\ttypeof credentials.orgName === \"string\" ? sanitizeOAuthAccountLabel(credentials.orgName) : undefined;\n\t\t\treturn sanitizeOAuthAccountLabel(orgName ? `${label} (${orgName})` : label);\n\t\t}\n\t}\n\ttry {\n\t\tconst label = getOAuthProvider(providerId)?.getAccountLabel?.(credentials);\n\t\treturn typeof label === \"string\" ? sanitizeOAuthAccountLabel(label) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction findMatchingOAuthAccount(accounts: OAuthCredentials[], credentials: OAuthCredentials): number {\n\tconst identity = getOAuthAccountIdentity(credentials);\n\tif (identity) {\n\t\tconst exactMatch = accounts.findIndex((account) => getOAuthAccountIdentity(account) === identity);\n\t\tif (exactMatch >= 0) return exactMatch;\n\t}\n\n\t// Let newly enriched credentials update older entries that predate email/org metadata.\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value !== \"string\" || !value.trim()) continue;\n\t\tconst normalizedValue = value.trim().toLowerCase();\n\t\tconst orgId = typeof credentials.orgId === \"string\" ? credentials.orgId.trim().toLowerCase() : \"\";\n\t\tconst compatibleMatch = accounts.findIndex((account) => {\n\t\t\tconst existingValue = account[field];\n\t\t\tif (typeof existingValue !== \"string\" || existingValue.trim().toLowerCase() !== normalizedValue) return false;\n\t\t\tconst existingOrgId = typeof account.orgId === \"string\" ? account.orgId.trim().toLowerCase() : \"\";\n\t\t\treturn !orgId || !existingOrgId || orgId === existingOrgId;\n\t\t});\n\t\tif (compatibleMatch >= 0) return compatibleMatch;\n\t}\n\n\treturn accounts.findIndex((account) => account.refresh === credentials.refresh);\n}\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\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\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\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, \"{}\", AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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 fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.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.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 parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\treturn omitRetiredGrokOAuthProxy(JSON.parse(content) as AuthStorageData);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(`Failed to parse auth storage: ${message}`);\n\t\t}\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 (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t} else if (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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return undefined;\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\tdelete this.data[provider];\n\t\t\tthis.persistProviderChange(provider, undefined);\n\t\t\treturn;\n\t\t}\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\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data).filter((provider) => provider !== RETIRED_GROK_OAUTH_PROXY);\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/** Return the number of stored OAuth accounts for a provider. */\n\tgetOAuthAccountCount(provider: string): number {\n\t\tconst credential = this.data[provider];\n\t\treturn credential?.type === \"oauth\" ? getOAuthAccounts(credential).length : 0;\n\t}\n\n\t/** Return safe, human-readable labels for every stored OAuth account. */\n\tlistOAuthAccounts(provider: string): OAuthAccountSummary[] {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") return [];\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\tconst selectedIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\treturn accounts.map((account, index) => ({\n\t\t\tindex,\n\t\t\tlabel: getOAuthAccountDisplayLabel(provider, account) ?? `Account ${index + 1}`,\n\t\t\tselected: index === selectedIndex,\n\t\t}));\n\t}\n\n\t/** Return the human-readable label of the selected OAuth account. */\n\tgetOAuthAccountLabel(provider: string): string | undefined {\n\t\treturn this.listOAuthAccounts(provider).find((account) => account.selected)?.label;\n\t}\n\n\t/** Explicitly select and persist the OAuth account used by this provider. */\n\tselectOAuthAccount(provider: string, accountIndex: number): void {\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tconst credential = currentData[provider];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\tthrow new Error(`No OAuth accounts configured for ${provider}`);\n\t\t\t}\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tif (!Number.isInteger(accountIndex) || accountIndex < 0 || accountIndex >= accounts.length) {\n\t\t\t\tthrow new Error(`Invalid OAuth account index ${accountIndex} for ${provider}`);\n\t\t\t}\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[provider]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t}\n\n\t/**\n\t * Return the explicitly selected OAuth credentials without refreshing tokens.\n\t * Used by provider model customization hooks.\n\t */\n\tgetOAuthCredentials(provider: string): OAuthCredentials | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\treturn accounts[getSelectedOAuthAccountIndex(credential, accounts.length)];\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\treturn { configured: false };\n\t\t}\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn omitRetiredGrokOAuthProxy({ ...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. Repeated logins append distinct accounts and\n\t * update an existing account when the provider exposes a stable identity.\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\tif (this.loadError) {\n\t\t\tthis.data[providerId] = { type: \"oauth\", ...credentials };\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 existing = currentData[providerId];\n\t\t\t\tlet credential: OAuthCredential;\n\n\t\t\t\tif (existing?.type === \"oauth\") {\n\t\t\t\t\tconst accounts = [...getOAuthAccounts(existing)];\n\t\t\t\t\tconst matchingIndex = findMatchingOAuthAccount(accounts, credentials);\n\t\t\t\t\tlet activeAccount: number;\n\t\t\t\t\tif (matchingIndex >= 0) {\n\t\t\t\t\t\taccounts[matchingIndex] = credentials;\n\t\t\t\t\t\tactiveAccount = matchingIndex;\n\t\t\t\t\t} else {\n\t\t\t\t\t\taccounts.push(credentials);\n\t\t\t\t\t\tactiveAccount = accounts.length - 1;\n\t\t\t\t\t}\n\t\t\t\t\tcredential = createOAuthCredential(accounts, activeAccount);\n\t\t\t\t} else {\n\t\t\t\t\tcredential = createOAuthCredential([credentials]);\n\t\t\t\t}\n\n\t\t\t\tconst merged: AuthStorageData = { ...currentData, [providerId]: credential };\n\t\t\t\tthis.data = merged;\n\t\t\t\tthis.loadError = null;\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 * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/** Resolve the explicitly selected account and refresh it under the storage lock. */\n\tprivate async resolveOAuthTokenWithLock(\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\treturn 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 credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tconst accountIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\t\tconst account = accounts[accountIndex];\n\t\t\tif (!account) return { result: null };\n\n\t\t\tconst resolved =\n\t\t\t\tDate.now() < account.expires\n\t\t\t\t\t? { apiKey: provider.getApiKey(account), newCredentials: account }\n\t\t\t\t\t: await getOAuthApiKey(providerId, { [providerId]: account });\n\t\t\tif (!resolved) return { result: null };\n\n\t\t\tif (resolved.newCredentials === account) {\n\t\t\t\treturn { result: resolved };\n\t\t\t}\n\n\t\t\tconst newCredentials = { ...account, ...resolved.newCredentials };\n\t\t\taccounts[accountIndex] = newCredentials;\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\treturn {\n\t\t\t\tresult: { ...resolved, newCredentials },\n\t\t\t\tnext: JSON.stringify(merged, null, 2),\n\t\t\t};\n\t\t});\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. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.resolveOAuthTokenWithLock(providerId);\n\t\t\t\tif (result) {\n\t\t\t\t\treturn result.apiKey;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\t// Credentials are preserved so a later request or /login can recover.\n\t\t\t\tthis.reload();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple OMK instances\n * try to refresh tokens simultaneously.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n\ttype OAuthProviderInterface,\n} from \"omk-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"omk-ai/oauth\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nconst RETIRED_GROK_OAUTH_PROXY = \"grok-oauth-proxy\";\n\nfunction omitRetiredGrokOAuthProxy(data: AuthStorageData): AuthStorageData {\n\tif (!(RETIRED_GROK_OAUTH_PROXY in data)) return data;\n\tconst { [RETIRED_GROK_OAUTH_PROXY]: _retired, ...kept } = data;\n\treturn kept;\n}\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n\t/** Complete account list when more than one subscription account is configured. */\n\taccounts?: OAuthCredentials[];\n\t/** Index of the account explicitly selected for this provider. */\n\tactiveAccount?: number;\n\t/** Legacy cursor accepted when migrating older auth.json files. */\n\tnextAccount?: number;\n} & OAuthCredentials;\n\nexport interface OAuthAccountSummary {\n\tindex: number;\n\tlabel: string;\n\tselected: boolean;\n}\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nconst OAUTH_ACCOUNT_ID_FIELDS = [\"accountId\", \"email\", \"userId\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_FIELDS = [\"email\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_MAX_LENGTH = 96;\nconst UNSAFE_ACCOUNT_LABEL_PATTERN =\n\t/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f\\ufeff]/g;\n\nfunction sanitizeOAuthAccountLabel(value: string): string | undefined {\n\tconst sanitized = stripAnsi(value)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(UNSAFE_ACCOUNT_LABEL_PATTERN, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, OAUTH_ACCOUNT_LABEL_MAX_LENGTH);\n\treturn sanitized || undefined;\n}\n\nfunction stripOAuthStorageMetadata(credential: OAuthCredential): OAuthCredentials {\n\tconst {\n\t\ttype: _type,\n\t\taccounts: _accounts,\n\t\tactiveAccount: _activeAccount,\n\t\tnextAccount: _nextAccount,\n\t\t...credentials\n\t} = credential;\n\treturn credentials;\n}\n\nfunction getOAuthAccounts(credential: OAuthCredential): OAuthCredentials[] {\n\tif (Array.isArray(credential.accounts) && credential.accounts.length > 0) {\n\t\treturn credential.accounts;\n\t}\n\treturn [stripOAuthStorageMetadata(credential)];\n}\n\nfunction normalizeOAuthAccountIndex(index: unknown, accountCount: number): number {\n\tif (accountCount <= 0 || typeof index !== \"number\" || !Number.isInteger(index)) {\n\t\treturn 0;\n\t}\n\treturn ((index % accountCount) + accountCount) % accountCount;\n}\n\nfunction getSelectedOAuthAccountIndex(credential: OAuthCredential, accountCount: number): number {\n\treturn normalizeOAuthAccountIndex(credential.activeAccount ?? credential.nextAccount, accountCount);\n}\n\nfunction createOAuthCredential(accounts: OAuthCredentials[], activeAccount = 0): OAuthCredential {\n\tconst selectedIndex = normalizeOAuthAccountIndex(activeAccount, accounts.length);\n\tconst selected = accounts[selectedIndex];\n\tif (!selected) {\n\t\tthrow new Error(\"OAuth credential must contain at least one account\");\n\t}\n\tif (accounts.length === 1) {\n\t\treturn { ...selected, type: \"oauth\" };\n\t}\n\treturn {\n\t\t...selected,\n\t\ttype: \"oauth\",\n\t\taccounts,\n\t\tactiveAccount: selectedIndex,\n\t};\n}\n\nfunction getOAuthAccountIdentity(credentials: OAuthCredentials): string | undefined {\n\tconst accountId = credentials.accountId;\n\tconst orgId = credentials.orgId;\n\tif (typeof accountId === \"string\" && accountId.trim() && typeof orgId === \"string\" && orgId.trim()) {\n\t\treturn `accountId:${accountId.trim().toLowerCase()}:orgId:${orgId.trim().toLowerCase()}`;\n\t}\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\treturn `${field}:${value.trim().toLowerCase()}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction getOAuthAccountDisplayLabel(providerId: string, credentials: OAuthCredentials): string | undefined {\n\tfor (const field of OAUTH_ACCOUNT_LABEL_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\tconst label = sanitizeOAuthAccountLabel(value);\n\t\t\tif (!label) continue;\n\t\t\tconst orgName =\n\t\t\t\ttypeof credentials.orgName === \"string\" ? sanitizeOAuthAccountLabel(credentials.orgName) : undefined;\n\t\t\treturn sanitizeOAuthAccountLabel(orgName ? `${label} (${orgName})` : label);\n\t\t}\n\t}\n\ttry {\n\t\tconst label = getOAuthProvider(providerId)?.getAccountLabel?.(credentials);\n\t\treturn typeof label === \"string\" ? sanitizeOAuthAccountLabel(label) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction findMatchingOAuthAccount(accounts: OAuthCredentials[], credentials: OAuthCredentials): number {\n\tconst identity = getOAuthAccountIdentity(credentials);\n\tif (identity) {\n\t\tconst exactMatch = accounts.findIndex((account) => getOAuthAccountIdentity(account) === identity);\n\t\tif (exactMatch >= 0) return exactMatch;\n\t}\n\n\t// Let newly enriched credentials update older entries that predate email/org metadata.\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value !== \"string\" || !value.trim()) continue;\n\t\tconst normalizedValue = value.trim().toLowerCase();\n\t\tconst orgId = typeof credentials.orgId === \"string\" ? credentials.orgId.trim().toLowerCase() : \"\";\n\t\tconst compatibleMatch = accounts.findIndex((account) => {\n\t\t\tconst existingValue = account[field];\n\t\t\tif (typeof existingValue !== \"string\" || existingValue.trim().toLowerCase() !== normalizedValue) return false;\n\t\t\tconst existingOrgId = typeof account.orgId === \"string\" ? account.orgId.trim().toLowerCase() : \"\";\n\t\t\treturn !orgId || !existingOrgId || orgId === existingOrgId;\n\t\t});\n\t\tif (compatibleMatch >= 0) return compatibleMatch;\n\t}\n\n\treturn accounts.findIndex((account) => account.refresh === credentials.refresh);\n}\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\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\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\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, \"{}\", AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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 fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.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.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 parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\treturn omitRetiredGrokOAuthProxy(JSON.parse(content) as AuthStorageData);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(`Failed to parse auth storage: ${message}`);\n\t\t}\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 (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t} else if (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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return undefined;\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\tdelete this.data[provider];\n\t\t\tthis.persistProviderChange(provider, undefined);\n\t\t\treturn;\n\t\t}\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\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data).filter((provider) => provider !== RETIRED_GROK_OAUTH_PROXY);\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/** Return the number of stored OAuth accounts for a provider. */\n\tgetOAuthAccountCount(provider: string): number {\n\t\tconst credential = this.data[provider];\n\t\treturn credential?.type === \"oauth\" ? getOAuthAccounts(credential).length : 0;\n\t}\n\n\t/** Return safe, human-readable labels for every stored OAuth account. */\n\tlistOAuthAccounts(provider: string): OAuthAccountSummary[] {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") return [];\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\tconst selectedIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\treturn accounts.map((account, index) => ({\n\t\t\tindex,\n\t\t\tlabel: getOAuthAccountDisplayLabel(provider, account) ?? `Account ${index + 1}`,\n\t\t\tselected: index === selectedIndex,\n\t\t}));\n\t}\n\n\t/** Return the human-readable label of the selected OAuth account. */\n\tgetOAuthAccountLabel(provider: string): string | undefined {\n\t\treturn this.listOAuthAccounts(provider).find((account) => account.selected)?.label;\n\t}\n\n\t/** Explicitly select and persist the OAuth account used by this provider. */\n\tselectOAuthAccount(provider: string, accountIndex: number): void {\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tconst credential = currentData[provider];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\tthrow new Error(`No OAuth accounts configured for ${provider}`);\n\t\t\t}\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tif (!Number.isInteger(accountIndex) || accountIndex < 0 || accountIndex >= accounts.length) {\n\t\t\t\tthrow new Error(`Invalid OAuth account index ${accountIndex} for ${provider}`);\n\t\t\t}\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[provider]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t}\n\n\t/**\n\t * Return the explicitly selected OAuth credentials without refreshing tokens.\n\t * Used by provider model customization hooks.\n\t */\n\tgetOAuthCredentials(provider: string): OAuthCredentials | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\treturn accounts[getSelectedOAuthAccountIndex(credential, accounts.length)];\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\treturn { configured: false };\n\t\t}\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn omitRetiredGrokOAuthProxy({ ...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. Repeated logins append distinct accounts and\n\t * update an existing account when the provider exposes a stable identity.\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\tif (this.loadError) {\n\t\t\tthis.data[providerId] = { type: \"oauth\", ...credentials };\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 existing = currentData[providerId];\n\t\t\t\tlet credential: OAuthCredential;\n\n\t\t\t\tif (existing?.type === \"oauth\") {\n\t\t\t\t\tconst accounts = [...getOAuthAccounts(existing)];\n\t\t\t\t\tconst matchingIndex = findMatchingOAuthAccount(accounts, credentials);\n\t\t\t\t\tlet activeAccount: number;\n\t\t\t\t\tif (matchingIndex >= 0) {\n\t\t\t\t\t\taccounts[matchingIndex] = credentials;\n\t\t\t\t\t\tactiveAccount = matchingIndex;\n\t\t\t\t\t} else {\n\t\t\t\t\t\taccounts.push(credentials);\n\t\t\t\t\t\tactiveAccount = accounts.length - 1;\n\t\t\t\t\t}\n\t\t\t\t\tcredential = createOAuthCredential(accounts, activeAccount);\n\t\t\t\t} else {\n\t\t\t\t\tcredential = createOAuthCredential([credentials]);\n\t\t\t\t}\n\n\t\t\t\tconst merged: AuthStorageData = { ...currentData, [providerId]: credential };\n\t\t\t\tthis.data = merged;\n\t\t\t\tthis.loadError = null;\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 * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Resolve the explicitly selected account and refresh it under the storage lock.\n\t *\n\t * `minRemainingMs` lets a long operation demand headroom. An access token is\n\t * resolved once and then reused for the whole request, so a token that is\n\t * merely \"not expired yet\" can still expire mid-flight and come back as a\n\t * provider 401. Callers that run for minutes (compaction) ask for a margin.\n\t */\n\tprivate async resolveOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t\tminRemainingMs = 0,\n\t\trejectedApiKey?: string,\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\treturn 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 credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tconst accountIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\t\tconst account = accounts[accountIndex];\n\t\t\tif (!account) return { result: null };\n\n\t\t\tconst resolved = await this.resolveAccountToken(provider, providerId, account, minRemainingMs, rejectedApiKey);\n\t\t\tif (!resolved) return { result: null };\n\n\t\t\tif (resolved.newCredentials === account) {\n\t\t\t\treturn { result: resolved };\n\t\t\t}\n\n\t\t\tconst newCredentials = { ...account, ...resolved.newCredentials };\n\t\t\taccounts[accountIndex] = newCredentials;\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\treturn {\n\t\t\t\tresult: { ...resolved, newCredentials },\n\t\t\t\tnext: JSON.stringify(merged, null, 2),\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Force-refresh the selected account after the provider rejected `rejectedApiKey`.\n\t *\n\t * Local expiry is not the last word: ChatGPT/Codex answers `401 token_expired`\n\t * for a token whose JWT `exp` and our stored `expires` still lie days ahead\n\t * (server-side revocation, rotation from another login). Refreshing only on\n\t * the local clock would keep replaying the dead token until that date.\n\t *\n\t * Several omk processes share auth.json. If the stored token no longer equals\n\t * the rejected one, another process already rotated it and the current token\n\t * is returned without a second refresh, which would only burn the new refresh\n\t * token. A failed refresh propagates so the caller can ask for `/login`; the\n\t * credential is left in place for that login to replace.\n\t */\n\tasync refreshRejectedOAuthToken(providerId: OAuthProviderId, rejectedApiKey: string): Promise<string | undefined> {\n\t\treturn (await this.resolveOAuthTokenWithLock(providerId, 0, rejectedApiKey))?.apiKey;\n\t}\n\n\t/**\n\t * Token for one account, refreshing when it is expired or too close to it.\n\t *\n\t * Hard expiry must refresh, and a refresh failure there is fatal. A margin\n\t * miss is only a precaution: the token is still valid, so a failed proactive\n\t * refresh falls back to it instead of breaking a working session. A token the\n\t * provider already rejected (`rejectedApiKey`) is refreshed unconditionally,\n\t * and that failure is fatal too.\n\t */\n\tprivate async resolveAccountToken(\n\t\tprovider: OAuthProviderInterface,\n\t\tproviderId: OAuthProviderId,\n\t\taccount: OAuthCredentials,\n\t\tminRemainingMs: number,\n\t\trejectedApiKey?: string,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst now = Date.now();\n\t\tif (now >= account.expires) {\n\t\t\treturn await getOAuthApiKey(providerId, { [providerId]: account });\n\t\t}\n\t\tif (rejectedApiKey !== undefined && provider.getApiKey(account) === rejectedApiKey) {\n\t\t\tconst refreshed = await provider.refreshToken(account);\n\t\t\treturn { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };\n\t\t}\n\t\tif (minRemainingMs > 0 && now + minRemainingMs >= account.expires) {\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(account);\n\t\t\t\treturn { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };\n\t\t\t} catch {\n\t\t\t\t// Still valid right now; prefer a usable token over a hard failure.\n\t\t\t}\n\t\t}\n\t\treturn { apiKey: provider.getApiKey(account), newCredentials: account };\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. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(\n\t\tproviderId: string,\n\t\toptions?: { includeFallback?: boolean; minRemainingMs?: number },\n\t): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.resolveOAuthTokenWithLock(providerId, options?.minRemainingMs ?? 0);\n\t\t\t\tif (result) {\n\t\t\t\t\treturn result.apiKey;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\t// Credentials are preserved so a later request or /login can recover.\n\t\t\t\tthis.reload();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
|
|
@@ -557,8 +557,15 @@ export class AuthStorage {
|
|
|
557
557
|
logout(provider) {
|
|
558
558
|
this.remove(provider);
|
|
559
559
|
}
|
|
560
|
-
/**
|
|
561
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Resolve the explicitly selected account and refresh it under the storage lock.
|
|
562
|
+
*
|
|
563
|
+
* `minRemainingMs` lets a long operation demand headroom. An access token is
|
|
564
|
+
* resolved once and then reused for the whole request, so a token that is
|
|
565
|
+
* merely "not expired yet" can still expire mid-flight and come back as a
|
|
566
|
+
* provider 401. Callers that run for minutes (compaction) ask for a margin.
|
|
567
|
+
*/
|
|
568
|
+
async resolveOAuthTokenWithLock(providerId, minRemainingMs = 0, rejectedApiKey) {
|
|
562
569
|
const provider = getOAuthProvider(providerId);
|
|
563
570
|
if (!provider) {
|
|
564
571
|
return null;
|
|
@@ -576,9 +583,7 @@ export class AuthStorage {
|
|
|
576
583
|
const account = accounts[accountIndex];
|
|
577
584
|
if (!account)
|
|
578
585
|
return { result: null };
|
|
579
|
-
const resolved =
|
|
580
|
-
? { apiKey: provider.getApiKey(account), newCredentials: account }
|
|
581
|
-
: await getOAuthApiKey(providerId, { [providerId]: account });
|
|
586
|
+
const resolved = await this.resolveAccountToken(provider, providerId, account, minRemainingMs, rejectedApiKey);
|
|
582
587
|
if (!resolved)
|
|
583
588
|
return { result: null };
|
|
584
589
|
if (resolved.newCredentials === account) {
|
|
@@ -597,6 +602,52 @@ export class AuthStorage {
|
|
|
597
602
|
};
|
|
598
603
|
});
|
|
599
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Force-refresh the selected account after the provider rejected `rejectedApiKey`.
|
|
607
|
+
*
|
|
608
|
+
* Local expiry is not the last word: ChatGPT/Codex answers `401 token_expired`
|
|
609
|
+
* for a token whose JWT `exp` and our stored `expires` still lie days ahead
|
|
610
|
+
* (server-side revocation, rotation from another login). Refreshing only on
|
|
611
|
+
* the local clock would keep replaying the dead token until that date.
|
|
612
|
+
*
|
|
613
|
+
* Several omk processes share auth.json. If the stored token no longer equals
|
|
614
|
+
* the rejected one, another process already rotated it and the current token
|
|
615
|
+
* is returned without a second refresh, which would only burn the new refresh
|
|
616
|
+
* token. A failed refresh propagates so the caller can ask for `/login`; the
|
|
617
|
+
* credential is left in place for that login to replace.
|
|
618
|
+
*/
|
|
619
|
+
async refreshRejectedOAuthToken(providerId, rejectedApiKey) {
|
|
620
|
+
return (await this.resolveOAuthTokenWithLock(providerId, 0, rejectedApiKey))?.apiKey;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Token for one account, refreshing when it is expired or too close to it.
|
|
624
|
+
*
|
|
625
|
+
* Hard expiry must refresh, and a refresh failure there is fatal. A margin
|
|
626
|
+
* miss is only a precaution: the token is still valid, so a failed proactive
|
|
627
|
+
* refresh falls back to it instead of breaking a working session. A token the
|
|
628
|
+
* provider already rejected (`rejectedApiKey`) is refreshed unconditionally,
|
|
629
|
+
* and that failure is fatal too.
|
|
630
|
+
*/
|
|
631
|
+
async resolveAccountToken(provider, providerId, account, minRemainingMs, rejectedApiKey) {
|
|
632
|
+
const now = Date.now();
|
|
633
|
+
if (now >= account.expires) {
|
|
634
|
+
return await getOAuthApiKey(providerId, { [providerId]: account });
|
|
635
|
+
}
|
|
636
|
+
if (rejectedApiKey !== undefined && provider.getApiKey(account) === rejectedApiKey) {
|
|
637
|
+
const refreshed = await provider.refreshToken(account);
|
|
638
|
+
return { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };
|
|
639
|
+
}
|
|
640
|
+
if (minRemainingMs > 0 && now + minRemainingMs >= account.expires) {
|
|
641
|
+
try {
|
|
642
|
+
const refreshed = await provider.refreshToken(account);
|
|
643
|
+
return { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
// Still valid right now; prefer a usable token over a hard failure.
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return { apiKey: provider.getApiKey(account), newCredentials: account };
|
|
650
|
+
}
|
|
600
651
|
/**
|
|
601
652
|
* Get API key for a provider.
|
|
602
653
|
* Priority:
|
|
@@ -618,7 +669,7 @@ export class AuthStorage {
|
|
|
618
669
|
}
|
|
619
670
|
if (cred?.type === "oauth") {
|
|
620
671
|
try {
|
|
621
|
-
const result = await this.resolveOAuthTokenWithLock(providerId);
|
|
672
|
+
const result = await this.resolveOAuthTokenWithLock(providerId, options?.minRemainingMs ?? 0);
|
|
622
673
|
if (result) {
|
|
623
674
|
return result.apiKey;
|
|
624
675
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-storage.js","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 OMK instances\n * try to refresh tokens simultaneously.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"omk-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"omk-ai/oauth\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nconst RETIRED_GROK_OAUTH_PROXY = \"grok-oauth-proxy\";\n\nfunction omitRetiredGrokOAuthProxy(data: AuthStorageData): AuthStorageData {\n\tif (!(RETIRED_GROK_OAUTH_PROXY in data)) return data;\n\tconst { [RETIRED_GROK_OAUTH_PROXY]: _retired, ...kept } = data;\n\treturn kept;\n}\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n\t/** Complete account list when more than one subscription account is configured. */\n\taccounts?: OAuthCredentials[];\n\t/** Index of the account explicitly selected for this provider. */\n\tactiveAccount?: number;\n\t/** Legacy cursor accepted when migrating older auth.json files. */\n\tnextAccount?: number;\n} & OAuthCredentials;\n\nexport interface OAuthAccountSummary {\n\tindex: number;\n\tlabel: string;\n\tselected: boolean;\n}\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nconst OAUTH_ACCOUNT_ID_FIELDS = [\"accountId\", \"email\", \"userId\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_FIELDS = [\"email\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_MAX_LENGTH = 96;\nconst UNSAFE_ACCOUNT_LABEL_PATTERN =\n\t/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f\\ufeff]/g;\n\nfunction sanitizeOAuthAccountLabel(value: string): string | undefined {\n\tconst sanitized = stripAnsi(value)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(UNSAFE_ACCOUNT_LABEL_PATTERN, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, OAUTH_ACCOUNT_LABEL_MAX_LENGTH);\n\treturn sanitized || undefined;\n}\n\nfunction stripOAuthStorageMetadata(credential: OAuthCredential): OAuthCredentials {\n\tconst {\n\t\ttype: _type,\n\t\taccounts: _accounts,\n\t\tactiveAccount: _activeAccount,\n\t\tnextAccount: _nextAccount,\n\t\t...credentials\n\t} = credential;\n\treturn credentials;\n}\n\nfunction getOAuthAccounts(credential: OAuthCredential): OAuthCredentials[] {\n\tif (Array.isArray(credential.accounts) && credential.accounts.length > 0) {\n\t\treturn credential.accounts;\n\t}\n\treturn [stripOAuthStorageMetadata(credential)];\n}\n\nfunction normalizeOAuthAccountIndex(index: unknown, accountCount: number): number {\n\tif (accountCount <= 0 || typeof index !== \"number\" || !Number.isInteger(index)) {\n\t\treturn 0;\n\t}\n\treturn ((index % accountCount) + accountCount) % accountCount;\n}\n\nfunction getSelectedOAuthAccountIndex(credential: OAuthCredential, accountCount: number): number {\n\treturn normalizeOAuthAccountIndex(credential.activeAccount ?? credential.nextAccount, accountCount);\n}\n\nfunction createOAuthCredential(accounts: OAuthCredentials[], activeAccount = 0): OAuthCredential {\n\tconst selectedIndex = normalizeOAuthAccountIndex(activeAccount, accounts.length);\n\tconst selected = accounts[selectedIndex];\n\tif (!selected) {\n\t\tthrow new Error(\"OAuth credential must contain at least one account\");\n\t}\n\tif (accounts.length === 1) {\n\t\treturn { ...selected, type: \"oauth\" };\n\t}\n\treturn {\n\t\t...selected,\n\t\ttype: \"oauth\",\n\t\taccounts,\n\t\tactiveAccount: selectedIndex,\n\t};\n}\n\nfunction getOAuthAccountIdentity(credentials: OAuthCredentials): string | undefined {\n\tconst accountId = credentials.accountId;\n\tconst orgId = credentials.orgId;\n\tif (typeof accountId === \"string\" && accountId.trim() && typeof orgId === \"string\" && orgId.trim()) {\n\t\treturn `accountId:${accountId.trim().toLowerCase()}:orgId:${orgId.trim().toLowerCase()}`;\n\t}\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\treturn `${field}:${value.trim().toLowerCase()}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction getOAuthAccountDisplayLabel(providerId: string, credentials: OAuthCredentials): string | undefined {\n\tfor (const field of OAUTH_ACCOUNT_LABEL_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\tconst label = sanitizeOAuthAccountLabel(value);\n\t\t\tif (!label) continue;\n\t\t\tconst orgName =\n\t\t\t\ttypeof credentials.orgName === \"string\" ? sanitizeOAuthAccountLabel(credentials.orgName) : undefined;\n\t\t\treturn sanitizeOAuthAccountLabel(orgName ? `${label} (${orgName})` : label);\n\t\t}\n\t}\n\ttry {\n\t\tconst label = getOAuthProvider(providerId)?.getAccountLabel?.(credentials);\n\t\treturn typeof label === \"string\" ? sanitizeOAuthAccountLabel(label) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction findMatchingOAuthAccount(accounts: OAuthCredentials[], credentials: OAuthCredentials): number {\n\tconst identity = getOAuthAccountIdentity(credentials);\n\tif (identity) {\n\t\tconst exactMatch = accounts.findIndex((account) => getOAuthAccountIdentity(account) === identity);\n\t\tif (exactMatch >= 0) return exactMatch;\n\t}\n\n\t// Let newly enriched credentials update older entries that predate email/org metadata.\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value !== \"string\" || !value.trim()) continue;\n\t\tconst normalizedValue = value.trim().toLowerCase();\n\t\tconst orgId = typeof credentials.orgId === \"string\" ? credentials.orgId.trim().toLowerCase() : \"\";\n\t\tconst compatibleMatch = accounts.findIndex((account) => {\n\t\t\tconst existingValue = account[field];\n\t\t\tif (typeof existingValue !== \"string\" || existingValue.trim().toLowerCase() !== normalizedValue) return false;\n\t\t\tconst existingOrgId = typeof account.orgId === \"string\" ? account.orgId.trim().toLowerCase() : \"\";\n\t\t\treturn !orgId || !existingOrgId || orgId === existingOrgId;\n\t\t});\n\t\tif (compatibleMatch >= 0) return compatibleMatch;\n\t}\n\n\treturn accounts.findIndex((account) => account.refresh === credentials.refresh);\n}\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\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\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\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, \"{}\", AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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 fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.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.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 parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\treturn omitRetiredGrokOAuthProxy(JSON.parse(content) as AuthStorageData);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(`Failed to parse auth storage: ${message}`);\n\t\t}\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 (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t} else if (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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return undefined;\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\tdelete this.data[provider];\n\t\t\tthis.persistProviderChange(provider, undefined);\n\t\t\treturn;\n\t\t}\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\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data).filter((provider) => provider !== RETIRED_GROK_OAUTH_PROXY);\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/** Return the number of stored OAuth accounts for a provider. */\n\tgetOAuthAccountCount(provider: string): number {\n\t\tconst credential = this.data[provider];\n\t\treturn credential?.type === \"oauth\" ? getOAuthAccounts(credential).length : 0;\n\t}\n\n\t/** Return safe, human-readable labels for every stored OAuth account. */\n\tlistOAuthAccounts(provider: string): OAuthAccountSummary[] {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") return [];\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\tconst selectedIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\treturn accounts.map((account, index) => ({\n\t\t\tindex,\n\t\t\tlabel: getOAuthAccountDisplayLabel(provider, account) ?? `Account ${index + 1}`,\n\t\t\tselected: index === selectedIndex,\n\t\t}));\n\t}\n\n\t/** Return the human-readable label of the selected OAuth account. */\n\tgetOAuthAccountLabel(provider: string): string | undefined {\n\t\treturn this.listOAuthAccounts(provider).find((account) => account.selected)?.label;\n\t}\n\n\t/** Explicitly select and persist the OAuth account used by this provider. */\n\tselectOAuthAccount(provider: string, accountIndex: number): void {\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tconst credential = currentData[provider];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\tthrow new Error(`No OAuth accounts configured for ${provider}`);\n\t\t\t}\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tif (!Number.isInteger(accountIndex) || accountIndex < 0 || accountIndex >= accounts.length) {\n\t\t\t\tthrow new Error(`Invalid OAuth account index ${accountIndex} for ${provider}`);\n\t\t\t}\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[provider]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t}\n\n\t/**\n\t * Return the explicitly selected OAuth credentials without refreshing tokens.\n\t * Used by provider model customization hooks.\n\t */\n\tgetOAuthCredentials(provider: string): OAuthCredentials | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\treturn accounts[getSelectedOAuthAccountIndex(credential, accounts.length)];\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\treturn { configured: false };\n\t\t}\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn omitRetiredGrokOAuthProxy({ ...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. Repeated logins append distinct accounts and\n\t * update an existing account when the provider exposes a stable identity.\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\tif (this.loadError) {\n\t\t\tthis.data[providerId] = { type: \"oauth\", ...credentials };\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 existing = currentData[providerId];\n\t\t\t\tlet credential: OAuthCredential;\n\n\t\t\t\tif (existing?.type === \"oauth\") {\n\t\t\t\t\tconst accounts = [...getOAuthAccounts(existing)];\n\t\t\t\t\tconst matchingIndex = findMatchingOAuthAccount(accounts, credentials);\n\t\t\t\t\tlet activeAccount: number;\n\t\t\t\t\tif (matchingIndex >= 0) {\n\t\t\t\t\t\taccounts[matchingIndex] = credentials;\n\t\t\t\t\t\tactiveAccount = matchingIndex;\n\t\t\t\t\t} else {\n\t\t\t\t\t\taccounts.push(credentials);\n\t\t\t\t\t\tactiveAccount = accounts.length - 1;\n\t\t\t\t\t}\n\t\t\t\t\tcredential = createOAuthCredential(accounts, activeAccount);\n\t\t\t\t} else {\n\t\t\t\t\tcredential = createOAuthCredential([credentials]);\n\t\t\t\t}\n\n\t\t\t\tconst merged: AuthStorageData = { ...currentData, [providerId]: credential };\n\t\t\t\tthis.data = merged;\n\t\t\t\tthis.loadError = null;\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 * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/** Resolve the explicitly selected account and refresh it under the storage lock. */\n\tprivate async resolveOAuthTokenWithLock(\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\treturn 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 credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tconst accountIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\t\tconst account = accounts[accountIndex];\n\t\t\tif (!account) return { result: null };\n\n\t\t\tconst resolved =\n\t\t\t\tDate.now() < account.expires\n\t\t\t\t\t? { apiKey: provider.getApiKey(account), newCredentials: account }\n\t\t\t\t\t: await getOAuthApiKey(providerId, { [providerId]: account });\n\t\t\tif (!resolved) return { result: null };\n\n\t\t\tif (resolved.newCredentials === account) {\n\t\t\t\treturn { result: resolved };\n\t\t\t}\n\n\t\t\tconst newCredentials = { ...account, ...resolved.newCredentials };\n\t\t\taccounts[accountIndex] = newCredentials;\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\treturn {\n\t\t\t\tresult: { ...resolved, newCredentials },\n\t\t\t\tnext: JSON.stringify(merged, null, 2),\n\t\t\t};\n\t\t});\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. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.resolveOAuthTokenWithLock(providerId);\n\t\t\t\tif (result) {\n\t\t\t\t\treturn result.apiKey;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\t// Credentials are preserved so a later request or /login can recover.\n\t\t\t\tthis.reload();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-storage.js","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 OMK instances\n * try to refresh tokens simultaneously.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n\ttype OAuthProviderInterface,\n} from \"omk-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"omk-ai/oauth\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nconst RETIRED_GROK_OAUTH_PROXY = \"grok-oauth-proxy\";\n\nfunction omitRetiredGrokOAuthProxy(data: AuthStorageData): AuthStorageData {\n\tif (!(RETIRED_GROK_OAUTH_PROXY in data)) return data;\n\tconst { [RETIRED_GROK_OAUTH_PROXY]: _retired, ...kept } = data;\n\treturn kept;\n}\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n\t/** Complete account list when more than one subscription account is configured. */\n\taccounts?: OAuthCredentials[];\n\t/** Index of the account explicitly selected for this provider. */\n\tactiveAccount?: number;\n\t/** Legacy cursor accepted when migrating older auth.json files. */\n\tnextAccount?: number;\n} & OAuthCredentials;\n\nexport interface OAuthAccountSummary {\n\tindex: number;\n\tlabel: string;\n\tselected: boolean;\n}\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nconst OAUTH_ACCOUNT_ID_FIELDS = [\"accountId\", \"email\", \"userId\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_FIELDS = [\"email\", \"username\"] as const;\nconst OAUTH_ACCOUNT_LABEL_MAX_LENGTH = 96;\nconst UNSAFE_ACCOUNT_LABEL_PATTERN =\n\t/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f\\ufeff]/g;\n\nfunction sanitizeOAuthAccountLabel(value: string): string | undefined {\n\tconst sanitized = stripAnsi(value)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(UNSAFE_ACCOUNT_LABEL_PATTERN, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, OAUTH_ACCOUNT_LABEL_MAX_LENGTH);\n\treturn sanitized || undefined;\n}\n\nfunction stripOAuthStorageMetadata(credential: OAuthCredential): OAuthCredentials {\n\tconst {\n\t\ttype: _type,\n\t\taccounts: _accounts,\n\t\tactiveAccount: _activeAccount,\n\t\tnextAccount: _nextAccount,\n\t\t...credentials\n\t} = credential;\n\treturn credentials;\n}\n\nfunction getOAuthAccounts(credential: OAuthCredential): OAuthCredentials[] {\n\tif (Array.isArray(credential.accounts) && credential.accounts.length > 0) {\n\t\treturn credential.accounts;\n\t}\n\treturn [stripOAuthStorageMetadata(credential)];\n}\n\nfunction normalizeOAuthAccountIndex(index: unknown, accountCount: number): number {\n\tif (accountCount <= 0 || typeof index !== \"number\" || !Number.isInteger(index)) {\n\t\treturn 0;\n\t}\n\treturn ((index % accountCount) + accountCount) % accountCount;\n}\n\nfunction getSelectedOAuthAccountIndex(credential: OAuthCredential, accountCount: number): number {\n\treturn normalizeOAuthAccountIndex(credential.activeAccount ?? credential.nextAccount, accountCount);\n}\n\nfunction createOAuthCredential(accounts: OAuthCredentials[], activeAccount = 0): OAuthCredential {\n\tconst selectedIndex = normalizeOAuthAccountIndex(activeAccount, accounts.length);\n\tconst selected = accounts[selectedIndex];\n\tif (!selected) {\n\t\tthrow new Error(\"OAuth credential must contain at least one account\");\n\t}\n\tif (accounts.length === 1) {\n\t\treturn { ...selected, type: \"oauth\" };\n\t}\n\treturn {\n\t\t...selected,\n\t\ttype: \"oauth\",\n\t\taccounts,\n\t\tactiveAccount: selectedIndex,\n\t};\n}\n\nfunction getOAuthAccountIdentity(credentials: OAuthCredentials): string | undefined {\n\tconst accountId = credentials.accountId;\n\tconst orgId = credentials.orgId;\n\tif (typeof accountId === \"string\" && accountId.trim() && typeof orgId === \"string\" && orgId.trim()) {\n\t\treturn `accountId:${accountId.trim().toLowerCase()}:orgId:${orgId.trim().toLowerCase()}`;\n\t}\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\treturn `${field}:${value.trim().toLowerCase()}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction getOAuthAccountDisplayLabel(providerId: string, credentials: OAuthCredentials): string | undefined {\n\tfor (const field of OAUTH_ACCOUNT_LABEL_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value === \"string\" && value.trim()) {\n\t\t\tconst label = sanitizeOAuthAccountLabel(value);\n\t\t\tif (!label) continue;\n\t\t\tconst orgName =\n\t\t\t\ttypeof credentials.orgName === \"string\" ? sanitizeOAuthAccountLabel(credentials.orgName) : undefined;\n\t\t\treturn sanitizeOAuthAccountLabel(orgName ? `${label} (${orgName})` : label);\n\t\t}\n\t}\n\ttry {\n\t\tconst label = getOAuthProvider(providerId)?.getAccountLabel?.(credentials);\n\t\treturn typeof label === \"string\" ? sanitizeOAuthAccountLabel(label) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction findMatchingOAuthAccount(accounts: OAuthCredentials[], credentials: OAuthCredentials): number {\n\tconst identity = getOAuthAccountIdentity(credentials);\n\tif (identity) {\n\t\tconst exactMatch = accounts.findIndex((account) => getOAuthAccountIdentity(account) === identity);\n\t\tif (exactMatch >= 0) return exactMatch;\n\t}\n\n\t// Let newly enriched credentials update older entries that predate email/org metadata.\n\tfor (const field of OAUTH_ACCOUNT_ID_FIELDS) {\n\t\tconst value = credentials[field];\n\t\tif (typeof value !== \"string\" || !value.trim()) continue;\n\t\tconst normalizedValue = value.trim().toLowerCase();\n\t\tconst orgId = typeof credentials.orgId === \"string\" ? credentials.orgId.trim().toLowerCase() : \"\";\n\t\tconst compatibleMatch = accounts.findIndex((account) => {\n\t\t\tconst existingValue = account[field];\n\t\t\tif (typeof existingValue !== \"string\" || existingValue.trim().toLowerCase() !== normalizedValue) return false;\n\t\t\tconst existingOrgId = typeof account.orgId === \"string\" ? account.orgId.trim().toLowerCase() : \"\";\n\t\t\treturn !orgId || !existingOrgId || orgId === existingOrgId;\n\t\t});\n\t\tif (compatibleMatch >= 0) return compatibleMatch;\n\t}\n\n\treturn accounts.findIndex((account) => account.refresh === credentials.refresh);\n}\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\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\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\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, \"{}\", AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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, AUTH_FILE_WRITE_OPTIONS);\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 fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.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.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 parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\treturn omitRetiredGrokOAuthProxy(JSON.parse(content) as AuthStorageData);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(`Failed to parse auth storage: ${message}`);\n\t\t}\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 (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t} else if (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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return undefined;\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\tdelete this.data[provider];\n\t\t\tthis.persistProviderChange(provider, undefined);\n\t\t\treturn;\n\t\t}\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\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data).filter((provider) => provider !== RETIRED_GROK_OAUTH_PROXY);\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (provider === RETIRED_GROK_OAUTH_PROXY) return false;\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/** Return the number of stored OAuth accounts for a provider. */\n\tgetOAuthAccountCount(provider: string): number {\n\t\tconst credential = this.data[provider];\n\t\treturn credential?.type === \"oauth\" ? getOAuthAccounts(credential).length : 0;\n\t}\n\n\t/** Return safe, human-readable labels for every stored OAuth account. */\n\tlistOAuthAccounts(provider: string): OAuthAccountSummary[] {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") return [];\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\tconst selectedIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\treturn accounts.map((account, index) => ({\n\t\t\tindex,\n\t\t\tlabel: getOAuthAccountDisplayLabel(provider, account) ?? `Account ${index + 1}`,\n\t\t\tselected: index === selectedIndex,\n\t\t}));\n\t}\n\n\t/** Return the human-readable label of the selected OAuth account. */\n\tgetOAuthAccountLabel(provider: string): string | undefined {\n\t\treturn this.listOAuthAccounts(provider).find((account) => account.selected)?.label;\n\t}\n\n\t/** Explicitly select and persist the OAuth account used by this provider. */\n\tselectOAuthAccount(provider: string, accountIndex: number): void {\n\t\tthis.storage.withLock((current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tconst credential = currentData[provider];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\tthrow new Error(`No OAuth accounts configured for ${provider}`);\n\t\t\t}\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tif (!Number.isInteger(accountIndex) || accountIndex < 0 || accountIndex >= accounts.length) {\n\t\t\t\tthrow new Error(`Invalid OAuth account index ${accountIndex} for ${provider}`);\n\t\t\t}\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[provider]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t}\n\n\t/**\n\t * Return the explicitly selected OAuth credentials without refreshing tokens.\n\t * Used by provider model customization hooks.\n\t */\n\tgetOAuthCredentials(provider: string): OAuthCredentials | undefined {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"oauth\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst accounts = getOAuthAccounts(credential);\n\t\treturn accounts[getSelectedOAuthAccountIndex(credential, accounts.length)];\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\tif (provider === RETIRED_GROK_OAUTH_PROXY) {\n\t\t\treturn { configured: false };\n\t\t}\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn omitRetiredGrokOAuthProxy({ ...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. Repeated logins append distinct accounts and\n\t * update an existing account when the provider exposes a stable identity.\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\tif (this.loadError) {\n\t\t\tthis.data[providerId] = { type: \"oauth\", ...credentials };\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 existing = currentData[providerId];\n\t\t\t\tlet credential: OAuthCredential;\n\n\t\t\t\tif (existing?.type === \"oauth\") {\n\t\t\t\t\tconst accounts = [...getOAuthAccounts(existing)];\n\t\t\t\t\tconst matchingIndex = findMatchingOAuthAccount(accounts, credentials);\n\t\t\t\t\tlet activeAccount: number;\n\t\t\t\t\tif (matchingIndex >= 0) {\n\t\t\t\t\t\taccounts[matchingIndex] = credentials;\n\t\t\t\t\t\tactiveAccount = matchingIndex;\n\t\t\t\t\t} else {\n\t\t\t\t\t\taccounts.push(credentials);\n\t\t\t\t\t\tactiveAccount = accounts.length - 1;\n\t\t\t\t\t}\n\t\t\t\t\tcredential = createOAuthCredential(accounts, activeAccount);\n\t\t\t\t} else {\n\t\t\t\t\tcredential = createOAuthCredential([credentials]);\n\t\t\t\t}\n\n\t\t\t\tconst merged: AuthStorageData = { ...currentData, [providerId]: credential };\n\t\t\t\tthis.data = merged;\n\t\t\t\tthis.loadError = null;\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 * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Resolve the explicitly selected account and refresh it under the storage lock.\n\t *\n\t * `minRemainingMs` lets a long operation demand headroom. An access token is\n\t * resolved once and then reused for the whole request, so a token that is\n\t * merely \"not expired yet\" can still expire mid-flight and come back as a\n\t * provider 401. Callers that run for minutes (compaction) ask for a margin.\n\t */\n\tprivate async resolveOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t\tminRemainingMs = 0,\n\t\trejectedApiKey?: string,\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\treturn 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 credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst accounts = [...getOAuthAccounts(credential)];\n\t\t\tconst accountIndex = getSelectedOAuthAccountIndex(credential, accounts.length);\n\t\t\tconst account = accounts[accountIndex];\n\t\t\tif (!account) return { result: null };\n\n\t\t\tconst resolved = await this.resolveAccountToken(provider, providerId, account, minRemainingMs, rejectedApiKey);\n\t\t\tif (!resolved) return { result: null };\n\n\t\t\tif (resolved.newCredentials === account) {\n\t\t\t\treturn { result: resolved };\n\t\t\t}\n\n\t\t\tconst newCredentials = { ...account, ...resolved.newCredentials };\n\t\t\taccounts[accountIndex] = newCredentials;\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: createOAuthCredential(accounts, accountIndex),\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\treturn {\n\t\t\t\tresult: { ...resolved, newCredentials },\n\t\t\t\tnext: JSON.stringify(merged, null, 2),\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Force-refresh the selected account after the provider rejected `rejectedApiKey`.\n\t *\n\t * Local expiry is not the last word: ChatGPT/Codex answers `401 token_expired`\n\t * for a token whose JWT `exp` and our stored `expires` still lie days ahead\n\t * (server-side revocation, rotation from another login). Refreshing only on\n\t * the local clock would keep replaying the dead token until that date.\n\t *\n\t * Several omk processes share auth.json. If the stored token no longer equals\n\t * the rejected one, another process already rotated it and the current token\n\t * is returned without a second refresh, which would only burn the new refresh\n\t * token. A failed refresh propagates so the caller can ask for `/login`; the\n\t * credential is left in place for that login to replace.\n\t */\n\tasync refreshRejectedOAuthToken(providerId: OAuthProviderId, rejectedApiKey: string): Promise<string | undefined> {\n\t\treturn (await this.resolveOAuthTokenWithLock(providerId, 0, rejectedApiKey))?.apiKey;\n\t}\n\n\t/**\n\t * Token for one account, refreshing when it is expired or too close to it.\n\t *\n\t * Hard expiry must refresh, and a refresh failure there is fatal. A margin\n\t * miss is only a precaution: the token is still valid, so a failed proactive\n\t * refresh falls back to it instead of breaking a working session. A token the\n\t * provider already rejected (`rejectedApiKey`) is refreshed unconditionally,\n\t * and that failure is fatal too.\n\t */\n\tprivate async resolveAccountToken(\n\t\tprovider: OAuthProviderInterface,\n\t\tproviderId: OAuthProviderId,\n\t\taccount: OAuthCredentials,\n\t\tminRemainingMs: number,\n\t\trejectedApiKey?: string,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst now = Date.now();\n\t\tif (now >= account.expires) {\n\t\t\treturn await getOAuthApiKey(providerId, { [providerId]: account });\n\t\t}\n\t\tif (rejectedApiKey !== undefined && provider.getApiKey(account) === rejectedApiKey) {\n\t\t\tconst refreshed = await provider.refreshToken(account);\n\t\t\treturn { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };\n\t\t}\n\t\tif (minRemainingMs > 0 && now + minRemainingMs >= account.expires) {\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(account);\n\t\t\t\treturn { apiKey: provider.getApiKey(refreshed), newCredentials: refreshed };\n\t\t\t} catch {\n\t\t\t\t// Still valid right now; prefer a usable token over a hard failure.\n\t\t\t}\n\t\t}\n\t\treturn { apiKey: provider.getApiKey(account), newCredentials: account };\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. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(\n\t\tproviderId: string,\n\t\toptions?: { includeFallback?: boolean; minRemainingMs?: number },\n\t): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.resolveOAuthTokenWithLock(providerId, options?.minRemainingMs ?? 0);\n\t\t\t\tif (result) {\n\t\t\t\t\treturn result.apiKey;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\t// Credentials are preserved so a later request or /login can recover.\n\t\t\t\tthis.reload();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One forced OAuth refresh and retry for a summarization whose token the
|
|
3
|
+
* provider rejected.
|
|
4
|
+
*
|
|
5
|
+
* Compaction runs on `compaction.model`, which a session may never use for
|
|
6
|
+
* turns, so its OAuth token can be dead while every turn on the session model
|
|
7
|
+
* succeeds. The stored expiry is not the last word either: ChatGPT/Codex
|
|
8
|
+
* answers `401 token_expired` days before the JWT `exp`. The transient-retry
|
|
9
|
+
* classifier rightly refuses to replay a 401, so without this step the same
|
|
10
|
+
* dead token would be re-sent until the stored expiry date passed.
|
|
11
|
+
*/
|
|
12
|
+
/** Provider rejected the bearer token itself, as opposed to quota, overload, or transcript errors. */
|
|
13
|
+
export declare function isRejectedOAuthTokenMessage(text: string | undefined): boolean;
|
|
14
|
+
export interface OAuthRecoveryInput<T> {
|
|
15
|
+
/** The api key the failed request was sent with; `undefined` skips recovery. */
|
|
16
|
+
readonly apiKey: string | undefined;
|
|
17
|
+
/** Provider id used only for the `/login` hint when the refresh fails. */
|
|
18
|
+
readonly provider?: string;
|
|
19
|
+
/** Runs the summarization with the given key; called at most twice. */
|
|
20
|
+
readonly run: (apiKey: string | undefined) => Promise<T>;
|
|
21
|
+
/**
|
|
22
|
+
* Force-refreshes the rejected token. Resolves with the replacement key, or
|
|
23
|
+
* `undefined` when the provider has no OAuth credential to refresh. A
|
|
24
|
+
* rejection means the refresh itself failed and the user must log in again.
|
|
25
|
+
*/
|
|
26
|
+
readonly refreshRejectedToken: (rejectedApiKey: string) => Promise<string | undefined>;
|
|
27
|
+
}
|
|
28
|
+
export declare function summarizeWithOAuthRecovery<T>(input: OAuthRecoveryInput<T>): Promise<T>;
|
|
29
|
+
//# sourceMappingURL=oauth-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-recovery.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/oauth-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,sGAAsG;AACtG,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE7E;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACpC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACvF;AAMD,wBAAsB,0BAA0B,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAkB5F","sourcesContent":["/**\n * One forced OAuth refresh and retry for a summarization whose token the\n * provider rejected.\n *\n * Compaction runs on `compaction.model`, which a session may never use for\n * turns, so its OAuth token can be dead while every turn on the session model\n * succeeds. The stored expiry is not the last word either: ChatGPT/Codex\n * answers `401 token_expired` days before the JWT `exp`. The transient-retry\n * classifier rightly refuses to replay a 401, so without this step the same\n * dead token would be re-sent until the stored expiry date passed.\n */\n\nconst REJECTED_OAUTH_TOKEN_PATTERN =\n\t/token_expired|token_revoked|authentication token is expired|token (?:is|has) expired|invalid_token|\\b401\\b|unauthori[sz]ed/i;\n\n/** Provider rejected the bearer token itself, as opposed to quota, overload, or transcript errors. */\nexport function isRejectedOAuthTokenMessage(text: string | undefined): boolean {\n\treturn text !== undefined && REJECTED_OAUTH_TOKEN_PATTERN.test(text);\n}\n\nexport interface OAuthRecoveryInput<T> {\n\t/** The api key the failed request was sent with; `undefined` skips recovery. */\n\treadonly apiKey: string | undefined;\n\t/** Provider id used only for the `/login` hint when the refresh fails. */\n\treadonly provider?: string;\n\t/** Runs the summarization with the given key; called at most twice. */\n\treadonly run: (apiKey: string | undefined) => Promise<T>;\n\t/**\n\t * Force-refreshes the rejected token. Resolves with the replacement key, or\n\t * `undefined` when the provider has no OAuth credential to refresh. A\n\t * rejection means the refresh itself failed and the user must log in again.\n\t */\n\treadonly refreshRejectedToken: (rejectedApiKey: string) => Promise<string | undefined>;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport async function summarizeWithOAuthRecovery<T>(input: OAuthRecoveryInput<T>): Promise<T> {\n\ttry {\n\t\treturn await input.run(input.apiKey);\n\t} catch (error) {\n\t\tif (input.apiKey === undefined || !isRejectedOAuthTokenMessage(errorMessage(error))) throw error;\n\t\tlet refreshed: string | undefined;\n\t\ttry {\n\t\t\trefreshed = await input.refreshRejectedToken(input.apiKey);\n\t\t} catch (refreshError) {\n\t\t\tconst provider = input.provider ?? \"the provider\";\n\t\t\tthrow new Error(\n\t\t\t\t`${errorMessage(error)} (OAuth refresh for ${provider} failed: ${errorMessage(refreshError)}; run '/login ${provider}')`,\n\t\t\t\t{ cause: error instanceof Error ? error : undefined },\n\t\t\t);\n\t\t}\n\t\tif (refreshed === undefined) throw error;\n\t\treturn await input.run(refreshed);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One forced OAuth refresh and retry for a summarization whose token the
|
|
3
|
+
* provider rejected.
|
|
4
|
+
*
|
|
5
|
+
* Compaction runs on `compaction.model`, which a session may never use for
|
|
6
|
+
* turns, so its OAuth token can be dead while every turn on the session model
|
|
7
|
+
* succeeds. The stored expiry is not the last word either: ChatGPT/Codex
|
|
8
|
+
* answers `401 token_expired` days before the JWT `exp`. The transient-retry
|
|
9
|
+
* classifier rightly refuses to replay a 401, so without this step the same
|
|
10
|
+
* dead token would be re-sent until the stored expiry date passed.
|
|
11
|
+
*/
|
|
12
|
+
const REJECTED_OAUTH_TOKEN_PATTERN = /token_expired|token_revoked|authentication token is expired|token (?:is|has) expired|invalid_token|\b401\b|unauthori[sz]ed/i;
|
|
13
|
+
/** Provider rejected the bearer token itself, as opposed to quota, overload, or transcript errors. */
|
|
14
|
+
export function isRejectedOAuthTokenMessage(text) {
|
|
15
|
+
return text !== undefined && REJECTED_OAUTH_TOKEN_PATTERN.test(text);
|
|
16
|
+
}
|
|
17
|
+
function errorMessage(error) {
|
|
18
|
+
return error instanceof Error ? error.message : String(error);
|
|
19
|
+
}
|
|
20
|
+
export async function summarizeWithOAuthRecovery(input) {
|
|
21
|
+
try {
|
|
22
|
+
return await input.run(input.apiKey);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (input.apiKey === undefined || !isRejectedOAuthTokenMessage(errorMessage(error)))
|
|
26
|
+
throw error;
|
|
27
|
+
let refreshed;
|
|
28
|
+
try {
|
|
29
|
+
refreshed = await input.refreshRejectedToken(input.apiKey);
|
|
30
|
+
}
|
|
31
|
+
catch (refreshError) {
|
|
32
|
+
const provider = input.provider ?? "the provider";
|
|
33
|
+
throw new Error(`${errorMessage(error)} (OAuth refresh for ${provider} failed: ${errorMessage(refreshError)}; run '/login ${provider}')`, { cause: error instanceof Error ? error : undefined });
|
|
34
|
+
}
|
|
35
|
+
if (refreshed === undefined)
|
|
36
|
+
throw error;
|
|
37
|
+
return await input.run(refreshed);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=oauth-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-recovery.js","sourceRoot":"","sources":["../../../src/core/compaction/oauth-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,4BAA4B,GACjC,6HAA6H,CAAC;AAE/H,sGAAsG;AACtG,MAAM,UAAU,2BAA2B,CAAC,IAAwB,EAAW;IAC9E,OAAO,IAAI,KAAK,SAAS,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACrE;AAiBD,SAAS,YAAY,CAAC,KAAc,EAAU;IAC7C,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC9D;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAI,KAA4B,EAAc;IAC7F,IAAI,CAAC;QACJ,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,2BAA2B,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAAE,MAAM,KAAK,CAAC;QACjG,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,SAAS,GAAG,MAAM,KAAK,CAAC,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,cAAc,CAAC;YAClD,MAAM,IAAI,KAAK,CACd,GAAG,YAAY,CAAC,KAAK,CAAC,uBAAuB,QAAQ,YAAY,YAAY,CAAC,YAAY,CAAC,iBAAiB,QAAQ,IAAI,EACxH,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,CACrD,CAAC;QACH,CAAC;QACD,IAAI,SAAS,KAAK,SAAS;YAAE,MAAM,KAAK,CAAC;QACzC,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;AAAA,CACD","sourcesContent":["/**\n * One forced OAuth refresh and retry for a summarization whose token the\n * provider rejected.\n *\n * Compaction runs on `compaction.model`, which a session may never use for\n * turns, so its OAuth token can be dead while every turn on the session model\n * succeeds. The stored expiry is not the last word either: ChatGPT/Codex\n * answers `401 token_expired` days before the JWT `exp`. The transient-retry\n * classifier rightly refuses to replay a 401, so without this step the same\n * dead token would be re-sent until the stored expiry date passed.\n */\n\nconst REJECTED_OAUTH_TOKEN_PATTERN =\n\t/token_expired|token_revoked|authentication token is expired|token (?:is|has) expired|invalid_token|\\b401\\b|unauthori[sz]ed/i;\n\n/** Provider rejected the bearer token itself, as opposed to quota, overload, or transcript errors. */\nexport function isRejectedOAuthTokenMessage(text: string | undefined): boolean {\n\treturn text !== undefined && REJECTED_OAUTH_TOKEN_PATTERN.test(text);\n}\n\nexport interface OAuthRecoveryInput<T> {\n\t/** The api key the failed request was sent with; `undefined` skips recovery. */\n\treadonly apiKey: string | undefined;\n\t/** Provider id used only for the `/login` hint when the refresh fails. */\n\treadonly provider?: string;\n\t/** Runs the summarization with the given key; called at most twice. */\n\treadonly run: (apiKey: string | undefined) => Promise<T>;\n\t/**\n\t * Force-refreshes the rejected token. Resolves with the replacement key, or\n\t * `undefined` when the provider has no OAuth credential to refresh. A\n\t * rejection means the refresh itself failed and the user must log in again.\n\t */\n\treadonly refreshRejectedToken: (rejectedApiKey: string) => Promise<string | undefined>;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport async function summarizeWithOAuthRecovery<T>(input: OAuthRecoveryInput<T>): Promise<T> {\n\ttry {\n\t\treturn await input.run(input.apiKey);\n\t} catch (error) {\n\t\tif (input.apiKey === undefined || !isRejectedOAuthTokenMessage(errorMessage(error))) throw error;\n\t\tlet refreshed: string | undefined;\n\t\ttry {\n\t\t\trefreshed = await input.refreshRejectedToken(input.apiKey);\n\t\t} catch (refreshError) {\n\t\t\tconst provider = input.provider ?? \"the provider\";\n\t\t\tthrow new Error(\n\t\t\t\t`${errorMessage(error)} (OAuth refresh for ${provider} failed: ${errorMessage(refreshError)}; run '/login ${provider}')`,\n\t\t\t\t{ cause: error instanceof Error ? error : undefined },\n\t\t\t);\n\t\t}\n\t\tif (refreshed === undefined) throw error;\n\t\treturn await input.run(refreshed);\n\t}\n}\n"]}
|