{"version":3,"file":"credentials-oauth.mjs","names":[],"sources":["../src/credentials-oauth.ts"],"sourcesContent":["import { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { CredentialProvider, type CredentialInfo, type CredentialKey, type CredentialRecord, type CredentialRecordEntry, type CredentialRecordInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials'\nimport { FileCredentialStore, oauthCredentialRef, providerIdOfOAuthRef, providerSupportsOAuth, resolveOAuthApiKey, storedOAuthCredential } from './oauth-bridge.js'\nimport { builtinProviders } from './compat/pi-ai.js'\nimport { getAgentDir } from './compat/pi-coding-agent.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// L4 of the OAuth host seam: a standard dsh-credentials provider that serves\n// Pi OAuth tokens to DSH's native LLM request path. A route configured as\n// `apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX` resolves per request through this\n// provider — Pi's double-checked-lock refresh runs on every resolution, so a\n// rotated token reaches the next model call with no restart, exactly the\n// per-operation semantics the credentials seam demands. Every other reference\n// falls through to the process environment, so one provider instance serves a\n// whole composition.\n//\n// Reference convention: PI2DSH_OAUTH_<PROVIDER_ID> where the provider id is\n// upper-cased with `-` as `_` (openai-codex → PI2DSH_OAUTH_OPENAI_CODEX).\n// The helpers live in oauth-bridge (see the note there); re-exported here so\n// this module's public surface is unchanged.\n\nexport { oauthCredentialRef } from './oauth-bridge.js'\nconst providerIdOfRef = providerIdOfOAuthRef\n\nexport interface PiOAuthCredentialProviderOptions {\n  /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */\n  authPath?: string\n  /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */\n  providers?: ReadonlyMap<string, UnknownRecord>\n}\n\nexport class PiOAuthCredentialProvider extends CredentialProvider {\n  private readonly oauthStore: FileCredentialStore\n  private readonly extraProviders: ReadonlyMap<string, UnknownRecord>\n\n  constructor(ctx: Context, options: PiOAuthCredentialProviderOptions = {}) {\n    super(ctx)\n    this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), 'auth.json'))\n    this.extraProviders = options.providers ?? new Map()\n  }\n\n  private oauthConfigFor(providerId: string): UnknownRecord | undefined {\n    const registered = this.extraProviders.get(providerId)\n    if (providerSupportsOAuth(registered)) return registered\n    const builtin = builtinProviders().find(provider => provider.id === providerId)\n    if (builtin === undefined) return undefined\n    return { name: builtin.name, baseUrl: builtin.baseUrl, oauth: builtin.auth.oauth }\n  }\n\n  async resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {\n    const providerId = providerIdOfRef(String(ref))\n    if (providerId === undefined) {\n      const value = process.env[String(ref)]\n      return value !== undefined && value.length > 0 ? { value, source: 'env' } : undefined\n    }\n    const config = this.oauthConfigFor(providerId)\n    if (config === undefined) return undefined\n    const value = await resolveOAuthApiKey({\n      providerId,\n      providerName: typeof config.name === 'string' ? config.name : providerId,\n      providerConfig: config,\n      store: this.oauthStore,\n    })\n    return value !== undefined && value.length > 0 ? { value, source: 'pi-oauth' } : undefined\n  }\n\n  async describe(ref: CredentialRef): Promise<CredentialInfo> {\n    const providerId = providerIdOfRef(String(ref))\n    if (providerId === undefined) {\n      const value = process.env[String(ref)]\n      return { configured: value !== undefined && value.length > 0, ...(value ? { source: 'env' } : {}), writable: false }\n    }\n    const stored = await storedOAuthCredential(this.oauthStore, providerId)\n    return { configured: stored !== undefined, ...(stored !== undefined ? { source: 'pi-oauth' } : {}), writable: false }\n  }\n\n  async set(ref: CredentialRef, _value: string): Promise<void> {\n    throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`)\n  }\n\n  async unset(ref: CredentialRef): Promise<void> {\n    const providerId = providerIdOfRef(String(ref))\n    if (providerId === undefined) {\n      throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`)\n    }\n    // Logging out is a legitimate unset: drop the stored token.\n    await this.oauthStore.delete(providerId, undefined)\n  }\n\n  // ---- credential records (0.1.1-line abstract members) --------------------\n  // The record space projects the SAME Pi-format store: a `pi2dsh/<provider>`\n  // key answers for that provider's stored OAuth login. Grant payloads never\n  // carry the token itself — resolution stays per-request through resolve().\n  // On the rc.8 line the base class has no record members and these concrete\n  // methods are simply extra.\n\n  private recordProviderId(key: unknown): string | undefined {\n    const value = String(key)\n    return value.startsWith('pi2dsh/') ? value.slice('pi2dsh/'.length) : undefined\n  }\n\n  async readRecord(key: CredentialKey): Promise<CredentialRecord | undefined> {\n    const providerId = this.recordProviderId(key)\n    if (providerId === undefined) return undefined\n    const stored = await storedOAuthCredential(this.oauthStore, providerId)\n    if (stored === undefined) return undefined\n    return { kind: 'grant', payload: { provider: providerId, managedBy: 'pi2dsh' } }\n  }\n\n  async describeRecord(key: CredentialKey): Promise<CredentialRecordInfo> {\n    const record = await this.readRecord(key)\n    return { configured: record !== undefined, ...(record === undefined ? {} : { kind: 'grant' as const }), writable: true }\n  }\n\n  async listRecords(): Promise<readonly CredentialRecordEntry[]> {\n    const entries: CredentialRecordEntry[] = []\n    const seen = new Set<string>()\n    const ids = [...this.extraProviders.keys(), ...builtinProviders().map(provider => provider.id)]\n    for (const providerId of ids) {\n      if (seen.has(providerId)) continue\n      seen.add(providerId)\n      if (await storedOAuthCredential(this.oauthStore, providerId) !== undefined) {\n        entries.push({ key: `pi2dsh/${providerId}` as CredentialKey, kind: 'grant' })\n      }\n    }\n    return entries\n  }\n\n  async modifyRecord(\n    key: CredentialKey,\n    mutate: (current: CredentialRecord | undefined) => Promise<CredentialRecord | undefined>,\n  ): Promise<CredentialRecord | undefined> {\n    const providerId = this.recordProviderId(key)\n    if (providerId === undefined) throw new Error(`credential record ${String(key)} is not managed by this provider`)\n    const next = await mutate(await this.readRecord(key))\n    if (next === undefined) {\n      await this.oauthStore.delete(providerId, undefined)\n    }\n    // A written grant is a registration witness; the token itself is managed\n    // by the login flow through the Pi store, so there is nothing to persist\n    // beyond what the flow already committed.\n    ;(this as unknown as { notifyRecordUpdated?(key: unknown): void }).notifyRecordUpdated?.(key)\n    return next\n  }\n\n  async deleteRecord(key: CredentialKey): Promise<void> {\n    const providerId = this.recordProviderId(key)\n    if (providerId === undefined) return\n    await this.oauthStore.delete(providerId, undefined)\n    ;(this as unknown as { notifyRecordUpdated?(key: unknown): void }).notifyRecordUpdated?.(key)\n  }\n}\n\nexport const name = 'pi2dsh-credentials-oauth'\n\nexport function apply(ctx: Context, config: PiOAuthCredentialProviderOptions = {}): void {\n  // The Service constructor registers itself as ctx.credentials.\n  void new PiOAuthCredentialProvider(ctx, config)\n}\n"],"mappings":"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"}