{"version":3,"file":"keyringTokenStore-LBsDb_eC.cjs","names":["FileTokenStore"],"sources":["../src/services/keyringTokenStore/index.ts"],"sourcesContent":["import { type AuthEntry, FileTokenStore, type TokenStore } from '@agimon-ai/mcp-proxy';\n\n/** Keyring service name. One credential per downstream MCP server. */\nexport const KEYRING_SERVICE = 'ai.agimon.doom-mcp.oauth';\n\n/** The slice of `@napi-rs/keyring`'s `AsyncEntry` this store uses. */\nexport interface KeyringEntry {\n  getPassword(): Promise<string | undefined>;\n  setPassword(password: string): Promise<void>;\n  deletePassword(): Promise<unknown>;\n}\n\nexport type KeyringEntryFactory = (service: string, account: string) => KeyringEntry;\n\n/**\n * OAuth credentials in the OS keyring, with an owner-only file store behind it.\n *\n * The keyring is a native module and the platform service it talks to may be absent\n * (a headless Linux box with no secret service, an unsupported architecture). A\n * missing credential is reported as `undefined` rather than raised, so a rejection\n * means the keyring itself is unusable and the store degrades to the file fallback\n * for the rest of the session instead of failing the run.\n */\nexport class KeyringTokenStore implements TokenStore {\n  private degraded = false;\n\n  constructor(\n    private readonly createEntry: KeyringEntryFactory,\n    private readonly fallback: TokenStore = new FileTokenStore(),\n  ) {}\n\n  /** True once the keyring has failed and the file store has taken over. */\n  get isDegraded(): boolean {\n    return this.degraded;\n  }\n\n  async read(serverName: string): Promise<AuthEntry | undefined> {\n    if (this.degraded) return this.fallback.read(serverName);\n    let raw: string | undefined;\n    try {\n      raw = await this.createEntry(KEYRING_SERVICE, serverName).getPassword();\n    } catch {\n      this.degraded = true;\n      return this.fallback.read(serverName);\n    }\n    if (!raw) return undefined;\n    try {\n      return JSON.parse(raw) as AuthEntry;\n    } catch {\n      // A credential we cannot parse is treated as absent so the next authorization\n      // overwrites it, rather than wedging the server on every start.\n      return undefined;\n    }\n  }\n\n  async write(serverName: string, entry: AuthEntry): Promise<void> {\n    if (!this.degraded) {\n      try {\n        await this.createEntry(KEYRING_SERVICE, serverName).setPassword(JSON.stringify(entry));\n        return;\n      } catch {\n        this.degraded = true;\n      }\n    }\n    await this.fallback.write(serverName, entry);\n  }\n\n  /**\n   * Removes the credential from both stores.\n   *\n   * A session that degraded part way through can have left a copy in either place,\n   * and \"signed out\" has to mean no credential survives anywhere. A keyring that\n   * refuses the delete is reported rather than absorbed: the caller was told the\n   * credential is gone, and it is not. The fallback is still cleared first so the\n   * failure leaves one surviving copy instead of two.\n   */\n  async clear(serverName: string): Promise<void> {\n    let keyringFailure: unknown;\n    if (!this.degraded) {\n      try {\n        await this.createEntry(KEYRING_SERVICE, serverName).deletePassword();\n      } catch (error) {\n        keyringFailure = error;\n      }\n    }\n    await this.fallback.clear(serverName);\n    if (keyringFailure !== undefined) {\n      const detail = keyringFailure instanceof Error ? keyringFailure.message : JSON.stringify(keyringFailure);\n      throw new Error(`Keyring credential for \"${serverName}\" could not be removed: ${detail}`, {\n        cause: keyringFailure,\n      });\n    }\n  }\n}\n\n/**\n * Builds the token store for this host, falling back when the native module is absent.\n *\n * doom-mcp loads inside Pi, so a platform without a prebuilt keyring binary must\n * still get a working session rather than a failed extension install.\n */\nexport async function createTokenStore(fallback: TokenStore = new FileTokenStore()): Promise<TokenStore> {\n  try {\n    const { AsyncEntry } = await import('@napi-rs/keyring');\n    return new KeyringTokenStore((service, account) => new AsyncEntry(service, account), fallback);\n  } catch {\n    return fallback;\n  }\n}\n"],"mappings":";;;;;;;;;AAGA,MAAa,kBAAkB;;;;;;;;;;AAoB/B,IAAa,oBAAb,MAAqD;CAIhC;CACA;CAJnB,WAAmB;CAEnB,YACE,aACA,WAAwC,IAAIA,qBAAAA,eAAe,GAC3D;EAFiB,KAAA,cAAA;EACA,KAAA,WAAA;CAChB;;CAGH,IAAI,aAAsB;EACxB,OAAO,KAAK;CACd;CAEA,MAAM,KAAK,YAAoD;EAC7D,IAAI,KAAK,UAAU,OAAO,KAAK,SAAS,KAAK,UAAU;EACvD,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,KAAK,YAAY,iBAAiB,UAAU,CAAC,CAAC,YAAY;EACxE,QAAQ;GACN,KAAK,WAAW;GAChB,OAAO,KAAK,SAAS,KAAK,UAAU;EACtC;EACA,IAAI,CAAC,KAAK,OAAO,KAAA;EACjB,IAAI;GACF,OAAO,KAAK,MAAM,GAAG;EACvB,QAAQ;GAGN;EACF;CACF;CAEA,MAAM,MAAM,YAAoB,OAAiC;EAC/D,IAAI,CAAC,KAAK,UACR,IAAI;GACF,MAAM,KAAK,YAAY,iBAAiB,UAAU,CAAC,CAAC,YAAY,KAAK,UAAU,KAAK,CAAC;GACrF;EACF,QAAQ;GACN,KAAK,WAAW;EAClB;EAEF,MAAM,KAAK,SAAS,MAAM,YAAY,KAAK;CAC7C;;;;;;;;;;CAWA,MAAM,MAAM,YAAmC;EAC7C,IAAI;EACJ,IAAI,CAAC,KAAK,UACR,IAAI;GACF,MAAM,KAAK,YAAY,iBAAiB,UAAU,CAAC,CAAC,eAAe;EACrE,SAAS,OAAO;GACd,iBAAiB;EACnB;EAEF,MAAM,KAAK,SAAS,MAAM,UAAU;EACpC,IAAI,mBAAmB,KAAA,GAAW;GAChC,MAAM,SAAS,0BAA0B,QAAQ,eAAe,UAAU,KAAK,UAAU,cAAc;GACvG,MAAM,IAAI,MAAM,2BAA2B,WAAW,0BAA0B,UAAU,EACxF,OAAO,eACT,CAAC;EACH;CACF;AACF;;;;;;;AAQA,eAAsB,iBAAiB,WAAuB,IAAIA,qBAAAA,eAAe,GAAwB;CACvG,IAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EACpC,OAAO,IAAI,mBAAmB,SAAS,YAAY,IAAI,WAAW,SAAS,OAAO,GAAG,QAAQ;CAC/F,QAAQ;EACN,OAAO;CACT;AACF"}