{"version":3,"file":"bankr-api.mjs","names":[],"sources":["../../../src/services/bankr-api.ts"],"sourcesContent":["/**\n * Bankr Agent API Client — shared client for all Bankr Agent API calls.\n *\n * Every tool and service that talks to api.bankr.bot uses this module.\n * Handles authentication, error mapping, and job polling.\n *\n * Base URL: https://api.bankr.bot\n * Auth: X-API-Key header with bk_... key\n */\n\nimport { getCredentialVault } from './credential-vault.js';\nimport type {\n  BankrUserInfo,\n  BankrBalancesResponse,\n  BankrChainBalance,\n  BankrTokenBalance,\n  BankrRawBalancesResponse,\n  BankrChain,\n  BankrPromptResponse,\n  BankrJobResult,\n  BankrSignRequest,\n  BankrSignResponse,\n  BankrSubmitRequest,\n  BankrSubmitResponse,\n  BankrDeployRequest,\n  BankrDeployResponse,\n} from './bankr-types.js';\n\nimport {\n  BankrAuthError,\n  BankrAccessError,\n  BankrReadOnlyError,\n  BankrRateLimitError,\n  BankrServerError,\n} from './bankr-types.js';\n\nimport { guardedFetch } from './endpoint-allowlist.js';\n\n// ─── Configuration ───────────────────────────────────────────────────────\n\nconst BANKR_API = 'https://api.bankr.bot';\n\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\nconst DEFAULT_POLL_TIMEOUT_MS = 120_000;\n\n// ─── Thread ID Storage ──────────────────────────────────────────────────\n\nconst _threadIds = new Map<string, string>();\nconst MAX_THREAD_IDS = 200;\n\nexport function storeBankrThreadId(userId: string, threadId: string): void {\n  _threadIds.set(userId, threadId);\n  // Evict oldest entry when over limit\n  if (_threadIds.size > MAX_THREAD_IDS) {\n    const first = _threadIds.keys().next().value;\n    if (first) _threadIds.delete(first);\n  }\n}\n\nexport function getBankrThreadId(userId: string): string | undefined {\n  return _threadIds.get(userId);\n}\n\n// ─── Key Management ─────────────────────────────────────────────────────\n\nexport function getBankrApiKey(): string | null {\n  return getCredentialVault().getSecret('bankr.apiKey', 'bankr-api');\n}\n\nexport function hasBankrApi(): boolean {\n  return !!getBankrApiKey();\n}\n\nfunction requireBankrApiKey(): string {\n  const key = getBankrApiKey();\n  if (!key) {\n    throw new BankrAuthError(\n      'BANKR_API_KEY not set. Connect via /connect_bankr or set the env var.'\n    );\n  }\n  return key;\n}\n\n// ─── HTTP Helpers ────────────────────────────────────────────────────────\n\nasync function handleResponse(res: Response): Promise<any> {\n  if (res.ok) {\n    const text = await res.text();\n    if (!text) return null;\n    try {\n      return JSON.parse(text);\n    } catch {\n      return text;\n    }\n  }\n\n  // Error handling by status code\n  const body = await res.text().catch(() => '');\n\n  switch (res.status) {\n    case 401:\n      throw new BankrAuthError();\n    case 402:\n      // 402 is LLM-specific but handle gracefully\n      throw new BankrAuthError('LLM credits exhausted. Top up at bankr.bot/llm');\n    case 403: {\n      // Distinguish \"not enabled\" vs \"read-only\"\n      const lower = body.toLowerCase();\n      if (lower.includes('read-only') || lower.includes('readonly')) {\n        throw new BankrReadOnlyError();\n      }\n      throw new BankrAccessError();\n    }\n    case 429: {\n      let resetAt = Date.now() + 60_000;\n      let limit = 0;\n      let used = 0;\n      try {\n        const parsed = JSON.parse(body);\n        resetAt = parsed.resetAt ?? resetAt;\n        limit = parsed.limit ?? 0;\n        used = parsed.used ?? 0;\n      } catch {\n        // Use defaults\n      }\n      throw new BankrRateLimitError(resetAt, limit, used);\n    }\n    default:\n      if (res.status >= 500) {\n        throw new BankrServerError(res.status, body || 'Bankr API server error');\n      }\n      throw new Error(`Bankr API error ${res.status}: ${body}`);\n  }\n}\n\n// H10: Default request timeout (30s) to prevent hanging on unresponsive APIs\nconst BANKR_REQUEST_TIMEOUT_MS = 30_000;\n\nexport async function bankrGet(path: string): Promise<any> {\n  const key = requireBankrApiKey();\n  const res = await guardedFetch(`${BANKR_API}${path}`, {\n    method: 'GET',\n    headers: {\n      'X-API-Key': key,\n      'Content-Type': 'application/json',\n    },\n    signal: AbortSignal.timeout(BANKR_REQUEST_TIMEOUT_MS),\n  });\n  return handleResponse(res);\n}\n\nexport async function bankrPost(path: string, body: unknown): Promise<any> {\n  const key = requireBankrApiKey();\n  const res = await guardedFetch(`${BANKR_API}${path}`, {\n    method: 'POST',\n    headers: {\n      'X-API-Key': key,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify(body),\n    signal: AbortSignal.timeout(BANKR_REQUEST_TIMEOUT_MS),\n  });\n  return handleResponse(res);\n}\n\n// ─── User Info ───────────────────────────────────────────────────────────\n\nexport async function getBankrUserInfo(): Promise<BankrUserInfo> {\n  const raw = await bankrGet('/agent/me');\n  // API returns the full response — extract the relevant fields\n  return {\n    wallets: raw.wallets ?? [],\n    bankrClub: raw.bankrClub ?? false,\n    socialAccounts: raw.socialAccounts ?? [],\n    refCode: raw.refCode,\n  };\n}\n\n// ─── Balances ────────────────────────────────────────────────────────────\n\nexport async function getBankrBalances(chains?: BankrChain[]): Promise<BankrBalancesResponse> {\n  const params = chains?.length ? `?chains=${chains.join(',')}` : '';\n  const raw: BankrRawBalancesResponse = await bankrGet(`/agent/balances${params}`);\n\n  // Normalize the nested-object response into our flat array format\n  const normalizedChains: BankrChainBalance[] = [];\n  let grandTotal = 0;\n\n  if (raw.balances) {\n    for (const [chainName, chainData] of Object.entries(raw.balances)) {\n      const tokens: BankrTokenBalance[] = (chainData.tokenBalances ?? []).map(entry => ({\n        symbol: entry.token?.baseToken?.symbol ?? 'UNKNOWN',\n        name: entry.token?.baseToken?.name ?? 'Unknown',\n        address: entry.address ?? entry.token?.baseToken?.address ?? '',\n        balance: entry.token?.balance ?? 0,\n        balanceUsd: entry.token?.balanceUSD ?? 0,\n        price: entry.token?.baseToken?.price ?? 0,\n        decimals: entry.token?.baseToken?.decimals ?? 18,\n      }));\n\n      const totalUsd = parseFloat(chainData.total ?? '0');\n      grandTotal += totalUsd;\n\n      normalizedChains.push({\n        chain: chainName,\n        nativeBalance: chainData.nativeBalance ?? '0',\n        nativeBalanceUsd: parseFloat(chainData.nativeUsd ?? '0'),\n        tokens,\n        totalUsd,\n      });\n    }\n  }\n\n  return { chains: normalizedChains, totalUsd: grandTotal };\n}\n\n// ─── Prompt + Job Polling ────────────────────────────────────────────────\n\nexport async function bankrPrompt(\n  prompt: string,\n  threadId?: string,\n): Promise<BankrPromptResponse> {\n  const body: Record<string, unknown> = { prompt };\n  if (threadId) body.threadId = threadId;\n  return bankrPost('/agent/prompt', body);\n}\n\n/**\n * Poll a Bankr job until it completes or fails.\n * Default: poll every 2s, timeout after 120s.\n */\nexport async function bankrPollJob(\n  jobId: string,\n  timeoutMs = DEFAULT_POLL_TIMEOUT_MS,\n  intervalMs = DEFAULT_POLL_INTERVAL_MS,\n): Promise<BankrJobResult> {\n  const deadline = Date.now() + timeoutMs;\n\n  while (Date.now() < deadline) {\n    const result: BankrJobResult = await bankrGet(`/agent/job/${jobId}`);\n\n    if (result.status === 'completed' || result.status === 'failed' || result.status === 'cancelled') {\n      return result;\n    }\n\n    // Wait before next poll\n    await new Promise(resolve => setTimeout(resolve, intervalMs));\n  }\n\n  // Timed out — try to cancel\n  try {\n    await bankrCancelJob(jobId);\n  } catch {\n    // Best-effort cancel\n  }\n\n  return {\n    jobId,\n    status: 'failed',\n    error: `Job timed out after ${Math.round(timeoutMs / 1000)}s. The operation may still be processing on Bankr's side.`,\n  };\n}\n\n/**\n * Submit a prompt and poll until completion. Convenience wrapper.\n */\nexport async function bankrPromptAndPoll(\n  prompt: string,\n  opts?: { threadId?: string; timeoutMs?: number },\n): Promise<BankrJobResult> {\n  const { jobId, threadId } = await bankrPrompt(prompt, opts?.threadId);\n  // Store threadId for conversation continuity if a user context exists\n  const result = await bankrPollJob(jobId, opts?.timeoutMs);\n  // Attach threadId to result for callers that need it\n  (result as any).threadId = threadId;\n  return result;\n}\n\nexport async function bankrCancelJob(jobId: string): Promise<void> {\n  await bankrPost(`/agent/job/${jobId}/cancel`, {});\n}\n\n// ─── Sign ────────────────────────────────────────────────────────────────\n\nexport async function bankrSign(req: BankrSignRequest): Promise<BankrSignResponse> {\n  return bankrPost('/agent/sign', req);\n}\n\n// ─── Submit Raw Transaction ──────────────────────────────────────────────\n\nexport async function bankrSubmit(req: BankrSubmitRequest): Promise<BankrSubmitResponse> {\n  return bankrPost('/agent/submit', req);\n}\n\n// ─── Token Deploy ────────────────────────────────────────────────────────\n\nexport async function bankrDeployToken(opts: BankrDeployRequest): Promise<BankrDeployResponse> {\n  return bankrPost('/token-launches/deploy', opts);\n}\n"],"mappings":"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"}