@mastra/code-sdk 1.9.0 → 1.10.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +23 -0
  2. package/dist/agent-connections/tools.d.ts.map +1 -1
  3. package/dist/agent-connections/tools.js +5 -5
  4. package/dist/agent-connections/tools.js.map +1 -1
  5. package/dist/analytics.d.ts.map +1 -1
  6. package/dist/analytics.js +2 -3
  7. package/dist/analytics.js.map +1 -1
  8. package/dist/auth/account-rotation-processor.d.ts.map +1 -1
  9. package/dist/auth/account-rotation-processor.js +17 -10
  10. package/dist/auth/account-rotation-processor.js.map +1 -1
  11. package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
  12. package/dist/auth/providers/kimi-coding.js +1 -2
  13. package/dist/auth/providers/kimi-coding.js.map +1 -1
  14. package/dist/auth/providers/openai-codex.d.ts +1 -1
  15. package/dist/auth/providers/openai-codex.d.ts.map +1 -1
  16. package/dist/auth/providers/openai-codex.js +7 -19
  17. package/dist/auth/providers/openai-codex.js.map +1 -1
  18. package/dist/auth/storage.js +3 -3
  19. package/dist/auth/storage.js.map +1 -1
  20. package/dist/experimental-agent.d.ts +11 -0
  21. package/dist/experimental-agent.d.ts.map +1 -0
  22. package/dist/experimental-agent.js +36 -0
  23. package/dist/experimental-agent.js.map +1 -0
  24. package/dist/goal-manager.d.ts.map +1 -1
  25. package/dist/goal-manager.js +3 -4
  26. package/dist/goal-manager.js.map +1 -1
  27. package/dist/headless/run-mc.d.ts.map +1 -1
  28. package/dist/headless/run-mc.js +1 -1
  29. package/dist/headless/run-mc.js.map +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +23 -7
  32. package/dist/index.js.map +1 -1
  33. package/dist/knowledge-inspector.d.ts.map +1 -1
  34. package/dist/knowledge-inspector.js +1 -2
  35. package/dist/knowledge-inspector.js.map +1 -1
  36. package/dist/onboarding/settings.d.ts +9 -0
  37. package/dist/onboarding/settings.d.ts.map +1 -1
  38. package/dist/onboarding/settings.js +36 -4
  39. package/dist/onboarding/settings.js.map +1 -1
  40. package/dist/plugins/loader.d.ts.map +1 -1
  41. package/dist/plugins/loader.js +1 -2
  42. package/dist/plugins/loader.js.map +1 -1
  43. package/dist/process-memory-diagnostics.d.ts.map +1 -1
  44. package/dist/process-memory-diagnostics.js +1 -2
  45. package/dist/process-memory-diagnostics.js.map +1 -1
  46. package/dist/tools/workflows/ephemeral-memory.d.ts +21 -0
  47. package/dist/tools/workflows/ephemeral-memory.d.ts.map +1 -1
  48. package/dist/tools/workflows/ephemeral-memory.js +1 -2
  49. package/dist/tools/workflows/ephemeral-memory.js.map +1 -1
  50. package/dist/utils/signals-pubsub.d.ts.map +1 -1
  51. package/dist/utils/signals-pubsub.js +1 -2
  52. package/dist/utils/signals-pubsub.js.map +1 -1
  53. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"openai-codex.js","names":[],"sources":["../../../src/auth/providers/openai-codex.ts"],"sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * Inspired by pi-mono's OAuth implementation:\n * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/openai-codex.ts\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds (web-ui)\nlet _randomBytes: ((size: number) => Buffer) | null = null;\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nlet _cryptoPromise: Promise<typeof import('node:crypto')> | null = null;\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nlet _httpPromise: Promise<typeof import('node:http')> | null = null;\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nlet _http: typeof import('node:http') | null = null;\ntype HttpServer = {\n off: (event: 'error' | 'listening', listener: (...args: any[]) => void) => HttpServer;\n once: (event: 'error' | 'listening', listener: (...args: any[]) => void) => HttpServer;\n listen: (port: number, hostname: string) => HttpServer;\n close: () => void;\n};\nif (typeof process !== 'undefined' && (process.versions?.node || process.versions?.bun)) {\n _cryptoPromise = import('node:crypto').then(m => {\n _randomBytes = m.randomBytes;\n return m;\n });\n _httpPromise = import('node:http').then(m => {\n _http = m;\n return m;\n });\n}\n\nimport { parseAuthorizationInput } from '../authorization-input.js';\nimport { generatePKCE } from '../pkce.js';\nimport type { AuthMode, OAuthCredentials, OAuthLoginCallbacks, OAuthPrompt, OAuthProviderInterface } from '../types.js';\n\nexport const OPENAI_CODEX_AUTH_MODES: ReadonlyArray<AuthMode> = [\n {\n id: 'browser',\n name: 'Browser (local callback)',\n description: 'Opens the browser and waits for the OAuth callback on localhost.',\n },\n {\n id: 'device',\n name: 'Device code (headless)',\n description: 'Shows a code to enter at openai.com — for SSH, remote, or no-browser environments.',\n },\n];\n\nconst CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';\nconst ISSUER = 'https://auth.openai.com';\nconst AUTHORIZE_URL = `${ISSUER}/oauth/authorize`;\nconst TOKEN_URL = `${ISSUER}/oauth/token`;\nconst DEVICE_USER_CODE_URL = `${ISSUER}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${ISSUER}/api/accounts/deviceauth/token`;\nconst DEVICE_AUTHORIZE_URL = `${ISSUER}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${ISSUER}/deviceauth/callback`;\nconst DEFAULT_CALLBACK_PORT = 1455;\nconst FALLBACK_CALLBACK_PORT = 1457;\nconst DEFAULT_TOKEN_EXPIRES_IN_SECONDS = 3600;\nconst DEVICE_AUTH_TIMEOUT_MS = 15 * 60 * 1000;\nconst OAUTH_REQUEST_TIMEOUT_MS = 15_000;\nconst SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke';\nconst JWT_CLAIM_PATH = 'https://api.openai.com/auth';\n\nconst SUCCESS_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Authentication successful</title>\n</head>\n<body>\n <p>Authentication successful. Return to your terminal to continue.</p>\n</body>\n</html>`;\n\ntype TokenSuccess = {\n type: 'success';\n access: string;\n refresh: string;\n expires: number;\n idToken?: string;\n};\ntype TokenFailure = { type: 'failed' };\ntype TokenResult = TokenSuccess | TokenFailure;\n\ntype JwtPayload = {\n chatgpt_account_id?: string;\n email?: string;\n [JWT_CLAIM_PATH]?: {\n chatgpt_account_id?: string;\n email?: string;\n };\n [key: string]: unknown;\n};\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n const timeout = AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS);\n return signal ? AbortSignal.any([timeout, signal]) : timeout;\n}\n\nasync function createState(): Promise<string> {\n const randomBytes = await getRandomBytes();\n return randomBytes(16).toString('hex');\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const payload = parts[1] ?? '';\n const padded = payload\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(Math.ceil(payload.length / 4) * 4, '=');\n const decoded = atob(padded);\n return JSON.parse(decoded) as JwtPayload;\n } catch {\n return null;\n }\n}\n\nfunction extractAccountIdFromClaims(payload: JwtPayload | null | undefined): string | null {\n if (!payload) return null;\n const accountId = payload.chatgpt_account_id ?? payload[JWT_CLAIM_PATH]?.chatgpt_account_id;\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : null;\n}\n\nfunction getAccountId(tokens: { idToken?: string; access: string }, fallback?: string): string | undefined {\n const fromIdToken = tokens.idToken ? extractAccountIdFromClaims(decodeJwt(tokens.idToken)) : null;\n if (fromIdToken) return fromIdToken;\n\n const fromAccessToken = extractAccountIdFromClaims(decodeJwt(tokens.access));\n if (fromAccessToken) return fromAccessToken;\n\n return fallback;\n}\n\nfunction requireAccountId(tokens: { idToken?: string; access: string }, fallback?: string): string {\n const accountId = getAccountId(tokens, fallback);\n if (!accountId) {\n throw new Error('Failed to extract ChatGPT account id from OpenAI Codex token');\n }\n return accountId;\n}\n\nfunction extractEmailFromClaims(payload: JwtPayload): string | null {\n const email = payload.email ?? payload[JWT_CLAIM_PATH]?.email;\n return typeof email === 'string' && email.length > 0 ? email : null;\n}\n\nfunction getEmailFromTokens(tokens: { idToken?: string; access: string }): string | undefined {\n const fromIdToken = tokens.idToken ? extractEmailFromClaims(decodeJwt(tokens.idToken) ?? {}) : null;\n if (fromIdToken) return fromIdToken;\n const fromAccessToken = extractEmailFromClaims(decodeJwt(tokens.access) ?? {});\n return fromAccessToken ?? undefined;\n}\n\ntype TokenResponseJson = {\n id_token?: string;\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n};\n\nfunction tokenResponseToResult(json: TokenResponseJson, logPrefix: string): TokenResult {\n if (!json.access_token || !json.refresh_token) {\n console.error(\n `[openai-codex] ${logPrefix} response missing required fields (access_token=${Boolean(json.access_token)}, refresh_token=${Boolean(json.refresh_token)})`,\n );\n return { type: 'failed' };\n }\n\n return {\n type: 'success',\n access: json.access_token,\n refresh: json.refresh_token,\n expires: Date.now() + (json.expires_in ?? DEFAULT_TOKEN_EXPIRES_IN_SECONDS) * 1000,\n idToken: json.id_token,\n };\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n verifier: string,\n redirectUri: string,\n signal?: AbortSignal,\n): Promise<TokenResult> {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n signal: requestSignal(signal),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri,\n }),\n });\n\n if (!response.ok) {\n console.error('[openai-codex] code->token failed:', response.status);\n return { type: 'failed' };\n }\n\n return tokenResponseToResult((await response.json()) as TokenResponseJson, 'token');\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResult> {\n try {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n signal: requestSignal(),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: CLIENT_ID,\n }),\n });\n\n if (!response.ok) {\n console.error('[openai-codex] Token refresh failed:', response.status);\n return { type: 'failed' };\n }\n\n return tokenResponseToResult((await response.json()) as TokenResponseJson, 'Token refresh');\n } catch {\n console.error('[openai-codex] Token refresh request failed');\n return { type: 'failed' };\n }\n}\n\nasync function getRandomBytes() {\n if (!_randomBytes && _cryptoPromise) {\n _randomBytes = (await _cryptoPromise).randomBytes;\n }\n if (!_randomBytes) {\n throw new Error('OpenAI Codex OAuth is only available in Node.js environments');\n }\n return _randomBytes;\n}\n\nasync function createAuthorizationFlow(\n redirectUri: string,\n state: string,\n originator: string = 'mastracode',\n): Promise<{ verifier: string; url: string }> {\n const { verifier, challenge } = await generatePKCE();\n\n const url = new URL(AUTHORIZE_URL);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', CLIENT_ID);\n url.searchParams.set('redirect_uri', redirectUri);\n url.searchParams.set('scope', SCOPE);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('state', state);\n url.searchParams.set('id_token_add_organizations', 'true');\n url.searchParams.set('codex_cli_simplified_flow', 'true');\n url.searchParams.set('originator', originator);\n\n return { verifier, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n redirectUri: string;\n warning?: string;\n close: () => void;\n cancelWait: () => void;\n waitForCode: () => Promise<{ code: string } | null>;\n};\n\ntype CallbackPorts = {\n defaultPort: number;\n fallbackPort: number;\n};\n\nconst CODEX_CALLBACK_PORTS: CallbackPorts = {\n defaultPort: DEFAULT_CALLBACK_PORT,\n fallbackPort: FALLBACK_CALLBACK_PORT,\n};\n\nasync function requestCancel(port: number): Promise<void> {\n try {\n await fetch(`http://127.0.0.1:${port}/cancel`, { signal: AbortSignal.timeout(200) });\n } catch {\n // The existing listener might not be a Codex auth server.\n }\n}\n\nfunction listen(server: HttpServer, port: number): Promise<boolean> {\n return new Promise(resolve => {\n const onError = () => {\n server.off('listening', onListening);\n resolve(false);\n };\n const onListening = () => {\n server.off('error', onError);\n resolve(true);\n };\n\n server.once('error', onError);\n server.once('listening', onListening);\n server.listen(port, '127.0.0.1');\n });\n}\n\nasync function bindOAuthServer(server: HttpServer, ports: CallbackPorts): Promise<number | null> {\n await requestCancel(ports.defaultPort);\n if (await listen(server, ports.defaultPort)) return ports.defaultPort;\n if (await listen(server, ports.fallbackPort)) return ports.fallbackPort;\n\n return null;\n}\n\nasync function getHttpModule() {\n if (!_http && _httpPromise) {\n _http = await _httpPromise;\n }\n if (!_http) {\n throw new Error('OpenAI Codex OAuth is only available in Node.js environments');\n }\n return _http;\n}\n\nasync function startLocalOAuthServer(\n state: string,\n ports: CallbackPorts = CODEX_CALLBACK_PORTS,\n): Promise<OAuthServerInfo> {\n const http = await getHttpModule();\n let lastCode: string | null = null;\n let cancelled = false;\n const server = http.createServer((req, res) => {\n try {\n const url = new URL(req.url || '', 'http://localhost');\n if (url.pathname === '/cancel') {\n cancelled = true;\n res.statusCode = 200;\n res.end('Cancelled');\n return;\n }\n if (url.pathname !== '/auth/callback') {\n res.statusCode = 404;\n res.end('Not found');\n return;\n }\n if (url.searchParams.get('state') !== state) {\n res.statusCode = 400;\n res.end('State mismatch');\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n res.statusCode = 400;\n res.end('Missing authorization code');\n return;\n }\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(SUCCESS_HTML);\n lastCode = code;\n } catch {\n res.statusCode = 500;\n res.end('Internal error');\n }\n });\n\n return new Promise(resolve => {\n bindOAuthServer(server, ports).then(port => {\n if (!port) {\n resolve({\n redirectUri: `http://localhost:${ports.fallbackPort}/auth/callback`,\n warning: `OpenAI Codex OAuth requires localhost port ${ports.defaultPort} or ${ports.fallbackPort}, but both are in use. Automatic browser callback will not work until one is freed.`,\n close: () => {\n try {\n server.close();\n } catch {\n // ignore\n }\n },\n cancelWait: () => {},\n waitForCode: async () => null,\n });\n return;\n }\n\n resolve({\n redirectUri: `http://localhost:${port}/auth/callback`,\n close: () => server.close(),\n cancelWait: () => {\n cancelled = true;\n },\n waitForCode: async () => {\n const sleep = () => new Promise(r => setTimeout(r, 100));\n for (let i = 0; i < 600; i += 1) {\n if (lastCode) return { code: lastCode };\n if (cancelled) return null;\n await sleep();\n }\n return null;\n },\n });\n });\n });\n}\n\n/**\n * Serializable pending state for a Codex device-code login. Safe to persist\n * (e.g. a `pending jsonb` column) so polling can span HTTP requests — the\n * device token response carries the `code_verifier`, so no PKCE state needs\n * to be kept client-side.\n */\nexport interface CodexDeviceLoginPending {\n deviceAuthId: string;\n userCode: string;\n /** Verification URL for the user to open. */\n url: string;\n instructions: string;\n /** Poll interval in ms suggested by the server. */\n intervalMs: number;\n /** ms epoch after which the device authorization expires. */\n deadlineAt: number;\n}\n\nexport type CodexDevicePollResult =\n | { status: 'complete'; credentials: OAuthCredentials }\n | { status: 'pending'; nextPollMs: number }\n | { status: 'failed'; error: string };\n\n/**\n * Start a Codex device-code login: request a user code and return the\n * serializable pending state for subsequent polls.\n */\nexport async function startCodexDeviceLogin(options?: { signal?: AbortSignal }): Promise<CodexDeviceLoginPending> {\n const response = await fetch(DEVICE_USER_CODE_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'mastracode',\n },\n body: JSON.stringify({ client_id: CLIENT_ID, originator: 'mastracode' }),\n signal: requestSignal(options?.signal),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to initiate OpenAI Codex device authorization: ${response.status}`);\n }\n\n const deviceData = (await response.json()) as {\n device_auth_id?: string;\n user_code?: string;\n usercode?: string;\n interval?: string | number;\n };\n\n const userCode = deviceData.user_code ?? deviceData.usercode;\n\n if (!deviceData.device_auth_id || !userCode) {\n throw new Error('OpenAI Codex device authorization response missing required fields');\n }\n\n const intervalSeconds =\n typeof deviceData.interval === 'number' ? deviceData.interval : Number.parseInt(deviceData.interval ?? '', 10) || 5;\n\n return {\n deviceAuthId: deviceData.device_auth_id,\n userCode,\n url: DEVICE_AUTHORIZE_URL,\n instructions: `Enter code: ${userCode}`,\n intervalMs: Math.max(intervalSeconds, 1) * 1000,\n deadlineAt: Date.now() + DEVICE_AUTH_TIMEOUT_MS,\n };\n}\n\n/**\n * Perform exactly one upstream poll for a pending Codex device login.\n * The Codex device endpoint signals \"still pending\" via HTTP 403/404 (it is\n * not an RFC 8628 error-JSON endpoint); on success it returns the\n * authorization code plus server-held PKCE verifier, which we exchange\n * immediately for credentials. Never throws for flow-level conditions.\n */\nexport async function pollCodexDeviceLogin(\n pending: CodexDeviceLoginPending,\n options?: { signal?: AbortSignal },\n): Promise<CodexDevicePollResult> {\n if (Date.now() >= pending.deadlineAt) {\n return { status: 'failed', error: 'OpenAI Codex device authorization timed out after 15 minutes' };\n }\n\n const pollResponse = await fetch(DEVICE_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'mastracode',\n },\n body: JSON.stringify({\n device_auth_id: pending.deviceAuthId,\n user_code: pending.userCode,\n }),\n signal: requestSignal(options?.signal),\n });\n\n if (pollResponse.ok) {\n const data = (await pollResponse.json()) as {\n authorization_code?: string;\n code_verifier?: string;\n };\n\n if (!data.authorization_code || !data.code_verifier) {\n return { status: 'failed', error: 'OpenAI Codex device token response missing required fields' };\n }\n\n const tokenResult = await exchangeAuthorizationCode(\n data.authorization_code,\n data.code_verifier,\n DEVICE_REDIRECT_URI,\n options?.signal,\n );\n if (tokenResult.type !== 'success') {\n return { status: 'failed', error: 'Token exchange failed' };\n }\n\n let accountId: string;\n try {\n accountId = requireAccountId(tokenResult);\n } catch (error) {\n return { status: 'failed', error: error instanceof Error ? error.message : String(error) };\n }\n\n return {\n status: 'complete',\n credentials: {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n email: getEmailFromTokens(tokenResult),\n },\n };\n }\n\n if (pollResponse.status !== 403 && pollResponse.status !== 404) {\n return {\n status: 'failed',\n error: `OpenAI Codex device authorization failed: ${pollResponse.status}`,\n };\n }\n\n return { status: 'pending', nextPollMs: pending.intervalMs };\n}\n\nasync function loginOpenAICodexDevice(options: {\n onAuth: (info: { url: string; instructions?: string }) => void;\n onProgress?: (message: string) => void;\n signal?: AbortSignal;\n sleep?: (ms: number) => Promise<void>;\n}): Promise<OAuthCredentials> {\n const pending = await startCodexDeviceLogin({ signal: options.signal });\n const sleep = options.sleep ?? (ms => new Promise<void>(resolve => setTimeout(resolve, ms)));\n\n options.onAuth({\n url: pending.url,\n instructions: pending.instructions,\n });\n\n await sleep(pending.intervalMs);\n\n while (true) {\n if (options.signal?.aborted) {\n throw new Error('Login cancelled');\n }\n\n const result = await pollCodexDeviceLogin(pending, { signal: options.signal });\n if (result.status === 'complete') {\n return result.credentials;\n }\n if (result.status === 'failed') {\n throw new Error(result.error);\n }\n\n options.onProgress?.('Waiting for OpenAI Codex device authorization...');\n await sleep(result.nextPollMs);\n }\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"mastracode\")\n */\nexport async function loginOpenAICodex(options: {\n onAuth: (info: { url: string; instructions?: string }) => void;\n onPrompt: (prompt: OAuthPrompt) => Promise<string>;\n onProgress?: (message: string) => void;\n onManualCodeInput?: () => Promise<string>;\n signal?: AbortSignal;\n originator?: string;\n mode?: 'browser' | 'device';\n}): Promise<OAuthCredentials> {\n const envMode =\n typeof process !== 'undefined' && process.env?.MASTRACODE_OPENAI_CODEX_AUTH_MODE === 'device'\n ? 'device'\n : undefined;\n const mode = options.mode ?? envMode ?? 'browser';\n if (mode === 'device') {\n return loginOpenAICodexDevice({\n onAuth: options.onAuth,\n onProgress: options.onProgress,\n signal: options.signal,\n });\n }\n\n const state = await createState();\n const server = await startLocalOAuthServer(state);\n if (server.warning) {\n options.onProgress?.(server.warning);\n }\n const { verifier, url } = await createAuthorizationFlow(\n server.redirectUri,\n state,\n options.originator ?? 'mastracode',\n );\n\n options.onAuth({\n url,\n instructions: server.warning\n ? `${server.warning} You can still paste the authorization code or full redirect URL manually.`\n : 'A browser window should open. Complete login to finish.',\n });\n\n let code: string | undefined;\n try {\n if (options.onManualCodeInput) {\n // Race between browser callback and manual input\n let manualCode: string | undefined;\n let manualError: Error | undefined;\n const manualPromise = options\n .onManualCodeInput()\n .then(input => {\n manualCode = input;\n server.cancelWait();\n })\n .catch(err => {\n manualError = err instanceof Error ? err : new Error(String(err));\n server.cancelWait();\n });\n\n const result = await server.waitForCode();\n\n // If manual input was cancelled, throw that error\n if (manualError) {\n throw manualError;\n }\n\n if (result?.code) {\n // Browser callback won\n code = result.code;\n } else if (manualCode) {\n // Manual input won (or callback timed out and user had entered code)\n const parsed = parseAuthorizationInput(manualCode);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n\n // If still no code, wait for manual promise to complete and try that\n if (!code) {\n await manualPromise;\n if (manualError) {\n throw manualError;\n }\n if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n }\n } else {\n // Original flow: wait for callback, then prompt if needed\n const result = await server.waitForCode();\n if (result?.code) {\n code = result.code;\n }\n }\n\n // Fallback to onPrompt if still no code\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code (or full redirect URL):',\n });\n const parsed = parseAuthorizationInput(input);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n\n if (!code) {\n throw new Error('Missing authorization code');\n }\n\n const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri, options.signal);\n if (tokenResult.type !== 'success') {\n throw new Error('Token exchange failed');\n }\n\n const accountId = requireAccountId(tokenResult);\n\n return {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n email: getEmailFromTokens(tokenResult),\n };\n } finally {\n server.close();\n }\n}\n\nexport const __testing = {\n createAuthorizationFlow,\n decodeJwt,\n extractAccountIdFromClaims,\n getAccountId,\n loginOpenAICodexDevice,\n requireAccountId,\n startLocalOAuthServer,\n};\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(\n refreshToken: string,\n previousAccountId?: string,\n previousEmail?: string,\n): Promise<OAuthCredentials> {\n const result = await refreshAccessToken(refreshToken);\n if (result.type !== 'success') {\n throw new Error('Failed to refresh OpenAI Codex token');\n }\n\n const accountId = requireAccountId(result, previousAccountId);\n\n return {\n access: result.access,\n refresh: result.refresh,\n expires: result.expires,\n accountId,\n email: getEmailFromTokens(result) ?? previousEmail,\n };\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n id: 'openai-codex',\n name: 'ChatGPT Plus/Pro (Codex Subscription)',\n usesCallbackServer: true,\n authModes: OPENAI_CODEX_AUTH_MODES,\n\n async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const mode = callbacks.authMode === 'device' || callbacks.authMode === 'browser' ? callbacks.authMode : undefined;\n return loginOpenAICodex({\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n signal: callbacks.signal,\n mode,\n });\n },\n\n async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n return refreshOpenAICodexToken(\n credentials.refresh,\n credentials.accountId as string | undefined,\n credentials.email as string | undefined,\n );\n },\n\n getApiKey(credentials: OAuthCredentials): string {\n return credentials.access;\n },\n\n getAccountLabel(credentials: OAuthCredentials): Promise<string | undefined> {\n const email = credentials.email;\n return Promise.resolve(typeof email === 'string' && email.length > 0 ? email : undefined);\n },\n\n /**\n * The ChatGPT account/workspace id from the token, when present — stable\n * across refreshes and re-authorizations of one subscription.\n */\n getAccountIdentity(credentials: OAuthCredentials): string | undefined {\n const accountId = credentials.accountId;\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : undefined;\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"openai-codex.js","names":[],"sources":["../../../src/auth/providers/openai-codex.ts"],"sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * Inspired by pi-mono's OAuth implementation:\n * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/openai-codex.ts\n *\n * NOTE: This module uses Node.js http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds (web-ui)\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nlet _httpPromise: Promise<typeof import('node:http')> | null = null;\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nlet _http: typeof import('node:http') | null = null;\ntype HttpServer = {\n off: (event: 'error' | 'listening', listener: (...args: any[]) => void) => HttpServer;\n once: (event: 'error' | 'listening', listener: (...args: any[]) => void) => HttpServer;\n listen: (port: number, hostname: string) => HttpServer;\n close: () => void;\n};\nif (typeof process !== 'undefined' && (process.versions?.node || process.versions?.bun)) {\n _httpPromise = import('node:http').then(m => {\n _http = m;\n return m;\n });\n}\n\nimport { parseAuthorizationInput } from '../authorization-input.js';\nimport { generatePKCE } from '../pkce.js';\nimport type { AuthMode, OAuthCredentials, OAuthLoginCallbacks, OAuthPrompt, OAuthProviderInterface } from '../types.js';\n\nexport const OPENAI_CODEX_AUTH_MODES: ReadonlyArray<AuthMode> = [\n {\n id: 'browser',\n name: 'Browser (local callback)',\n description: 'Opens the browser and waits for the OAuth callback on localhost.',\n },\n {\n id: 'device',\n name: 'Device code (headless)',\n description: 'Shows a code to enter at openai.com — for SSH, remote, or no-browser environments.',\n },\n];\n\nconst CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';\nconst ISSUER = 'https://auth.openai.com';\nconst AUTHORIZE_URL = `${ISSUER}/oauth/authorize`;\nconst TOKEN_URL = `${ISSUER}/oauth/token`;\nconst DEVICE_USER_CODE_URL = `${ISSUER}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${ISSUER}/api/accounts/deviceauth/token`;\nconst DEVICE_AUTHORIZE_URL = `${ISSUER}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${ISSUER}/deviceauth/callback`;\nconst DEFAULT_CALLBACK_PORT = 1455;\nconst FALLBACK_CALLBACK_PORT = 1457;\nconst DEFAULT_TOKEN_EXPIRES_IN_SECONDS = 3600;\nconst DEVICE_AUTH_TIMEOUT_MS = 15 * 60 * 1000;\nconst OAUTH_REQUEST_TIMEOUT_MS = 15_000;\nconst SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke';\nconst JWT_CLAIM_PATH = 'https://api.openai.com/auth';\n\nconst SUCCESS_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Authentication successful</title>\n</head>\n<body>\n <p>Authentication successful. Return to your terminal to continue.</p>\n</body>\n</html>`;\n\ntype TokenSuccess = {\n type: 'success';\n access: string;\n refresh: string;\n expires: number;\n idToken?: string;\n};\ntype TokenFailure = { type: 'failed' };\ntype TokenResult = TokenSuccess | TokenFailure;\n\ntype JwtPayload = {\n chatgpt_account_id?: string;\n email?: string;\n [JWT_CLAIM_PATH]?: {\n chatgpt_account_id?: string;\n email?: string;\n };\n [key: string]: unknown;\n};\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n const timeout = AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS);\n return signal ? AbortSignal.any([timeout, signal]) : timeout;\n}\n\nasync function createState(): Promise<string> {\n if (typeof process === 'undefined' || !(process.versions?.node || process.versions?.bun)) {\n throw new Error('OpenAI Codex OAuth is only available in Node.js environments');\n }\n return Buffer.from(globalThis.crypto.getRandomValues(new Uint8Array(16))).toString('hex');\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const payload = parts[1] ?? '';\n const padded = payload\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(Math.ceil(payload.length / 4) * 4, '=');\n const decoded = atob(padded);\n return JSON.parse(decoded) as JwtPayload;\n } catch {\n return null;\n }\n}\n\nfunction extractAccountIdFromClaims(payload: JwtPayload | null | undefined): string | null {\n if (!payload) return null;\n const accountId = payload.chatgpt_account_id ?? payload[JWT_CLAIM_PATH]?.chatgpt_account_id;\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : null;\n}\n\nfunction getAccountId(tokens: { idToken?: string; access: string }, fallback?: string): string | undefined {\n const fromIdToken = tokens.idToken ? extractAccountIdFromClaims(decodeJwt(tokens.idToken)) : null;\n if (fromIdToken) return fromIdToken;\n\n const fromAccessToken = extractAccountIdFromClaims(decodeJwt(tokens.access));\n if (fromAccessToken) return fromAccessToken;\n\n return fallback;\n}\n\nfunction requireAccountId(tokens: { idToken?: string; access: string }, fallback?: string): string {\n const accountId = getAccountId(tokens, fallback);\n if (!accountId) {\n throw new Error('Failed to extract ChatGPT account id from OpenAI Codex token');\n }\n return accountId;\n}\n\nfunction extractEmailFromClaims(payload: JwtPayload): string | null {\n const email = payload.email ?? payload[JWT_CLAIM_PATH]?.email;\n return typeof email === 'string' && email.length > 0 ? email : null;\n}\n\nfunction getEmailFromTokens(tokens: { idToken?: string; access: string }): string | undefined {\n const fromIdToken = tokens.idToken ? extractEmailFromClaims(decodeJwt(tokens.idToken) ?? {}) : null;\n if (fromIdToken) return fromIdToken;\n const fromAccessToken = extractEmailFromClaims(decodeJwt(tokens.access) ?? {});\n return fromAccessToken ?? undefined;\n}\n\ntype TokenResponseJson = {\n id_token?: string;\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n};\n\nfunction tokenResponseToResult(json: TokenResponseJson, logPrefix: string): TokenResult {\n if (!json.access_token || !json.refresh_token) {\n console.error(\n `[openai-codex] ${logPrefix} response missing required fields (access_token=${Boolean(json.access_token)}, refresh_token=${Boolean(json.refresh_token)})`,\n );\n return { type: 'failed' };\n }\n\n return {\n type: 'success',\n access: json.access_token,\n refresh: json.refresh_token,\n expires: Date.now() + (json.expires_in ?? DEFAULT_TOKEN_EXPIRES_IN_SECONDS) * 1000,\n idToken: json.id_token,\n };\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n verifier: string,\n redirectUri: string,\n signal?: AbortSignal,\n): Promise<TokenResult> {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n signal: requestSignal(signal),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri,\n }),\n });\n\n if (!response.ok) {\n console.error('[openai-codex] code->token failed:', response.status);\n return { type: 'failed' };\n }\n\n return tokenResponseToResult((await response.json()) as TokenResponseJson, 'token');\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResult> {\n try {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n signal: requestSignal(),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: CLIENT_ID,\n }),\n });\n\n if (!response.ok) {\n console.error('[openai-codex] Token refresh failed:', response.status);\n return { type: 'failed' };\n }\n\n return tokenResponseToResult((await response.json()) as TokenResponseJson, 'Token refresh');\n } catch {\n console.error('[openai-codex] Token refresh request failed');\n return { type: 'failed' };\n }\n}\n\nasync function createAuthorizationFlow(\n redirectUri: string,\n state: string,\n originator: string = 'mastracode',\n): Promise<{ verifier: string; url: string }> {\n const { verifier, challenge } = await generatePKCE();\n\n const url = new URL(AUTHORIZE_URL);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', CLIENT_ID);\n url.searchParams.set('redirect_uri', redirectUri);\n url.searchParams.set('scope', SCOPE);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('state', state);\n url.searchParams.set('id_token_add_organizations', 'true');\n url.searchParams.set('codex_cli_simplified_flow', 'true');\n url.searchParams.set('originator', originator);\n\n return { verifier, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n redirectUri: string;\n warning?: string;\n close: () => void;\n cancelWait: () => void;\n waitForCode: () => Promise<{ code: string } | null>;\n};\n\ntype CallbackPorts = {\n defaultPort: number;\n fallbackPort: number;\n};\n\nconst CODEX_CALLBACK_PORTS: CallbackPorts = {\n defaultPort: DEFAULT_CALLBACK_PORT,\n fallbackPort: FALLBACK_CALLBACK_PORT,\n};\n\nasync function requestCancel(port: number): Promise<void> {\n try {\n await fetch(`http://127.0.0.1:${port}/cancel`, { signal: AbortSignal.timeout(200) });\n } catch {\n // The existing listener might not be a Codex auth server.\n }\n}\n\nfunction listen(server: HttpServer, port: number): Promise<boolean> {\n return new Promise(resolve => {\n const onError = () => {\n server.off('listening', onListening);\n resolve(false);\n };\n const onListening = () => {\n server.off('error', onError);\n resolve(true);\n };\n\n server.once('error', onError);\n server.once('listening', onListening);\n server.listen(port, '127.0.0.1');\n });\n}\n\nasync function bindOAuthServer(server: HttpServer, ports: CallbackPorts): Promise<number | null> {\n await requestCancel(ports.defaultPort);\n if (await listen(server, ports.defaultPort)) return ports.defaultPort;\n if (await listen(server, ports.fallbackPort)) return ports.fallbackPort;\n\n return null;\n}\n\nasync function getHttpModule() {\n if (!_http && _httpPromise) {\n _http = await _httpPromise;\n }\n if (!_http) {\n throw new Error('OpenAI Codex OAuth is only available in Node.js environments');\n }\n return _http;\n}\n\nasync function startLocalOAuthServer(\n state: string,\n ports: CallbackPorts = CODEX_CALLBACK_PORTS,\n): Promise<OAuthServerInfo> {\n const http = await getHttpModule();\n let lastCode: string | null = null;\n let cancelled = false;\n const server = http.createServer((req, res) => {\n try {\n const url = new URL(req.url || '', 'http://localhost');\n if (url.pathname === '/cancel') {\n cancelled = true;\n res.statusCode = 200;\n res.end('Cancelled');\n return;\n }\n if (url.pathname !== '/auth/callback') {\n res.statusCode = 404;\n res.end('Not found');\n return;\n }\n if (url.searchParams.get('state') !== state) {\n res.statusCode = 400;\n res.end('State mismatch');\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n res.statusCode = 400;\n res.end('Missing authorization code');\n return;\n }\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(SUCCESS_HTML);\n lastCode = code;\n } catch {\n res.statusCode = 500;\n res.end('Internal error');\n }\n });\n\n return new Promise(resolve => {\n bindOAuthServer(server, ports).then(port => {\n if (!port) {\n resolve({\n redirectUri: `http://localhost:${ports.fallbackPort}/auth/callback`,\n warning: `OpenAI Codex OAuth requires localhost port ${ports.defaultPort} or ${ports.fallbackPort}, but both are in use. Automatic browser callback will not work until one is freed.`,\n close: () => {\n try {\n server.close();\n } catch {\n // ignore\n }\n },\n cancelWait: () => {},\n waitForCode: async () => null,\n });\n return;\n }\n\n resolve({\n redirectUri: `http://localhost:${port}/auth/callback`,\n close: () => server.close(),\n cancelWait: () => {\n cancelled = true;\n },\n waitForCode: async () => {\n const sleep = () => new Promise(r => setTimeout(r, 100));\n for (let i = 0; i < 600; i += 1) {\n if (lastCode) return { code: lastCode };\n if (cancelled) return null;\n await sleep();\n }\n return null;\n },\n });\n });\n });\n}\n\n/**\n * Serializable pending state for a Codex device-code login. Safe to persist\n * (e.g. a `pending jsonb` column) so polling can span HTTP requests — the\n * device token response carries the `code_verifier`, so no PKCE state needs\n * to be kept client-side.\n */\nexport interface CodexDeviceLoginPending {\n deviceAuthId: string;\n userCode: string;\n /** Verification URL for the user to open. */\n url: string;\n instructions: string;\n /** Poll interval in ms suggested by the server. */\n intervalMs: number;\n /** ms epoch after which the device authorization expires. */\n deadlineAt: number;\n}\n\nexport type CodexDevicePollResult =\n | { status: 'complete'; credentials: OAuthCredentials }\n | { status: 'pending'; nextPollMs: number }\n | { status: 'failed'; error: string };\n\n/**\n * Start a Codex device-code login: request a user code and return the\n * serializable pending state for subsequent polls.\n */\nexport async function startCodexDeviceLogin(options?: { signal?: AbortSignal }): Promise<CodexDeviceLoginPending> {\n const response = await fetch(DEVICE_USER_CODE_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'mastracode',\n },\n body: JSON.stringify({ client_id: CLIENT_ID, originator: 'mastracode' }),\n signal: requestSignal(options?.signal),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to initiate OpenAI Codex device authorization: ${response.status}`);\n }\n\n const deviceData = (await response.json()) as {\n device_auth_id?: string;\n user_code?: string;\n usercode?: string;\n interval?: string | number;\n };\n\n const userCode = deviceData.user_code ?? deviceData.usercode;\n\n if (!deviceData.device_auth_id || !userCode) {\n throw new Error('OpenAI Codex device authorization response missing required fields');\n }\n\n const intervalSeconds =\n typeof deviceData.interval === 'number' ? deviceData.interval : Number.parseInt(deviceData.interval ?? '', 10) || 5;\n\n return {\n deviceAuthId: deviceData.device_auth_id,\n userCode,\n url: DEVICE_AUTHORIZE_URL,\n instructions: `Enter code: ${userCode}`,\n intervalMs: Math.max(intervalSeconds, 1) * 1000,\n deadlineAt: Date.now() + DEVICE_AUTH_TIMEOUT_MS,\n };\n}\n\n/**\n * Perform exactly one upstream poll for a pending Codex device login.\n * The Codex device endpoint signals \"still pending\" via HTTP 403/404 (it is\n * not an RFC 8628 error-JSON endpoint); on success it returns the\n * authorization code plus server-held PKCE verifier, which we exchange\n * immediately for credentials. Never throws for flow-level conditions.\n */\nexport async function pollCodexDeviceLogin(\n pending: CodexDeviceLoginPending,\n options?: { signal?: AbortSignal },\n): Promise<CodexDevicePollResult> {\n if (Date.now() >= pending.deadlineAt) {\n return { status: 'failed', error: 'OpenAI Codex device authorization timed out after 15 minutes' };\n }\n\n const pollResponse = await fetch(DEVICE_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'mastracode',\n },\n body: JSON.stringify({\n device_auth_id: pending.deviceAuthId,\n user_code: pending.userCode,\n }),\n signal: requestSignal(options?.signal),\n });\n\n if (pollResponse.ok) {\n const data = (await pollResponse.json()) as {\n authorization_code?: string;\n code_verifier?: string;\n };\n\n if (!data.authorization_code || !data.code_verifier) {\n return { status: 'failed', error: 'OpenAI Codex device token response missing required fields' };\n }\n\n const tokenResult = await exchangeAuthorizationCode(\n data.authorization_code,\n data.code_verifier,\n DEVICE_REDIRECT_URI,\n options?.signal,\n );\n if (tokenResult.type !== 'success') {\n return { status: 'failed', error: 'Token exchange failed' };\n }\n\n let accountId: string;\n try {\n accountId = requireAccountId(tokenResult);\n } catch (error) {\n return { status: 'failed', error: error instanceof Error ? error.message : String(error) };\n }\n\n return {\n status: 'complete',\n credentials: {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n email: getEmailFromTokens(tokenResult),\n },\n };\n }\n\n if (pollResponse.status !== 403 && pollResponse.status !== 404) {\n return {\n status: 'failed',\n error: `OpenAI Codex device authorization failed: ${pollResponse.status}`,\n };\n }\n\n return { status: 'pending', nextPollMs: pending.intervalMs };\n}\n\nasync function loginOpenAICodexDevice(options: {\n onAuth: (info: { url: string; instructions?: string }) => void;\n onProgress?: (message: string) => void;\n signal?: AbortSignal;\n sleep?: (ms: number) => Promise<void>;\n}): Promise<OAuthCredentials> {\n const pending = await startCodexDeviceLogin({ signal: options.signal });\n const sleep = options.sleep ?? (ms => new Promise<void>(resolve => setTimeout(resolve, ms)));\n\n options.onAuth({\n url: pending.url,\n instructions: pending.instructions,\n });\n\n await sleep(pending.intervalMs);\n\n while (true) {\n if (options.signal?.aborted) {\n throw new Error('Login cancelled');\n }\n\n const result = await pollCodexDeviceLogin(pending, { signal: options.signal });\n if (result.status === 'complete') {\n return result.credentials;\n }\n if (result.status === 'failed') {\n throw new Error(result.error);\n }\n\n options.onProgress?.('Waiting for OpenAI Codex device authorization...');\n await sleep(result.nextPollMs);\n }\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"mastracode\")\n */\nexport async function loginOpenAICodex(options: {\n onAuth: (info: { url: string; instructions?: string }) => void;\n onPrompt: (prompt: OAuthPrompt) => Promise<string>;\n onProgress?: (message: string) => void;\n onManualCodeInput?: () => Promise<string>;\n signal?: AbortSignal;\n originator?: string;\n mode?: 'browser' | 'device';\n}): Promise<OAuthCredentials> {\n const envMode =\n typeof process !== 'undefined' && process.env?.MASTRACODE_OPENAI_CODEX_AUTH_MODE === 'device'\n ? 'device'\n : undefined;\n const mode = options.mode ?? envMode ?? 'browser';\n if (mode === 'device') {\n return loginOpenAICodexDevice({\n onAuth: options.onAuth,\n onProgress: options.onProgress,\n signal: options.signal,\n });\n }\n\n const state = await createState();\n const server = await startLocalOAuthServer(state);\n if (server.warning) {\n options.onProgress?.(server.warning);\n }\n const { verifier, url } = await createAuthorizationFlow(\n server.redirectUri,\n state,\n options.originator ?? 'mastracode',\n );\n\n options.onAuth({\n url,\n instructions: server.warning\n ? `${server.warning} You can still paste the authorization code or full redirect URL manually.`\n : 'A browser window should open. Complete login to finish.',\n });\n\n let code: string | undefined;\n try {\n if (options.onManualCodeInput) {\n // Race between browser callback and manual input\n let manualCode: string | undefined;\n let manualError: Error | undefined;\n const manualPromise = options\n .onManualCodeInput()\n .then(input => {\n manualCode = input;\n server.cancelWait();\n })\n .catch(err => {\n manualError = err instanceof Error ? err : new Error(String(err));\n server.cancelWait();\n });\n\n const result = await server.waitForCode();\n\n // If manual input was cancelled, throw that error\n if (manualError) {\n throw manualError;\n }\n\n if (result?.code) {\n // Browser callback won\n code = result.code;\n } else if (manualCode) {\n // Manual input won (or callback timed out and user had entered code)\n const parsed = parseAuthorizationInput(manualCode);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n\n // If still no code, wait for manual promise to complete and try that\n if (!code) {\n await manualPromise;\n if (manualError) {\n throw manualError;\n }\n if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n }\n } else {\n // Original flow: wait for callback, then prompt if needed\n const result = await server.waitForCode();\n if (result?.code) {\n code = result.code;\n }\n }\n\n // Fallback to onPrompt if still no code\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code (or full redirect URL):',\n });\n const parsed = parseAuthorizationInput(input);\n if (parsed.state && parsed.state !== state) {\n throw new Error('State mismatch');\n }\n code = parsed.code;\n }\n\n if (!code) {\n throw new Error('Missing authorization code');\n }\n\n const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri, options.signal);\n if (tokenResult.type !== 'success') {\n throw new Error('Token exchange failed');\n }\n\n const accountId = requireAccountId(tokenResult);\n\n return {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n email: getEmailFromTokens(tokenResult),\n };\n } finally {\n server.close();\n }\n}\n\nexport const __testing = {\n createAuthorizationFlow,\n decodeJwt,\n extractAccountIdFromClaims,\n getAccountId,\n loginOpenAICodexDevice,\n requireAccountId,\n startLocalOAuthServer,\n};\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(\n refreshToken: string,\n previousAccountId?: string,\n previousEmail?: string,\n): Promise<OAuthCredentials> {\n const result = await refreshAccessToken(refreshToken);\n if (result.type !== 'success') {\n throw new Error('Failed to refresh OpenAI Codex token');\n }\n\n const accountId = requireAccountId(result, previousAccountId);\n\n return {\n access: result.access,\n refresh: result.refresh,\n expires: result.expires,\n accountId,\n email: getEmailFromTokens(result) ?? previousEmail,\n };\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n id: 'openai-codex',\n name: 'ChatGPT Plus/Pro (Codex Subscription)',\n usesCallbackServer: true,\n authModes: OPENAI_CODEX_AUTH_MODES,\n\n async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const mode = callbacks.authMode === 'device' || callbacks.authMode === 'browser' ? callbacks.authMode : undefined;\n return loginOpenAICodex({\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n signal: callbacks.signal,\n mode,\n });\n },\n\n async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n return refreshOpenAICodexToken(\n credentials.refresh,\n credentials.accountId as string | undefined,\n credentials.email as string | undefined,\n );\n },\n\n getApiKey(credentials: OAuthCredentials): string {\n return credentials.access;\n },\n\n getAccountLabel(credentials: OAuthCredentials): Promise<string | undefined> {\n const email = credentials.email;\n return Promise.resolve(typeof email === 'string' && email.length > 0 ? email : undefined);\n },\n\n /**\n * The ChatGPT account/workspace id from the token, when present — stable\n * across refreshes and re-authorizations of one subscription.\n */\n getAccountIdentity(credentials: OAuthCredentials): string | undefined {\n const accountId = credentials.accountId;\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : undefined;\n },\n};\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { openaiCodexOAuthProvider } from "./providers/openai-codex.js";
6
6
  import { xaiOAuthProvider } from "./providers/xai.js";
7
7
  import { dirname, join } from "path";
8
8
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
9
- import { createHash, randomUUID } from "crypto";
9
+ import { createHash } from "crypto";
10
10
  //#region src/auth/storage.ts
11
11
  /**
12
12
  * Credential storage for API keys and OAuth tokens.
@@ -53,7 +53,7 @@ function getOAuthProviders() {
53
53
  * then failed to match its own entry and registered a second one for it.
54
54
  */
55
55
  function mintAccountId(providerId) {
56
- return `${providerId}:${randomUUID()}`;
56
+ return `${providerId}:${globalThis.crypto.randomUUID()}`;
57
57
  }
58
58
  /**
59
59
  * The pre-A13 id scheme: `${providerId}:${sha256(refresh).slice(0,8)}`.
@@ -230,7 +230,7 @@ var AuthStorage = class {
230
230
  recursive: true,
231
231
  mode: 448
232
232
  });
233
- const tempPath = `${this.authPath}.${process.pid}.${randomUUID()}.tmp`;
233
+ const tempPath = `${this.authPath}.${process.pid}.${globalThis.crypto.randomUUID()}.tmp`;
234
234
  try {
235
235
  writeFileSync(tempPath, JSON.stringify(this.data, null, 2), {
236
236
  encoding: "utf-8",
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","names":[],"sources":["../../src/auth/storage.ts"],"sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppDataDir } from '../utils/project.js';\nimport { anthropicOAuthProvider } from './providers/anthropic.js';\nimport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nimport { kimiCodingOAuthProvider } from './providers/kimi-coding.js';\nimport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\nimport { xaiOAuthProvider } from './providers/xai.js';\nimport type {\n AuthCredential,\n AuthStorageData,\n OAuthAccountRecord,\n OAuthCredential,\n OAuthCredentialSnapshot,\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderId,\n OAuthProviderInterface,\n} from './types.js';\n\n/**\n * Best/default models for each OAuth provider.\n * Used when auto-selecting a model after login.\n */\nexport const PROVIDER_DEFAULT_MODELS: Record<OAuthProviderId, string> = {\n anthropic: 'anthropic/claude-fable-5',\n 'openai-codex': 'openai/gpt-5.6-sol',\n // gpt-4.1 routes through `/chat/completions` (which our OpenAI-compatible\n // adapter handles); Anthropic-shaped Copilot models (Claude on `/v1/messages`)\n // are not yet wired up, so picking one as the post-login default would error.\n 'github-copilot': 'github-copilot/gpt-4.1',\n 'kimi-for-coding': 'kimi-for-coding/kimi-for-coding',\n xai: 'xai/grok-4.5',\n};\n\n// Provider registry\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>([\n [anthropicOAuthProvider.id, anthropicOAuthProvider],\n [openaiCodexOAuthProvider.id, openaiCodexOAuthProvider],\n [githubCopilotOAuthProvider.id, githubCopilotOAuthProvider],\n [kimiCodingOAuthProvider.id, kimiCodingOAuthProvider],\n [xaiOAuthProvider.id, xaiOAuthProvider],\n]);\n\n/**\n * Get an OAuth provider by ID\n */\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n return oauthProviderRegistry.get(id);\n}\n\n/**\n * Get all registered OAuth providers\n */\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n return Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * Mint an account instance id: `${providerId}:${randomUUID()}`.\n *\n * Assigned once, when the account is created, and never derived from\n * credentials. Deriving it from the refresh token (the pre-A13 scheme) looked\n * stable but was not: `persistRefreshedCredential` keeps an entry's key while\n * writing rotated tokens onto it, so every refresh left the id describing a\n * credential the account no longer had — and re-adding that same subscription\n * then failed to match its own entry and registered a second one for it.\n */\nfunction mintAccountId(providerId: string): string {\n return `${providerId}:${randomUUID()}`;\n}\n\n/**\n * The pre-A13 id scheme: `${providerId}:${sha256(refresh).slice(0,8)}`.\n *\n * No longer used to assign ids, but still needed to recognize registries\n * written before A13 — both to match a re-authorized account onto its existing\n * entry, and to recognize entries whose fields the A13 migration must backfill.\n */\nfunction legacyAccountIdFor(providerId: string, refreshToken: string): string {\n return `${providerId}:${createHash('sha256').update(refreshToken).digest('hex').slice(0, 8)}`;\n}\n\n/**\n * Stable identity for the *subscription* behind an account, when the provider\n * exposes one (an account id, an email — anything that survives a refresh and\n * a re-authorization). Used to decide whether an added account is one we\n * already have. Providers whose credentials carry no such identifier return\n * undefined; for those, a re-authorization is indistinguishable from a new\n * account without spending a request.\n */\nfunction providerIdentity(provider: OAuthProviderInterface | undefined, creds: OAuthCredentials): string | undefined {\n try {\n const identity = provider?.getAccountIdentity?.(creds);\n return typeof identity === 'string' && identity.length > 0 ? identity : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isOAuthAccountRecord(value: unknown): value is OAuthAccountRecord {\n if (!value || typeof value !== 'object') return false;\n const v = value as Partial<OAuthAccountRecord>;\n return (\n v.type === 'oauth-account' &&\n typeof v.id === 'string' &&\n typeof v.label === 'string' &&\n typeof v.addedAt === 'string' &&\n typeof v.active === 'boolean' &&\n typeof v.refresh === 'string' &&\n typeof v.access === 'string' &&\n typeof v.expires === 'number'\n );\n}\n\n/** The credential fields of an account record — everything but registry identity. */\nfunction credentialFieldsOf(record: OAuthAccountRecord): OAuthCredentials {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n identity: _identity,\n ...creds\n } = record;\n return creds;\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n private data: AuthStorageData = {};\n private refreshPromises = new Map<string, Promise<OAuthCredentials | undefined>>();\n\n constructor(private authPath: string = join(getAppDataDir(), 'auth.json')) {\n this.reload();\n }\n\n /**\n * Reload credentials from disk.\n */\n reload(): void {\n if (!existsSync(this.authPath)) {\n this.data = {};\n return;\n }\n try {\n this.data = JSON.parse(readFileSync(this.authPath, 'utf-8'));\n } catch {\n this.data = {};\n }\n this.migrate();\n }\n\n /**\n * Bring auth.json up to the multi-account registry format. Runs on every\n * load; saves only when something actually changed.\n *\n * 1. A legacy OAuth slot with no registry gets one adopted active entry\n * (the slot's tokens stay put — the entry mirrors them).\n * 2. When the slot's refresh token differs from the active entry's, an\n * external writer (older build, another worktree) owns the slot — the\n * slot wins and its tokens are adopted onto the active entry.\n * 3. A registry with no active entry (hand-edited) self-heals to its first\n * entry; malformed `accounts:` values are skipped, never fatal.\n * 4. A registry whose legacy slot disappeared (partial write, an external\n * hand-edit) self-heals by mirroring the active entry's tokens back into\n * the slot, so `isLoggedIn()`/`getOAuthCredential()` keep agreeing with\n * `listAccounts()`. `remove()`/`logout()` clear the registry with the\n * slot, so they never leave the shape this heals.\n */\n private migrate(): void {\n let changed = false;\n for (const key of Object.keys(this.data)) {\n const slot = this.data[key];\n if (!slot || slot.type !== 'oauth') continue;\n const providerId = key;\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) {\n this.adoptSlot(providerId, slot);\n changed = true;\n continue;\n }\n const active = entries.find(entry => entry.active);\n if (!active) {\n this.data[this.accountKeyFor(entries[0]!.id)] = { ...entries[0]!, active: true };\n changed = true;\n continue;\n }\n if (active.refresh !== slot.refresh) {\n // An external writer (older build, another worktree) owns the slot.\n // If its tokens belong to a different registered account, that\n // account is now the active one — re-point activation to it instead\n // of cloning the refresh token onto the current active entry.\n const matching = entries.find(entry => entry.id !== active.id && entry.refresh === slot.refresh);\n if (matching) {\n const { type: _t, ...slotCreds } = slot;\n this.data[this.accountKeyFor(matching.id)] = {\n ...matching,\n ...slotCreds,\n type: 'oauth-account',\n active: true,\n };\n this.data[this.accountKeyFor(active.id)] = { ...active, active: false };\n changed = true;\n } else {\n const { type: _type, ...slotCreds } = slot;\n this.data[this.accountKeyFor(active.id)] = { ...active, ...slotCreds, type: 'oauth-account' };\n changed = true;\n }\n }\n }\n\n // Case 4: registry entries without a legacy slot (the slot loop above\n // only ever sees providers that still have one).\n const registeredProviders = new Set<string>();\n for (const key of Object.keys(this.data)) {\n if (!key.startsWith('accounts:')) continue;\n const providerId = key.slice('accounts:'.length).split(':')[0];\n if (providerId) registeredProviders.add(providerId);\n }\n for (const providerId of registeredProviders) {\n if (this.data[providerId]?.type === 'oauth') continue;\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) continue;\n let active = entries.find(entry => entry.active);\n if (!active) {\n active = entries[0]!;\n this.data[this.accountKeyFor(active.id)] = { ...active, active: true };\n }\n this.data[providerId] = { type: 'oauth', ...credentialFieldsOf(active) };\n changed = true;\n }\n\n // Case 5 (A13): backfill the stable account identity on entries that\n // predate it, so adding a subscription we already hold updates its entry\n // instead of registering a second one for it.\n //\n // Ids are deliberately left exactly as they are. Ids are minted once and\n // derive from nothing (A13), so there is nothing to repair — and re-keying\n // a live registry would invalidate every id persisted outside this file\n // (settings routing preferences, per-thread routing state) for no gain.\n for (const providerId of registeredProviders) {\n const provider = getOAuthProvider(providerId as OAuthProviderId);\n if (!provider?.getAccountIdentity) continue;\n for (const entry of this.accountEntries(providerId)) {\n if (entry.identity) continue;\n const identity = providerIdentity(provider, entry);\n if (!identity) continue;\n this.data[this.accountKeyFor(entry.id)] = { ...entry, identity };\n changed = true;\n }\n }\n\n if (changed) this.save();\n }\n\n /**\n * Save credentials to disk.\n */\n private save(): void {\n const dir = dirname(this.authPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n // Write to a sibling file, then atomically replace auth.json. Provider\n // fetch wrappers reload this file on every request, including in older\n // Mastra Code processes; writing in place lets them observe truncated JSON\n // and temporarily treat every provider as logged out.\n const tempPath = `${this.authPath}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writeFileSync(tempPath, JSON.stringify(this.data, null, 2), { encoding: 'utf-8', mode: 0o600 });\n chmodSync(tempPath, 0o600);\n renameSync(tempPath, this.authPath);\n } finally {\n rmSync(tempPath, { force: true });\n }\n }\n\n /**\n * Get credential for a provider. Registry entries (type 'oauth-account')\n * live under `accounts:` keys and never surface here.\n */\n get(provider: string): AuthCredential | undefined {\n const cred = this.data[provider];\n return cred !== undefined && cred.type !== 'oauth-account' ? cred : undefined;\n }\n\n /**\n * Set credential for a provider.\n */\n set(provider: string, credential: AuthCredential): void {\n this.reload();\n this.data[provider] = credential;\n this.save();\n }\n\n /**\n * Remove credential for a provider, including its account registry.\n * Clearing only the slot would leave the registry behind, and the next\n * load's migration would heal the slot back from it — resurrecting the\n * provider the caller just signed out.\n */\n remove(provider: string): void {\n this.reload();\n delete this.data[provider];\n const prefix = this.accountPrefixFor(provider);\n for (const key of Object.keys(this.data)) {\n if (key.startsWith(prefix)) delete this.data[key];\n }\n this.save();\n }\n\n /**\n * List all providers with credentials.\n */\n list(): string[] {\n return Object.keys(this.data);\n }\n\n /**\n * Check if credentials exist for a provider.\n */\n has(provider: string): boolean {\n return provider in this.data;\n }\n\n /**\n * Check if logged in via OAuth for a provider.\n */\n isLoggedIn(provider: string): boolean {\n const cred = this.data[provider];\n return cred?.type === 'oauth';\n }\n\n /**\n * Check if a stored API key exists for a provider.\n * Keys are stored under `apikey:<provider>` in auth.json.\n */\n hasStoredApiKey(provider: string): boolean {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0;\n }\n\n /**\n * Get a stored API key for a provider, if any.\n */\n getStoredApiKey(provider: string): string | undefined {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0 ? cred.key : undefined;\n }\n\n /**\n * Store an API key for a provider.\n * Also sets the corresponding environment variable so model resolution can find it.\n */\n setStoredApiKey(provider: string, key: string, envVar?: string): void {\n this.set(`apikey:${provider}`, { type: 'api_key', key });\n if (envVar) {\n process.env[envVar] = key;\n }\n }\n\n /**\n * Load all stored API keys into process.env.\n * Called at startup so model resolution can find stored keys.\n * Only sets env vars that aren't already set (env vars take precedence).\n */\n loadStoredApiKeysIntoEnv(providerEnvVars: Record<string, string | undefined>): void {\n for (const [key, cred] of Object.entries(this.data)) {\n if (!key.startsWith('apikey:') || cred.type !== 'api_key' || !cred.key) continue;\n const provider = key.substring('apikey:'.length);\n const envVar = providerEnvVars[provider];\n if (envVar && !process.env[envVar]) {\n process.env[envVar] = cred.key;\n }\n }\n }\n\n /**\n * Login to an OAuth provider. Resolves to the registered account record.\n * Pass `activate: false` to add the account without making it active (the\n * first account for a provider is always activated — a provider with\n * accounts must have an active one).\n */\n async login(\n providerId: OAuthProviderId,\n callbacks: OAuthLoginCallbacks,\n opts?: { replaceAccountId?: string; activate?: boolean },\n ): Promise<OAuthAccountRecord> {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown OAuth provider: ${providerId}`);\n }\n\n const credentials = await provider.login(callbacks);\n // Route through the account registry: the new account is appended (or\n // updated in place — by id collision or an explicit replaceAccountId for\n // re-authentication) and becomes the active account unless activate:false.\n return this.addAccount(providerId, credentials, opts);\n }\n\n /**\n * Logout from a provider: remove the legacy slot and every registered account.\n */\n logout(provider: string): void {\n this.remove(provider);\n }\n\n // ---------------------------------------------------------------------------\n // Multi-account registry\n // ---------------------------------------------------------------------------\n\n private accountPrefixFor(providerId: string): string {\n return `accounts:${providerId}:`;\n }\n\n private accountKeyFor(id: string): string {\n return `accounts:${id}`;\n }\n\n /** Registry entries for a provider in JSON insertion order (malformed values skipped). */\n private accountEntries(providerId: string): OAuthAccountRecord[] {\n const prefix = this.accountPrefixFor(providerId);\n const out: OAuthAccountRecord[] = [];\n for (const [key, value] of Object.entries(this.data)) {\n if (key.startsWith(prefix) && isOAuthAccountRecord(value)) out.push(value);\n }\n return out;\n }\n\n /** Adopt a legacy slot credential as the registry's first active entry. */\n private adoptSlot(providerId: string, slot: OAuthCredential): OAuthAccountRecord {\n const id = mintAccountId(providerId);\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, slot);\n const record: OAuthAccountRecord = {\n ...slot,\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n label: `${provider?.name ?? providerId} account 1`,\n addedAt: new Date().toISOString(),\n active: true,\n };\n this.data[this.accountKeyFor(id)] = record;\n return record;\n }\n\n /**\n * Registered OAuth accounts for a provider, in insertion order. Copies —\n * callers cannot corrupt unsaved storage state by mutating a record.\n */\n listAccounts(providerId: string): OAuthAccountRecord[] {\n return this.accountEntries(providerId).map(entry => ({ ...entry }));\n }\n\n /**\n * The provider's active registry entry, if a registry exists. A copy.\n */\n getActiveAccount(providerId: string): OAuthAccountRecord | undefined {\n const active = this.accountEntries(providerId).find(entry => entry.active);\n return active ? { ...active } : undefined;\n }\n\n /**\n * Register OAuth credentials as an account for a provider, making it active\n * unless `activate: false` is passed.\n *\n * With `replaceAccountId` (re-authentication of a picked account), the\n * target entry's tokens are replaced in place — id re-keyed to the new\n * refresh-token hash, label/position/addedAt preserved — because providers\n * rotate refresh tokens per authorization, so the picked account's old id\n * never matches the new token hash. Re-authentication keeps the target's\n * active state: an inactive account stays inactive and the currently active\n * account keeps the legacy slot. Otherwise, when the new credentials\n * hash to an existing entry's id (same refresh token), that entry is\n * updated in place; a genuinely new account is appended and activated.\n *\n * `activate: false` only applies to the plain add path: the new/updated entry\n * keeps the active state it had, and the previously active account stays\n * active. The first account of a provider is activated regardless.\n */\n async addAccount(\n providerId: string,\n creds: OAuthCredentials,\n opts?: { label?: string; replaceAccountId?: string; activate?: boolean },\n ): Promise<OAuthAccountRecord> {\n this.reload();\n const entries = this.accountEntries(providerId);\n\n if (opts?.replaceAccountId) {\n const target = entries.find(entry => entry.id === opts.replaceAccountId);\n if (!target) {\n throw new Error(`No account ${opts.replaceAccountId} for provider ${providerId}`);\n }\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, creds) ?? target.identity;\n // Re-authentication updates the account in place and **keeps its id**:\n // ids are minted once and never derived from credentials (A13), so the\n // entry's insertion position, the settings routing preference and any\n // thread routing state that name this id all stay valid.\n //\n // A stale entry for the *same* subscription is dropped in favor of the\n // picked account. Same-subscription is a matching stable identity, or —\n // for a registry written before A13, whose ids were the refresh-token\n // hash — the credentials' own legacy id, or an entry already holding\n // exactly these credentials (a re-authorization that returns the same\n // tokens, the only signal an identity-less provider gives). The survivor\n // inherits the collided entry's active state, so re-authenticating an\n // inactive account onto the active account's credentials cannot leave the\n // registry with no active entry.\n const legacyId = legacyAccountIdFor(providerId, creds.refresh);\n const collided = entries.find(\n entry =>\n entry.id !== target.id &&\n ((identity !== undefined && entry.identity === identity) ||\n entry.id === legacyId ||\n entry.refresh === creds.refresh),\n );\n // Credential metadata (device id, enterprise URL, …) follows the tokens:\n // when the fresh response omits a field, the collided entry's value is the\n // coherent fallback, not the target's — pairing fresh tokens with another\n // subscription's stale endpoint would misroute requests. Fresh `creds`\n // win last; only the target's registry metadata (id, label, addedAt)\n // carries over.\n const replacement: OAuthAccountRecord = {\n ...target,\n ...(collided ? credentialFieldsOf(collided) : {}),\n ...creds,\n type: 'oauth-account',\n ...(identity ? { identity } : {}),\n label: opts.label ?? target.label,\n };\n const rebuilt: AuthStorageData = {};\n for (const [key, value] of Object.entries(this.data)) {\n if (key === this.accountKeyFor(target.id)) {\n rebuilt[key] = collided?.active ? { ...replacement, active: true } : replacement;\n } else if (collided && key === this.accountKeyFor(collided.id)) {\n continue;\n } else {\n rebuilt[key] = value;\n }\n }\n this.data = rebuilt;\n // Re-authentication preserves the account's active state: fixing a\n // secondary account's tokens must not hijack the active slot. The target\n // is activated when it was already active, when the entry it collided\n // with was active, or when the provider has no active account at all\n // (first account, or a self-healed gap).\n const wasActive =\n target.active === true ||\n collided?.active === true ||\n entries.some(entry => entry.active && entry.id !== target.id) === false;\n if (wasActive) {\n const activated = this.activateInMemory(providerId, target.id);\n if (!activated) {\n throw new Error(`Failed to activate account ${target.id} for provider ${providerId}`);\n }\n this.save();\n return activated;\n }\n // Inactive target: tokens stay on the registry entry, the legacy slot\n // keeps the currently active account's credential untouched.\n const replacementEntry = this.accountEntries(providerId).find(entry => entry.id === target.id);\n if (!replacementEntry) {\n throw new Error(`Failed to store account ${target.id} for provider ${providerId}`);\n }\n this.save();\n return replacementEntry;\n }\n\n // Resolve the label before the final reload+save: the provider hook may\n // hit the network, and a concurrent write during that await must not be\n // clobbered by a stale snapshot.\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, creds);\n const label = opts?.label ?? (await provider?.getAccountLabel?.(creds)) ?? null;\n this.reload();\n const freshEntries = this.accountEntries(providerId);\n\n // Is this a subscription we already hold? Match the provider's stable\n // account identity where it exposes one, then the pre-A13 id (the\n // refresh-token hash) so a pre-A13 registry still updates its entry rather\n // than gaining a second one for the same subscription, and finally an\n // entry already holding these exact credentials (a re-add of the same\n // account as returned, which is the only signal an identity-less provider\n // such as Anthropic gives us — a provider that cannot name its accounts\n // cannot say \"this is the same subscription, re-authorized\").\n //\n // Crucially this must not re-derive the id of a *minted* entry: after a\n // refresh such an entry's id no longer hashes its current token, and\n // treating that miss as \"new account\" is exactly how re-adding a\n // subscription used to create a duplicate entry for it.\n const legacyId = legacyAccountIdFor(providerId, creds.refresh);\n const existing =\n (identity ? freshEntries.find(entry => entry.identity === identity) : undefined) ??\n freshEntries.find(entry => entry.id === legacyId) ??\n freshEntries.find(entry => entry.refresh === creds.refresh);\n const id = existing?.id ?? mintAccountId(providerId);\n if (existing) {\n this.data[this.accountKeyFor(id)] = {\n ...existing,\n ...creds,\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n active: existing.active,\n };\n } else {\n const resolvedLabel = label ?? `${provider?.name ?? providerId} account ${freshEntries.length + 1}`;\n this.data[this.accountKeyFor(id)] = {\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n label: resolvedLabel,\n addedAt: new Date().toISOString(),\n active: false,\n ...creds,\n };\n }\n\n // activate:false (add-another): keep the current active account. The\n // first account of a provider always activates — a non-empty registry\n // must have an active entry. An id collision with the already-active\n // entry also falls through to activateInMemory so its fresh tokens move\n // into the legacy slot (tokens are single-homed).\n if (opts?.activate === false && freshEntries.length > 0 && !existing?.active) {\n this.save();\n return { ...(this.data[this.accountKeyFor(id)] as OAuthAccountRecord) };\n }\n\n const activated = this.activateInMemory(providerId, id);\n if (!activated) {\n throw new Error(`Failed to activate account ${id} for provider ${providerId}`);\n }\n this.save();\n return activated;\n }\n\n /**\n * Activate an account: move the active tokens out of the legacy slot back\n * into the previously active entry, and the target's tokens into the slot\n * (never copy — one home per token set). With no `instanceId`, rotate to\n * the next entry in insertion order, wrapping once to the front; returns\n * undefined when there is no other entry to rotate to.\n */\n activateAccount(providerId: string, instanceId?: string): OAuthAccountRecord | undefined {\n this.reload();\n const activated = this.activateInMemory(providerId, instanceId);\n if (activated) this.save();\n return activated;\n }\n\n /**\n * Activate an account in memory only — the caller owns reload/save. Move the\n * active tokens out of the legacy slot back into the previously active\n * entry, and the target's tokens into the slot (never copy — one home per\n * token set). With no `instanceId`, rotate to the next entry in insertion\n * order, wrapping once to the front; returns undefined when there is no\n * other entry to rotate to.\n */\n private activateInMemory(providerId: string, instanceId?: string): OAuthAccountRecord | undefined {\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) return undefined;\n\n let target: OAuthAccountRecord | undefined;\n if (instanceId !== undefined) {\n target = entries.find(entry => entry.id === instanceId);\n if (!target) return undefined;\n } else {\n if (entries.length <= 1) return undefined;\n const currentIdx = entries.findIndex(entry => entry.active);\n const candidates =\n currentIdx >= 0 ? [...entries.slice(currentIdx + 1), ...entries.slice(0, currentIdx)] : [...entries];\n target = candidates[0]!;\n }\n if (!target) return undefined;\n\n const slot = this.data[providerId];\n const current = entries.find(entry => entry.active && entry.id !== target.id);\n\n // Move the slot's tokens back onto the previously active entry. Skipped\n // when the target is already active — its (possibly re-authenticated)\n // entry already holds the freshest tokens.\n if (current && slot?.type === 'oauth') {\n const { type: _type, ...slotCreds } = slot;\n this.data[this.accountKeyFor(current.id)] = { ...current, ...slotCreds, type: 'oauth-account', active: false };\n }\n\n // Move the target's tokens into the legacy slot. The slot is rebuilt from\n // the target's own credentials only: carrying fields over from the\n // previously active account would pair this account's tokens with that\n // account's metadata (`deviceId`, `enterpriseUrl`, account id), and the\n // providers read those fields off the slot.\n this.data[providerId] = { ...credentialFieldsOf(target), type: 'oauth' };\n\n // Exactly the target stays active (self-heals multi-active states).\n this.data[this.accountKeyFor(target.id)] = { ...target, active: true };\n for (const entry of entries) {\n if (entry.id === target.id || entry.id === current?.id) continue;\n if (entry.active) {\n this.data[this.accountKeyFor(entry.id)] = { ...entry, active: false };\n }\n }\n\n return { ...target, active: true };\n }\n\n /**\n * Remove an account from the registry. If it was active, the next entry in\n * insertion order is activated; when it was the last one, the legacy slot\n * is removed too (full sign-out for that provider).\n */\n removeAccount(providerId: string, instanceId: string): void {\n this.reload();\n const entries = this.accountEntries(providerId);\n const target = entries.find(entry => entry.id === instanceId);\n if (!target) return;\n\n const wasActive = target.active;\n delete this.data[this.accountKeyFor(instanceId)];\n\n if (wasActive) {\n const idx = entries.indexOf(target);\n const next = entries[idx + 1] ?? entries.find(entry => entry.id !== instanceId);\n if (next) {\n // The removed account's tokens die with it — only move tokens in.\n this.data[providerId] = { type: 'oauth', ...credentialFieldsOf(next) };\n this.data[this.accountKeyFor(next.id)] = { ...next, active: true };\n for (const entry of entries) {\n if (entry.id === next.id || entry.id === instanceId) continue;\n if (entry.active) {\n this.data[this.accountKeyFor(entry.id)] = { ...entry, active: false };\n }\n }\n } else {\n delete this.data[providerId];\n }\n }\n\n this.save();\n }\n\n /**\n * Rename an account's label.\n */\n renameAccount(providerId: string, instanceId: string, label: string): void {\n this.reload();\n const key = this.accountKeyFor(instanceId);\n const entry = this.data[key];\n if (!isOAuthAccountRecord(entry)) return;\n this.data[key] = { ...entry, label };\n this.save();\n }\n\n /**\n * Persist refreshed credentials for the account that initiated the refresh.\n * The legacy slot is updated only if that account is still active when the\n * refresh completes; otherwise a delayed refresh could overwrite a newer\n * activation.\n */\n private persistRefreshedCredential(\n providerId: string,\n instanceId: string | undefined,\n creds: OAuthCredentials,\n ): void {\n this.reload();\n if (instanceId) {\n const key = this.accountKeyFor(instanceId);\n const entry = this.data[key];\n if (!isOAuthAccountRecord(entry)) return;\n this.data[key] = { ...entry, ...creds, type: 'oauth-account' };\n if (entry.active) {\n const slot = this.data[providerId];\n this.data[providerId] =\n slot?.type === 'oauth' ? { ...slot, ...creds, type: 'oauth' } : { type: 'oauth', ...creds };\n }\n } else {\n const slot = this.data[providerId];\n this.data[providerId] =\n slot?.type === 'oauth' ? { ...slot, ...creds, type: 'oauth' } : { type: 'oauth', ...creds };\n }\n this.save();\n }\n\n /**\n * Refresh one account instance through the per-instance dedupe map. Used\n * for sibling refreshes during the rotation walk, so a concurrent\n * `getApiKey` that reloads storage mid-walk (after the candidate was\n * activated but before its refresh resolves) joins the same refresh\n * instead of double-spending a single-use refresh token.\n */\n private async refreshInstance(\n providerId: string,\n instanceId: string,\n creds: OAuthCredentials,\n ): Promise<OAuthCredentials | undefined> {\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n const refreshKey = `${providerId}:${instanceId}`;\n const pending = this.refreshPromises.get(refreshKey);\n if (pending) return pending;\n const refresh = (async () => {\n try {\n const fresh = await provider.refreshToken(creds);\n this.persistRefreshedCredential(providerId, instanceId, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n this.refreshPromises.set(refreshKey, refresh);\n try {\n return await refresh;\n } finally {\n this.refreshPromises.delete(refreshKey);\n }\n }\n\n /**\n * Get a ready-to-use OAuth credential snapshot, auto-refreshing if needed.\n * A failed refresh leaves the active account unchanged and returns undefined;\n * the account-rotation processor owns switching so it can persist a visible\n * account-switch notice with the request that triggered the change.\n */\n async getOAuthCredential(\n providerId: string,\n accountInstanceId?: string,\n ): Promise<OAuthCredentialSnapshot | undefined> {\n this.reload();\n const activeEntry = this.getActiveAccount(providerId);\n const selectedEntry = accountInstanceId\n ? this.accountEntries(providerId).find(entry => entry.id === accountInstanceId)\n : activeEntry;\n const slot = this.data[providerId];\n let credential: OAuthCredentials | undefined;\n if (selectedEntry) {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n ...accountCredential\n } = selectedEntry;\n credential = accountCredential;\n // The active account's credentials live in both the legacy slot and its\n // registry entry, and a legacy writer (older build, another worktree) can\n // refresh the slot without rotating the refresh token. `migrate()` only\n // reconciles those when the refresh token changes, so the registry entry\n // can hold stale access/expiry. Prefer the slot for the active account\n // and keep the registry entry as the account identity.\n if (selectedEntry.active && slot?.type === 'oauth' && slot.refresh === selectedEntry.refresh) {\n const { type: _slotType, ...slotCredential } = slot;\n credential = { ...credential, ...slotCredential };\n }\n } else if (!accountInstanceId && slot?.type === 'oauth') {\n credential = slot;\n }\n if (!credential) return undefined;\n\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n const selectedInstanceId = selectedEntry?.id;\n const toSnapshot = (credentials: OAuthCredentials): OAuthCredentialSnapshot => ({\n type: 'oauth',\n ...credentials,\n accountInstanceId: selectedInstanceId,\n });\n\n if (Date.now() < credential.expires) return toSnapshot(credential);\n\n if (selectedInstanceId) {\n const refreshed = await this.refreshInstance(providerId, selectedInstanceId, credential);\n return refreshed ? toSnapshot(refreshed) : undefined;\n }\n\n const pendingRefresh = this.refreshPromises.get(providerId);\n const refresh =\n pendingRefresh ??\n (async (): Promise<OAuthCredentials | undefined> => {\n try {\n const fresh = await provider.refreshToken(credential);\n this.persistRefreshedCredential(providerId, undefined, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n if (!pendingRefresh) this.refreshPromises.set(providerId, refresh);\n try {\n const refreshed = await refresh;\n return refreshed ? toSnapshot(refreshed) : undefined;\n } finally {\n if (!pendingRefresh) this.refreshPromises.delete(providerId);\n }\n }\n\n /** Get API key for a provider, refreshing OAuth tokens if needed. */\n async getApiKey(providerId: string, accountInstanceId?: string): Promise<string | undefined> {\n this.reload();\n const cred = this.data[providerId];\n // A provider slot holding an API key belongs to the provider, not to any one\n // subscription, so it is only the right credential for a request that did\n // not select an account. A routed request must resolve the account it asked\n // for — or nothing — rather than be served this key.\n if (accountInstanceId === undefined && cred?.type === 'api_key') return cred.key;\n\n const oauth = await this.getOAuthCredential(providerId, accountInstanceId);\n const provider = oauth ? getOAuthProvider(providerId) : undefined;\n return oauth && provider ? provider.getApiKey(oauth) : undefined;\n }\n\n /**\n * Force one refresh of the active OAuth account's tokens, regardless of\n * expiry. The account-rotation error processor calls this when a provider\n * rejects a not-yet-expired token (401/403) — server-side clock skew and\n * refresh-token races surface that way. Returns the fresh access token, or\n * undefined when the refresh fails or there is no OAuth credential.\n * Shares the per-instance refresh dedupe with `getApiKey`.\n */\n async forceRefreshActiveAccount(providerId: string, accountInstanceId?: string): Promise<string | undefined> {\n this.reload();\n const activeEntry = this.getActiveAccount(providerId);\n const selectedEntry = accountInstanceId\n ? this.accountEntries(providerId).find(entry => entry.id === accountInstanceId)\n : activeEntry;\n const slot = this.get(providerId);\n let cred: OAuthCredentials | undefined;\n if (selectedEntry) {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n ...accountCredential\n } = selectedEntry;\n cred = accountCredential;\n } else if (!accountInstanceId && slot?.type === 'oauth') {\n cred = slot;\n }\n if (!cred) return undefined;\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n\n const refreshKey = selectedEntry ? `${providerId}:${selectedEntry.id}` : providerId;\n const pending = this.refreshPromises.get(refreshKey);\n const refresh =\n pending ??\n (async (): Promise<OAuthCredentials | undefined> => {\n try {\n const fresh = await provider.refreshToken(cred);\n this.persistRefreshedCredential(providerId, selectedEntry?.id, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n if (!pending) this.refreshPromises.set(refreshKey, refresh);\n try {\n const creds = await refresh;\n return creds ? provider.getApiKey(creds) : undefined;\n } finally {\n if (!pending) this.refreshPromises.delete(refreshKey);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","names":[],"sources":["../../src/auth/storage.ts"],"sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n */\n\nimport { createHash } from 'node:crypto';\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppDataDir } from '../utils/project.js';\nimport { anthropicOAuthProvider } from './providers/anthropic.js';\nimport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nimport { kimiCodingOAuthProvider } from './providers/kimi-coding.js';\nimport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\nimport { xaiOAuthProvider } from './providers/xai.js';\nimport type {\n AuthCredential,\n AuthStorageData,\n OAuthAccountRecord,\n OAuthCredential,\n OAuthCredentialSnapshot,\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderId,\n OAuthProviderInterface,\n} from './types.js';\n\n/**\n * Best/default models for each OAuth provider.\n * Used when auto-selecting a model after login.\n */\nexport const PROVIDER_DEFAULT_MODELS: Record<OAuthProviderId, string> = {\n anthropic: 'anthropic/claude-fable-5',\n 'openai-codex': 'openai/gpt-5.6-sol',\n // gpt-4.1 routes through `/chat/completions` (which our OpenAI-compatible\n // adapter handles); Anthropic-shaped Copilot models (Claude on `/v1/messages`)\n // are not yet wired up, so picking one as the post-login default would error.\n 'github-copilot': 'github-copilot/gpt-4.1',\n 'kimi-for-coding': 'kimi-for-coding/kimi-for-coding',\n xai: 'xai/grok-4.5',\n};\n\n// Provider registry\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>([\n [anthropicOAuthProvider.id, anthropicOAuthProvider],\n [openaiCodexOAuthProvider.id, openaiCodexOAuthProvider],\n [githubCopilotOAuthProvider.id, githubCopilotOAuthProvider],\n [kimiCodingOAuthProvider.id, kimiCodingOAuthProvider],\n [xaiOAuthProvider.id, xaiOAuthProvider],\n]);\n\n/**\n * Get an OAuth provider by ID\n */\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n return oauthProviderRegistry.get(id);\n}\n\n/**\n * Get all registered OAuth providers\n */\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n return Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * Mint an account instance id: `${providerId}:${randomUUID()}`.\n *\n * Assigned once, when the account is created, and never derived from\n * credentials. Deriving it from the refresh token (the pre-A13 scheme) looked\n * stable but was not: `persistRefreshedCredential` keeps an entry's key while\n * writing rotated tokens onto it, so every refresh left the id describing a\n * credential the account no longer had — and re-adding that same subscription\n * then failed to match its own entry and registered a second one for it.\n */\nfunction mintAccountId(providerId: string): string {\n return `${providerId}:${globalThis.crypto.randomUUID()}`;\n}\n\n/**\n * The pre-A13 id scheme: `${providerId}:${sha256(refresh).slice(0,8)}`.\n *\n * No longer used to assign ids, but still needed to recognize registries\n * written before A13 — both to match a re-authorized account onto its existing\n * entry, and to recognize entries whose fields the A13 migration must backfill.\n */\nfunction legacyAccountIdFor(providerId: string, refreshToken: string): string {\n return `${providerId}:${createHash('sha256').update(refreshToken).digest('hex').slice(0, 8)}`;\n}\n\n/**\n * Stable identity for the *subscription* behind an account, when the provider\n * exposes one (an account id, an email — anything that survives a refresh and\n * a re-authorization). Used to decide whether an added account is one we\n * already have. Providers whose credentials carry no such identifier return\n * undefined; for those, a re-authorization is indistinguishable from a new\n * account without spending a request.\n */\nfunction providerIdentity(provider: OAuthProviderInterface | undefined, creds: OAuthCredentials): string | undefined {\n try {\n const identity = provider?.getAccountIdentity?.(creds);\n return typeof identity === 'string' && identity.length > 0 ? identity : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isOAuthAccountRecord(value: unknown): value is OAuthAccountRecord {\n if (!value || typeof value !== 'object') return false;\n const v = value as Partial<OAuthAccountRecord>;\n return (\n v.type === 'oauth-account' &&\n typeof v.id === 'string' &&\n typeof v.label === 'string' &&\n typeof v.addedAt === 'string' &&\n typeof v.active === 'boolean' &&\n typeof v.refresh === 'string' &&\n typeof v.access === 'string' &&\n typeof v.expires === 'number'\n );\n}\n\n/** The credential fields of an account record — everything but registry identity. */\nfunction credentialFieldsOf(record: OAuthAccountRecord): OAuthCredentials {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n identity: _identity,\n ...creds\n } = record;\n return creds;\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n private data: AuthStorageData = {};\n private refreshPromises = new Map<string, Promise<OAuthCredentials | undefined>>();\n\n constructor(private authPath: string = join(getAppDataDir(), 'auth.json')) {\n this.reload();\n }\n\n /**\n * Reload credentials from disk.\n */\n reload(): void {\n if (!existsSync(this.authPath)) {\n this.data = {};\n return;\n }\n try {\n this.data = JSON.parse(readFileSync(this.authPath, 'utf-8'));\n } catch {\n this.data = {};\n }\n this.migrate();\n }\n\n /**\n * Bring auth.json up to the multi-account registry format. Runs on every\n * load; saves only when something actually changed.\n *\n * 1. A legacy OAuth slot with no registry gets one adopted active entry\n * (the slot's tokens stay put — the entry mirrors them).\n * 2. When the slot's refresh token differs from the active entry's, an\n * external writer (older build, another worktree) owns the slot — the\n * slot wins and its tokens are adopted onto the active entry.\n * 3. A registry with no active entry (hand-edited) self-heals to its first\n * entry; malformed `accounts:` values are skipped, never fatal.\n * 4. A registry whose legacy slot disappeared (partial write, an external\n * hand-edit) self-heals by mirroring the active entry's tokens back into\n * the slot, so `isLoggedIn()`/`getOAuthCredential()` keep agreeing with\n * `listAccounts()`. `remove()`/`logout()` clear the registry with the\n * slot, so they never leave the shape this heals.\n */\n private migrate(): void {\n let changed = false;\n for (const key of Object.keys(this.data)) {\n const slot = this.data[key];\n if (!slot || slot.type !== 'oauth') continue;\n const providerId = key;\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) {\n this.adoptSlot(providerId, slot);\n changed = true;\n continue;\n }\n const active = entries.find(entry => entry.active);\n if (!active) {\n this.data[this.accountKeyFor(entries[0]!.id)] = { ...entries[0]!, active: true };\n changed = true;\n continue;\n }\n if (active.refresh !== slot.refresh) {\n // An external writer (older build, another worktree) owns the slot.\n // If its tokens belong to a different registered account, that\n // account is now the active one — re-point activation to it instead\n // of cloning the refresh token onto the current active entry.\n const matching = entries.find(entry => entry.id !== active.id && entry.refresh === slot.refresh);\n if (matching) {\n const { type: _t, ...slotCreds } = slot;\n this.data[this.accountKeyFor(matching.id)] = {\n ...matching,\n ...slotCreds,\n type: 'oauth-account',\n active: true,\n };\n this.data[this.accountKeyFor(active.id)] = { ...active, active: false };\n changed = true;\n } else {\n const { type: _type, ...slotCreds } = slot;\n this.data[this.accountKeyFor(active.id)] = { ...active, ...slotCreds, type: 'oauth-account' };\n changed = true;\n }\n }\n }\n\n // Case 4: registry entries without a legacy slot (the slot loop above\n // only ever sees providers that still have one).\n const registeredProviders = new Set<string>();\n for (const key of Object.keys(this.data)) {\n if (!key.startsWith('accounts:')) continue;\n const providerId = key.slice('accounts:'.length).split(':')[0];\n if (providerId) registeredProviders.add(providerId);\n }\n for (const providerId of registeredProviders) {\n if (this.data[providerId]?.type === 'oauth') continue;\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) continue;\n let active = entries.find(entry => entry.active);\n if (!active) {\n active = entries[0]!;\n this.data[this.accountKeyFor(active.id)] = { ...active, active: true };\n }\n this.data[providerId] = { type: 'oauth', ...credentialFieldsOf(active) };\n changed = true;\n }\n\n // Case 5 (A13): backfill the stable account identity on entries that\n // predate it, so adding a subscription we already hold updates its entry\n // instead of registering a second one for it.\n //\n // Ids are deliberately left exactly as they are. Ids are minted once and\n // derive from nothing (A13), so there is nothing to repair — and re-keying\n // a live registry would invalidate every id persisted outside this file\n // (settings routing preferences, per-thread routing state) for no gain.\n for (const providerId of registeredProviders) {\n const provider = getOAuthProvider(providerId as OAuthProviderId);\n if (!provider?.getAccountIdentity) continue;\n for (const entry of this.accountEntries(providerId)) {\n if (entry.identity) continue;\n const identity = providerIdentity(provider, entry);\n if (!identity) continue;\n this.data[this.accountKeyFor(entry.id)] = { ...entry, identity };\n changed = true;\n }\n }\n\n if (changed) this.save();\n }\n\n /**\n * Save credentials to disk.\n */\n private save(): void {\n const dir = dirname(this.authPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n // Write to a sibling file, then atomically replace auth.json. Provider\n // fetch wrappers reload this file on every request, including in older\n // Mastra Code processes; writing in place lets them observe truncated JSON\n // and temporarily treat every provider as logged out.\n const tempPath = `${this.authPath}.${process.pid}.${globalThis.crypto.randomUUID()}.tmp`;\n try {\n writeFileSync(tempPath, JSON.stringify(this.data, null, 2), { encoding: 'utf-8', mode: 0o600 });\n chmodSync(tempPath, 0o600);\n renameSync(tempPath, this.authPath);\n } finally {\n rmSync(tempPath, { force: true });\n }\n }\n\n /**\n * Get credential for a provider. Registry entries (type 'oauth-account')\n * live under `accounts:` keys and never surface here.\n */\n get(provider: string): AuthCredential | undefined {\n const cred = this.data[provider];\n return cred !== undefined && cred.type !== 'oauth-account' ? cred : undefined;\n }\n\n /**\n * Set credential for a provider.\n */\n set(provider: string, credential: AuthCredential): void {\n this.reload();\n this.data[provider] = credential;\n this.save();\n }\n\n /**\n * Remove credential for a provider, including its account registry.\n * Clearing only the slot would leave the registry behind, and the next\n * load's migration would heal the slot back from it — resurrecting the\n * provider the caller just signed out.\n */\n remove(provider: string): void {\n this.reload();\n delete this.data[provider];\n const prefix = this.accountPrefixFor(provider);\n for (const key of Object.keys(this.data)) {\n if (key.startsWith(prefix)) delete this.data[key];\n }\n this.save();\n }\n\n /**\n * List all providers with credentials.\n */\n list(): string[] {\n return Object.keys(this.data);\n }\n\n /**\n * Check if credentials exist for a provider.\n */\n has(provider: string): boolean {\n return provider in this.data;\n }\n\n /**\n * Check if logged in via OAuth for a provider.\n */\n isLoggedIn(provider: string): boolean {\n const cred = this.data[provider];\n return cred?.type === 'oauth';\n }\n\n /**\n * Check if a stored API key exists for a provider.\n * Keys are stored under `apikey:<provider>` in auth.json.\n */\n hasStoredApiKey(provider: string): boolean {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0;\n }\n\n /**\n * Get a stored API key for a provider, if any.\n */\n getStoredApiKey(provider: string): string | undefined {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0 ? cred.key : undefined;\n }\n\n /**\n * Store an API key for a provider.\n * Also sets the corresponding environment variable so model resolution can find it.\n */\n setStoredApiKey(provider: string, key: string, envVar?: string): void {\n this.set(`apikey:${provider}`, { type: 'api_key', key });\n if (envVar) {\n process.env[envVar] = key;\n }\n }\n\n /**\n * Load all stored API keys into process.env.\n * Called at startup so model resolution can find stored keys.\n * Only sets env vars that aren't already set (env vars take precedence).\n */\n loadStoredApiKeysIntoEnv(providerEnvVars: Record<string, string | undefined>): void {\n for (const [key, cred] of Object.entries(this.data)) {\n if (!key.startsWith('apikey:') || cred.type !== 'api_key' || !cred.key) continue;\n const provider = key.substring('apikey:'.length);\n const envVar = providerEnvVars[provider];\n if (envVar && !process.env[envVar]) {\n process.env[envVar] = cred.key;\n }\n }\n }\n\n /**\n * Login to an OAuth provider. Resolves to the registered account record.\n * Pass `activate: false` to add the account without making it active (the\n * first account for a provider is always activated — a provider with\n * accounts must have an active one).\n */\n async login(\n providerId: OAuthProviderId,\n callbacks: OAuthLoginCallbacks,\n opts?: { replaceAccountId?: string; activate?: boolean },\n ): Promise<OAuthAccountRecord> {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown OAuth provider: ${providerId}`);\n }\n\n const credentials = await provider.login(callbacks);\n // Route through the account registry: the new account is appended (or\n // updated in place — by id collision or an explicit replaceAccountId for\n // re-authentication) and becomes the active account unless activate:false.\n return this.addAccount(providerId, credentials, opts);\n }\n\n /**\n * Logout from a provider: remove the legacy slot and every registered account.\n */\n logout(provider: string): void {\n this.remove(provider);\n }\n\n // ---------------------------------------------------------------------------\n // Multi-account registry\n // ---------------------------------------------------------------------------\n\n private accountPrefixFor(providerId: string): string {\n return `accounts:${providerId}:`;\n }\n\n private accountKeyFor(id: string): string {\n return `accounts:${id}`;\n }\n\n /** Registry entries for a provider in JSON insertion order (malformed values skipped). */\n private accountEntries(providerId: string): OAuthAccountRecord[] {\n const prefix = this.accountPrefixFor(providerId);\n const out: OAuthAccountRecord[] = [];\n for (const [key, value] of Object.entries(this.data)) {\n if (key.startsWith(prefix) && isOAuthAccountRecord(value)) out.push(value);\n }\n return out;\n }\n\n /** Adopt a legacy slot credential as the registry's first active entry. */\n private adoptSlot(providerId: string, slot: OAuthCredential): OAuthAccountRecord {\n const id = mintAccountId(providerId);\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, slot);\n const record: OAuthAccountRecord = {\n ...slot,\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n label: `${provider?.name ?? providerId} account 1`,\n addedAt: new Date().toISOString(),\n active: true,\n };\n this.data[this.accountKeyFor(id)] = record;\n return record;\n }\n\n /**\n * Registered OAuth accounts for a provider, in insertion order. Copies —\n * callers cannot corrupt unsaved storage state by mutating a record.\n */\n listAccounts(providerId: string): OAuthAccountRecord[] {\n return this.accountEntries(providerId).map(entry => ({ ...entry }));\n }\n\n /**\n * The provider's active registry entry, if a registry exists. A copy.\n */\n getActiveAccount(providerId: string): OAuthAccountRecord | undefined {\n const active = this.accountEntries(providerId).find(entry => entry.active);\n return active ? { ...active } : undefined;\n }\n\n /**\n * Register OAuth credentials as an account for a provider, making it active\n * unless `activate: false` is passed.\n *\n * With `replaceAccountId` (re-authentication of a picked account), the\n * target entry's tokens are replaced in place — id re-keyed to the new\n * refresh-token hash, label/position/addedAt preserved — because providers\n * rotate refresh tokens per authorization, so the picked account's old id\n * never matches the new token hash. Re-authentication keeps the target's\n * active state: an inactive account stays inactive and the currently active\n * account keeps the legacy slot. Otherwise, when the new credentials\n * hash to an existing entry's id (same refresh token), that entry is\n * updated in place; a genuinely new account is appended and activated.\n *\n * `activate: false` only applies to the plain add path: the new/updated entry\n * keeps the active state it had, and the previously active account stays\n * active. The first account of a provider is activated regardless.\n */\n async addAccount(\n providerId: string,\n creds: OAuthCredentials,\n opts?: { label?: string; replaceAccountId?: string; activate?: boolean },\n ): Promise<OAuthAccountRecord> {\n this.reload();\n const entries = this.accountEntries(providerId);\n\n if (opts?.replaceAccountId) {\n const target = entries.find(entry => entry.id === opts.replaceAccountId);\n if (!target) {\n throw new Error(`No account ${opts.replaceAccountId} for provider ${providerId}`);\n }\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, creds) ?? target.identity;\n // Re-authentication updates the account in place and **keeps its id**:\n // ids are minted once and never derived from credentials (A13), so the\n // entry's insertion position, the settings routing preference and any\n // thread routing state that name this id all stay valid.\n //\n // A stale entry for the *same* subscription is dropped in favor of the\n // picked account. Same-subscription is a matching stable identity, or —\n // for a registry written before A13, whose ids were the refresh-token\n // hash — the credentials' own legacy id, or an entry already holding\n // exactly these credentials (a re-authorization that returns the same\n // tokens, the only signal an identity-less provider gives). The survivor\n // inherits the collided entry's active state, so re-authenticating an\n // inactive account onto the active account's credentials cannot leave the\n // registry with no active entry.\n const legacyId = legacyAccountIdFor(providerId, creds.refresh);\n const collided = entries.find(\n entry =>\n entry.id !== target.id &&\n ((identity !== undefined && entry.identity === identity) ||\n entry.id === legacyId ||\n entry.refresh === creds.refresh),\n );\n // Credential metadata (device id, enterprise URL, …) follows the tokens:\n // when the fresh response omits a field, the collided entry's value is the\n // coherent fallback, not the target's — pairing fresh tokens with another\n // subscription's stale endpoint would misroute requests. Fresh `creds`\n // win last; only the target's registry metadata (id, label, addedAt)\n // carries over.\n const replacement: OAuthAccountRecord = {\n ...target,\n ...(collided ? credentialFieldsOf(collided) : {}),\n ...creds,\n type: 'oauth-account',\n ...(identity ? { identity } : {}),\n label: opts.label ?? target.label,\n };\n const rebuilt: AuthStorageData = {};\n for (const [key, value] of Object.entries(this.data)) {\n if (key === this.accountKeyFor(target.id)) {\n rebuilt[key] = collided?.active ? { ...replacement, active: true } : replacement;\n } else if (collided && key === this.accountKeyFor(collided.id)) {\n continue;\n } else {\n rebuilt[key] = value;\n }\n }\n this.data = rebuilt;\n // Re-authentication preserves the account's active state: fixing a\n // secondary account's tokens must not hijack the active slot. The target\n // is activated when it was already active, when the entry it collided\n // with was active, or when the provider has no active account at all\n // (first account, or a self-healed gap).\n const wasActive =\n target.active === true ||\n collided?.active === true ||\n entries.some(entry => entry.active && entry.id !== target.id) === false;\n if (wasActive) {\n const activated = this.activateInMemory(providerId, target.id);\n if (!activated) {\n throw new Error(`Failed to activate account ${target.id} for provider ${providerId}`);\n }\n this.save();\n return activated;\n }\n // Inactive target: tokens stay on the registry entry, the legacy slot\n // keeps the currently active account's credential untouched.\n const replacementEntry = this.accountEntries(providerId).find(entry => entry.id === target.id);\n if (!replacementEntry) {\n throw new Error(`Failed to store account ${target.id} for provider ${providerId}`);\n }\n this.save();\n return replacementEntry;\n }\n\n // Resolve the label before the final reload+save: the provider hook may\n // hit the network, and a concurrent write during that await must not be\n // clobbered by a stale snapshot.\n const provider = getOAuthProvider(providerId);\n const identity = providerIdentity(provider, creds);\n const label = opts?.label ?? (await provider?.getAccountLabel?.(creds)) ?? null;\n this.reload();\n const freshEntries = this.accountEntries(providerId);\n\n // Is this a subscription we already hold? Match the provider's stable\n // account identity where it exposes one, then the pre-A13 id (the\n // refresh-token hash) so a pre-A13 registry still updates its entry rather\n // than gaining a second one for the same subscription, and finally an\n // entry already holding these exact credentials (a re-add of the same\n // account as returned, which is the only signal an identity-less provider\n // such as Anthropic gives us — a provider that cannot name its accounts\n // cannot say \"this is the same subscription, re-authorized\").\n //\n // Crucially this must not re-derive the id of a *minted* entry: after a\n // refresh such an entry's id no longer hashes its current token, and\n // treating that miss as \"new account\" is exactly how re-adding a\n // subscription used to create a duplicate entry for it.\n const legacyId = legacyAccountIdFor(providerId, creds.refresh);\n const existing =\n (identity ? freshEntries.find(entry => entry.identity === identity) : undefined) ??\n freshEntries.find(entry => entry.id === legacyId) ??\n freshEntries.find(entry => entry.refresh === creds.refresh);\n const id = existing?.id ?? mintAccountId(providerId);\n if (existing) {\n this.data[this.accountKeyFor(id)] = {\n ...existing,\n ...creds,\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n active: existing.active,\n };\n } else {\n const resolvedLabel = label ?? `${provider?.name ?? providerId} account ${freshEntries.length + 1}`;\n this.data[this.accountKeyFor(id)] = {\n type: 'oauth-account',\n id,\n ...(identity ? { identity } : {}),\n label: resolvedLabel,\n addedAt: new Date().toISOString(),\n active: false,\n ...creds,\n };\n }\n\n // activate:false (add-another): keep the current active account. The\n // first account of a provider always activates — a non-empty registry\n // must have an active entry. An id collision with the already-active\n // entry also falls through to activateInMemory so its fresh tokens move\n // into the legacy slot (tokens are single-homed).\n if (opts?.activate === false && freshEntries.length > 0 && !existing?.active) {\n this.save();\n return { ...(this.data[this.accountKeyFor(id)] as OAuthAccountRecord) };\n }\n\n const activated = this.activateInMemory(providerId, id);\n if (!activated) {\n throw new Error(`Failed to activate account ${id} for provider ${providerId}`);\n }\n this.save();\n return activated;\n }\n\n /**\n * Activate an account: move the active tokens out of the legacy slot back\n * into the previously active entry, and the target's tokens into the slot\n * (never copy — one home per token set). With no `instanceId`, rotate to\n * the next entry in insertion order, wrapping once to the front; returns\n * undefined when there is no other entry to rotate to.\n */\n activateAccount(providerId: string, instanceId?: string): OAuthAccountRecord | undefined {\n this.reload();\n const activated = this.activateInMemory(providerId, instanceId);\n if (activated) this.save();\n return activated;\n }\n\n /**\n * Activate an account in memory only — the caller owns reload/save. Move the\n * active tokens out of the legacy slot back into the previously active\n * entry, and the target's tokens into the slot (never copy — one home per\n * token set). With no `instanceId`, rotate to the next entry in insertion\n * order, wrapping once to the front; returns undefined when there is no\n * other entry to rotate to.\n */\n private activateInMemory(providerId: string, instanceId?: string): OAuthAccountRecord | undefined {\n const entries = this.accountEntries(providerId);\n if (entries.length === 0) return undefined;\n\n let target: OAuthAccountRecord | undefined;\n if (instanceId !== undefined) {\n target = entries.find(entry => entry.id === instanceId);\n if (!target) return undefined;\n } else {\n if (entries.length <= 1) return undefined;\n const currentIdx = entries.findIndex(entry => entry.active);\n const candidates =\n currentIdx >= 0 ? [...entries.slice(currentIdx + 1), ...entries.slice(0, currentIdx)] : [...entries];\n target = candidates[0]!;\n }\n if (!target) return undefined;\n\n const slot = this.data[providerId];\n const current = entries.find(entry => entry.active && entry.id !== target.id);\n\n // Move the slot's tokens back onto the previously active entry. Skipped\n // when the target is already active — its (possibly re-authenticated)\n // entry already holds the freshest tokens.\n if (current && slot?.type === 'oauth') {\n const { type: _type, ...slotCreds } = slot;\n this.data[this.accountKeyFor(current.id)] = { ...current, ...slotCreds, type: 'oauth-account', active: false };\n }\n\n // Move the target's tokens into the legacy slot. The slot is rebuilt from\n // the target's own credentials only: carrying fields over from the\n // previously active account would pair this account's tokens with that\n // account's metadata (`deviceId`, `enterpriseUrl`, account id), and the\n // providers read those fields off the slot.\n this.data[providerId] = { ...credentialFieldsOf(target), type: 'oauth' };\n\n // Exactly the target stays active (self-heals multi-active states).\n this.data[this.accountKeyFor(target.id)] = { ...target, active: true };\n for (const entry of entries) {\n if (entry.id === target.id || entry.id === current?.id) continue;\n if (entry.active) {\n this.data[this.accountKeyFor(entry.id)] = { ...entry, active: false };\n }\n }\n\n return { ...target, active: true };\n }\n\n /**\n * Remove an account from the registry. If it was active, the next entry in\n * insertion order is activated; when it was the last one, the legacy slot\n * is removed too (full sign-out for that provider).\n */\n removeAccount(providerId: string, instanceId: string): void {\n this.reload();\n const entries = this.accountEntries(providerId);\n const target = entries.find(entry => entry.id === instanceId);\n if (!target) return;\n\n const wasActive = target.active;\n delete this.data[this.accountKeyFor(instanceId)];\n\n if (wasActive) {\n const idx = entries.indexOf(target);\n const next = entries[idx + 1] ?? entries.find(entry => entry.id !== instanceId);\n if (next) {\n // The removed account's tokens die with it — only move tokens in.\n this.data[providerId] = { type: 'oauth', ...credentialFieldsOf(next) };\n this.data[this.accountKeyFor(next.id)] = { ...next, active: true };\n for (const entry of entries) {\n if (entry.id === next.id || entry.id === instanceId) continue;\n if (entry.active) {\n this.data[this.accountKeyFor(entry.id)] = { ...entry, active: false };\n }\n }\n } else {\n delete this.data[providerId];\n }\n }\n\n this.save();\n }\n\n /**\n * Rename an account's label.\n */\n renameAccount(providerId: string, instanceId: string, label: string): void {\n this.reload();\n const key = this.accountKeyFor(instanceId);\n const entry = this.data[key];\n if (!isOAuthAccountRecord(entry)) return;\n this.data[key] = { ...entry, label };\n this.save();\n }\n\n /**\n * Persist refreshed credentials for the account that initiated the refresh.\n * The legacy slot is updated only if that account is still active when the\n * refresh completes; otherwise a delayed refresh could overwrite a newer\n * activation.\n */\n private persistRefreshedCredential(\n providerId: string,\n instanceId: string | undefined,\n creds: OAuthCredentials,\n ): void {\n this.reload();\n if (instanceId) {\n const key = this.accountKeyFor(instanceId);\n const entry = this.data[key];\n if (!isOAuthAccountRecord(entry)) return;\n this.data[key] = { ...entry, ...creds, type: 'oauth-account' };\n if (entry.active) {\n const slot = this.data[providerId];\n this.data[providerId] =\n slot?.type === 'oauth' ? { ...slot, ...creds, type: 'oauth' } : { type: 'oauth', ...creds };\n }\n } else {\n const slot = this.data[providerId];\n this.data[providerId] =\n slot?.type === 'oauth' ? { ...slot, ...creds, type: 'oauth' } : { type: 'oauth', ...creds };\n }\n this.save();\n }\n\n /**\n * Refresh one account instance through the per-instance dedupe map. Used\n * for sibling refreshes during the rotation walk, so a concurrent\n * `getApiKey` that reloads storage mid-walk (after the candidate was\n * activated but before its refresh resolves) joins the same refresh\n * instead of double-spending a single-use refresh token.\n */\n private async refreshInstance(\n providerId: string,\n instanceId: string,\n creds: OAuthCredentials,\n ): Promise<OAuthCredentials | undefined> {\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n const refreshKey = `${providerId}:${instanceId}`;\n const pending = this.refreshPromises.get(refreshKey);\n if (pending) return pending;\n const refresh = (async () => {\n try {\n const fresh = await provider.refreshToken(creds);\n this.persistRefreshedCredential(providerId, instanceId, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n this.refreshPromises.set(refreshKey, refresh);\n try {\n return await refresh;\n } finally {\n this.refreshPromises.delete(refreshKey);\n }\n }\n\n /**\n * Get a ready-to-use OAuth credential snapshot, auto-refreshing if needed.\n * A failed refresh leaves the active account unchanged and returns undefined;\n * the account-rotation processor owns switching so it can persist a visible\n * account-switch notice with the request that triggered the change.\n */\n async getOAuthCredential(\n providerId: string,\n accountInstanceId?: string,\n ): Promise<OAuthCredentialSnapshot | undefined> {\n this.reload();\n const activeEntry = this.getActiveAccount(providerId);\n const selectedEntry = accountInstanceId\n ? this.accountEntries(providerId).find(entry => entry.id === accountInstanceId)\n : activeEntry;\n const slot = this.data[providerId];\n let credential: OAuthCredentials | undefined;\n if (selectedEntry) {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n ...accountCredential\n } = selectedEntry;\n credential = accountCredential;\n // The active account's credentials live in both the legacy slot and its\n // registry entry, and a legacy writer (older build, another worktree) can\n // refresh the slot without rotating the refresh token. `migrate()` only\n // reconciles those when the refresh token changes, so the registry entry\n // can hold stale access/expiry. Prefer the slot for the active account\n // and keep the registry entry as the account identity.\n if (selectedEntry.active && slot?.type === 'oauth' && slot.refresh === selectedEntry.refresh) {\n const { type: _slotType, ...slotCredential } = slot;\n credential = { ...credential, ...slotCredential };\n }\n } else if (!accountInstanceId && slot?.type === 'oauth') {\n credential = slot;\n }\n if (!credential) return undefined;\n\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n const selectedInstanceId = selectedEntry?.id;\n const toSnapshot = (credentials: OAuthCredentials): OAuthCredentialSnapshot => ({\n type: 'oauth',\n ...credentials,\n accountInstanceId: selectedInstanceId,\n });\n\n if (Date.now() < credential.expires) return toSnapshot(credential);\n\n if (selectedInstanceId) {\n const refreshed = await this.refreshInstance(providerId, selectedInstanceId, credential);\n return refreshed ? toSnapshot(refreshed) : undefined;\n }\n\n const pendingRefresh = this.refreshPromises.get(providerId);\n const refresh =\n pendingRefresh ??\n (async (): Promise<OAuthCredentials | undefined> => {\n try {\n const fresh = await provider.refreshToken(credential);\n this.persistRefreshedCredential(providerId, undefined, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n if (!pendingRefresh) this.refreshPromises.set(providerId, refresh);\n try {\n const refreshed = await refresh;\n return refreshed ? toSnapshot(refreshed) : undefined;\n } finally {\n if (!pendingRefresh) this.refreshPromises.delete(providerId);\n }\n }\n\n /** Get API key for a provider, refreshing OAuth tokens if needed. */\n async getApiKey(providerId: string, accountInstanceId?: string): Promise<string | undefined> {\n this.reload();\n const cred = this.data[providerId];\n // A provider slot holding an API key belongs to the provider, not to any one\n // subscription, so it is only the right credential for a request that did\n // not select an account. A routed request must resolve the account it asked\n // for — or nothing — rather than be served this key.\n if (accountInstanceId === undefined && cred?.type === 'api_key') return cred.key;\n\n const oauth = await this.getOAuthCredential(providerId, accountInstanceId);\n const provider = oauth ? getOAuthProvider(providerId) : undefined;\n return oauth && provider ? provider.getApiKey(oauth) : undefined;\n }\n\n /**\n * Force one refresh of the active OAuth account's tokens, regardless of\n * expiry. The account-rotation error processor calls this when a provider\n * rejects a not-yet-expired token (401/403) — server-side clock skew and\n * refresh-token races surface that way. Returns the fresh access token, or\n * undefined when the refresh fails or there is no OAuth credential.\n * Shares the per-instance refresh dedupe with `getApiKey`.\n */\n async forceRefreshActiveAccount(providerId: string, accountInstanceId?: string): Promise<string | undefined> {\n this.reload();\n const activeEntry = this.getActiveAccount(providerId);\n const selectedEntry = accountInstanceId\n ? this.accountEntries(providerId).find(entry => entry.id === accountInstanceId)\n : activeEntry;\n const slot = this.get(providerId);\n let cred: OAuthCredentials | undefined;\n if (selectedEntry) {\n const {\n type: _type,\n id: _id,\n label: _label,\n addedAt: _addedAt,\n active: _active,\n ...accountCredential\n } = selectedEntry;\n cred = accountCredential;\n } else if (!accountInstanceId && slot?.type === 'oauth') {\n cred = slot;\n }\n if (!cred) return undefined;\n const provider = getOAuthProvider(providerId);\n if (!provider) return undefined;\n\n const refreshKey = selectedEntry ? `${providerId}:${selectedEntry.id}` : providerId;\n const pending = this.refreshPromises.get(refreshKey);\n const refresh =\n pending ??\n (async (): Promise<OAuthCredentials | undefined> => {\n try {\n const fresh = await provider.refreshToken(cred);\n this.persistRefreshedCredential(providerId, selectedEntry?.id, fresh);\n return fresh;\n } catch {\n return undefined;\n }\n })();\n if (!pending) this.refreshPromises.set(refreshKey, refresh);\n try {\n const creds = await refresh;\n return creds ? provider.getApiKey(creds) : undefined;\n } finally {\n if (!pending) this.refreshPromises.delete(refreshKey);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Agent } from '@mastra/core/agent';
2
+ import type { Mastra } from '@mastra/core/mastra';
3
+ import { type ExperimentalAgent, type GlobalSettings } from './onboarding/settings.js';
4
+ export interface ExperimentalAgentEnvironment {
5
+ MASTRACODE_EXPERIMENTAL_AGENT?: string;
6
+ }
7
+ export declare function parseExperimentalAgentEnvironment(env?: ExperimentalAgentEnvironment): ExperimentalAgent | null;
8
+ export declare function resolveExperimentalAgent(settings: Pick<GlobalSettings, 'experimentalAgent' | '_experimentalAgentSettingsPath'>, env?: ExperimentalAgentEnvironment): ExperimentalAgent | null;
9
+ export declare function wrapExperimentalAgent(agent: Agent, selection: ExperimentalAgent | null): Agent;
10
+ export declare function validateExperimentalAgent(agent: Agent, mastra: Mastra | undefined, report?: (message: string) => void): void;
11
+ //# sourceMappingURL=experimental-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experimental-agent.d.ts","sourceRoot":"","sources":["../src/experimental-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAiC,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEtH,MAAM,WAAW,4BAA4B;IAC3C,6BAA6B,CAAC,EAAE,MAAM,CAAC;CACxC;AAID,wBAAgB,iCAAiC,CAC/C,GAAG,GAAE,4BAA0C,GAC9C,iBAAiB,GAAG,IAAI,CAK1B;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,GAAG,gCAAgC,CAAC,EACtF,GAAG,GAAE,4BAA0C,GAC9C,iBAAiB,GAAG,IAAI,CAK1B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,iBAAiB,GAAG,IAAI,GAAG,KAAK,CAI9F;AAMD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAmB,GAC/C,IAAI,CAiCN"}
@@ -0,0 +1,36 @@
1
+ import { parseExperimentalAgentSetting } from "./onboarding/settings.js";
2
+ import { createDurableAgent, createEventedAgent, isDurableAgent, isEventedAgent } from "@mastra/core/agent/durable";
3
+ //#region src/experimental-agent.ts
4
+ const ACCEPTED_VALUES = "\"durable\", \"evented\", or unset";
5
+ function parseExperimentalAgentEnvironment(env = process.env) {
6
+ const value = env.MASTRACODE_EXPERIMENTAL_AGENT;
7
+ if (value === void 0 || value === "") return null;
8
+ if (value === "durable" || value === "evented") return value;
9
+ throw new Error(`Invalid MASTRACODE_EXPERIMENTAL_AGENT value ${JSON.stringify(value)}. Expected ${ACCEPTED_VALUES}.`);
10
+ }
11
+ function resolveExperimentalAgent(settings, env = process.env) {
12
+ const environmentSelection = parseExperimentalAgentEnvironment(env);
13
+ if (environmentSelection) return environmentSelection;
14
+ return parseExperimentalAgentSetting(settings.experimentalAgent, settings._experimentalAgentSettingsPath);
15
+ }
16
+ function wrapExperimentalAgent(agent, selection) {
17
+ if (selection === "durable") return createDurableAgent({ agent });
18
+ if (selection === "evented") return createEventedAgent({ agent });
19
+ return agent;
20
+ }
21
+ function validateExperimentalAgent(agent, mastra, report = console.info) {
22
+ const selection = isEventedAgent(agent) ? "evented" : isDurableAgent(agent) ? "durable" : null;
23
+ if (!selection) return;
24
+ if (selection === "evented" && (!mastra || agent.getMastraInstance() !== mastra)) throw new Error("Experimental agent \"evented\" requires the coding agent to be registered on a Mastra host before startup.");
25
+ const workflowsStore = mastra?.getStorage()?.stores?.workflows;
26
+ if (!workflowsStore) throw new Error(`Experimental agent "${selection}" requires a configured workflow storage domain.`);
27
+ if (selection === "evented" && workflowsStore.supportsConcurrentUpdates?.() !== true) throw new Error("Experimental agent \"evented\" requires workflow storage with atomic concurrent updates (supportsConcurrentUpdates() must return true).");
28
+ const engineType = agent.getWorkflow().engineType;
29
+ const expectedEngine = selection === "evented" ? "evented" : "default";
30
+ if (engineType !== expectedEngine) throw new Error(`Experimental agent "${selection}" resolved workflow engine ${JSON.stringify(engineType)} instead of "${expectedEngine}".`);
31
+ report(`Experimental agent: ${selection} (workflow engine: ${engineType})`);
32
+ }
33
+ //#endregion
34
+ export { parseExperimentalAgentEnvironment, resolveExperimentalAgent, validateExperimentalAgent, wrapExperimentalAgent };
35
+
36
+ //# sourceMappingURL=experimental-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experimental-agent.js","names":[],"sources":["../src/experimental-agent.ts"],"sourcesContent":["import type { Agent } from '@mastra/core/agent';\nimport { createDurableAgent, createEventedAgent, isDurableAgent, isEventedAgent } from '@mastra/core/agent/durable';\nimport type { Mastra } from '@mastra/core/mastra';\n\nimport { parseExperimentalAgentSetting, type ExperimentalAgent, type GlobalSettings } from './onboarding/settings.js';\n\nexport interface ExperimentalAgentEnvironment {\n MASTRACODE_EXPERIMENTAL_AGENT?: string;\n}\n\nconst ACCEPTED_VALUES = '\"durable\", \"evented\", or unset';\n\nexport function parseExperimentalAgentEnvironment(\n env: ExperimentalAgentEnvironment = process.env,\n): ExperimentalAgent | null {\n const value = env.MASTRACODE_EXPERIMENTAL_AGENT;\n if (value === undefined || value === '') return null;\n if (value === 'durable' || value === 'evented') return value;\n throw new Error(`Invalid MASTRACODE_EXPERIMENTAL_AGENT value ${JSON.stringify(value)}. Expected ${ACCEPTED_VALUES}.`);\n}\n\nexport function resolveExperimentalAgent(\n settings: Pick<GlobalSettings, 'experimentalAgent' | '_experimentalAgentSettingsPath'>,\n env: ExperimentalAgentEnvironment = process.env,\n): ExperimentalAgent | null {\n const environmentSelection = parseExperimentalAgentEnvironment(env);\n if (environmentSelection) return environmentSelection;\n\n return parseExperimentalAgentSetting(settings.experimentalAgent, settings._experimentalAgentSettingsPath);\n}\n\nexport function wrapExperimentalAgent(agent: Agent, selection: ExperimentalAgent | null): Agent {\n if (selection === 'durable') return createDurableAgent({ agent }) as unknown as Agent;\n if (selection === 'evented') return createEventedAgent({ agent }) as unknown as Agent;\n return agent;\n}\n\ninterface WorkflowBackedAgent extends Agent {\n getWorkflow(): { engineType?: string };\n}\n\nexport function validateExperimentalAgent(\n agent: Agent,\n mastra: Mastra | undefined,\n report: (message: string) => void = console.info,\n): void {\n const selection: ExperimentalAgent | null = isEventedAgent(agent)\n ? 'evented'\n : isDurableAgent(agent)\n ? 'durable'\n : null;\n if (!selection) return;\n\n if (selection === 'evented' && (!mastra || agent.getMastraInstance() !== mastra)) {\n throw new Error(\n 'Experimental agent \"evented\" requires the coding agent to be registered on a Mastra host before startup.',\n );\n }\n\n const workflowsStore = mastra?.getStorage()?.stores?.workflows;\n if (!workflowsStore) {\n throw new Error(`Experimental agent \"${selection}\" requires a configured workflow storage domain.`);\n }\n if (selection === 'evented' && workflowsStore.supportsConcurrentUpdates?.() !== true) {\n throw new Error(\n 'Experimental agent \"evented\" requires workflow storage with atomic concurrent updates (supportsConcurrentUpdates() must return true).',\n );\n }\n\n const engineType = (agent as WorkflowBackedAgent).getWorkflow().engineType;\n const expectedEngine = selection === 'evented' ? 'evented' : 'default';\n if (engineType !== expectedEngine) {\n throw new Error(\n `Experimental agent \"${selection}\" resolved workflow engine ${JSON.stringify(engineType)} instead of \"${expectedEngine}\".`,\n );\n }\n\n report(`Experimental agent: ${selection} (workflow engine: ${engineType})`);\n}\n"],"mappings":";;;AAUA,MAAM,kBAAkB;AAExB,SAAgB,kCACd,MAAoC,QAAQ,KAClB;CAC1B,MAAM,QAAQ,IAAI;CAClB,IAAI,UAAU,KAAA,KAAa,UAAU,IAAI,OAAO;CAChD,IAAI,UAAU,aAAa,UAAU,WAAW,OAAO;CACvD,MAAM,IAAI,MAAM,+CAA+C,KAAK,UAAU,KAAK,EAAE,aAAa,gBAAgB,EAAE;AACtH;AAEA,SAAgB,yBACd,UACA,MAAoC,QAAQ,KAClB;CAC1B,MAAM,uBAAuB,kCAAkC,GAAG;CAClE,IAAI,sBAAsB,OAAO;CAEjC,OAAO,8BAA8B,SAAS,mBAAmB,SAAS,8BAA8B;AAC1G;AAEA,SAAgB,sBAAsB,OAAc,WAA4C;CAC9F,IAAI,cAAc,WAAW,OAAO,mBAAmB,EAAE,MAAM,CAAC;CAChE,IAAI,cAAc,WAAW,OAAO,mBAAmB,EAAE,MAAM,CAAC;CAChE,OAAO;AACT;AAMA,SAAgB,0BACd,OACA,QACA,SAAoC,QAAQ,MACtC;CACN,MAAM,YAAsC,eAAe,KAAK,IAC5D,YACA,eAAe,KAAK,IAClB,YACA;CACN,IAAI,CAAC,WAAW;CAEhB,IAAI,cAAc,cAAc,CAAC,UAAU,MAAM,kBAAkB,MAAM,SACvE,MAAM,IAAI,MACR,4GACF;CAGF,MAAM,iBAAiB,QAAQ,WAAW,CAAC,EAAE,QAAQ;CACrD,IAAI,CAAC,gBACH,MAAM,IAAI,MAAM,uBAAuB,UAAU,iDAAiD;CAEpG,IAAI,cAAc,aAAa,eAAe,4BAA4B,MAAM,MAC9E,MAAM,IAAI,MACR,yIACF;CAGF,MAAM,aAAc,MAA8B,YAAY,CAAC,CAAC;CAChE,MAAM,iBAAiB,cAAc,YAAY,YAAY;CAC7D,IAAI,eAAe,gBACjB,MAAM,IAAI,MACR,uBAAuB,UAAU,6BAA6B,KAAK,UAAU,UAAU,EAAE,eAAe,eAAe,GACzH;CAGF,OAAO,uBAAuB,UAAU,qBAAqB,WAAW,EAAE;AAC5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChG,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAWpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,6BAA6B,CAAS;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAkE;IAMvF,OAAO,IAAI,SAAS,GAAG,IAAI;IAwB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAU3C;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA2B5B;;;OAGG;IACG,mBAAmB,CACvB,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsB5B,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAO1B,QAAQ,IAAI,IAAI;IAMhB,KAAK,IAAI,IAAI;IAQb;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IAqB1G;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2D1D;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBjE;;;;;;;;OAQG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,GAAE,MAAM,OAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAsCtG;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI;IA4B3E,OAAO,CAAC,QAAQ;IAQhB,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;CAmBpB"}
1
+ {"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChG,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAWpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,6BAA6B,CAAS;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAkE;IAMvF,OAAO,IAAI,SAAS,GAAG,IAAI;IAwB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAU3C;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA2B5B;;;OAGG;IACG,mBAAmB,CACvB,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsB5B,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAO1B,QAAQ,IAAI,IAAI;IAMhB,KAAK,IAAI,IAAI;IAQb;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IAqB1G;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2D1D;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBjE;;;;;;;;OAQG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,GAAE,MAAM,OAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAsCtG;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI;IA4B3E,OAAO,CAAC,QAAQ;IAQhB,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;CAmBpB"}
@@ -1,5 +1,4 @@
1
1
  import { loadSettings } from "./onboarding/settings.js";
2
- import { randomUUID } from "crypto";
3
2
  import { getGoalActivityDurationMs } from "@mastra/core/agent";
4
3
  //#region src/goal-manager.ts
5
4
  /**
@@ -73,7 +72,7 @@ var GoalManager = class {
73
72
  const threadId = state.session.thread.getId();
74
73
  const agent = this.getAgent(state);
75
74
  const now = Date.now();
76
- const id = randomUUID();
75
+ const id = globalThis.crypto.randomUUID();
77
76
  this.pendingDelete = null;
78
77
  this.threadId = threadId ?? void 0;
79
78
  this.agentId = agent?.id;
@@ -276,7 +275,7 @@ var GoalManager = class {
276
275
  storedId = record.id;
277
276
  nextRecord = {
278
277
  ...record,
279
- id: record.id ?? randomUUID(),
278
+ id: record.id ?? globalThis.crypto.randomUUID(),
280
279
  activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs)
281
280
  };
282
281
  }
@@ -316,7 +315,7 @@ var GoalManager = class {
316
315
  ...saved.pausedReason ? { pausedReason: saved.pausedReason } : {},
317
316
  startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),
318
317
  updatedAt: Date.now(),
319
- id: saved.id ?? randomUUID()
318
+ id: saved.id ?? globalThis.crypto.randomUUID()
320
319
  };
321
320
  else this.record = null;
322
321
  }