UNPKG

run402-mcp

Version:

MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.

126 lines • 5.37 kB
/** * The agent's Lightning wallet on the local machine: mint the budgeted wallet * on Run402's Hub, keep its pairing in the active profile's `wallet.json` * beside the Base key, report its balance, revoke it. Shared by `r.init` * (`run402 init lightning`) and `run402 wallets lightning …`. The pairing * secret is never returned by anything here. */ import { readWallet, saveWallet } from "../../core-dist/wallet.js"; import { LocalError } from "../errors.js"; export const LIGHTNING_MINT_TIMEOUT_MS = 45_000; /** Public facts only: never the pairing. */ export function describeLightning(localWallet, wallet = null, balance = null) { const local = localWallet?.lightning ?? null; if (!local && !wallet) return null; return { wallet_id: wallet?.wallet_id ?? local?.wallet_id ?? null, status: wallet?.status ?? (local ? "active" : null), custody: "run402_hub", custody_note: "The sats sit on Run402's Lightning Hub; this wallet is a budgeted connection to them, not self-custody.", lightning_address: wallet?.lightning_address ?? local?.lightning_address ?? null, budget_sats: wallet?.budget_sats ?? local?.budget_sats ?? null, starter_sats: wallet?.starter_sats ?? local?.starter_sats ?? null, has_pairing: Boolean(local?.nwc), minted_at: local?.minted_at ?? wallet?.activated_at ?? null, ...(balance ? balance : {}), }; } /** Balance and remaining budget over NWC, best-effort (null on any failure). */ export async function readLightningBalance(localWallet) { const nwc = localWallet?.lightning?.nwc; if (!nwc) return null; try { const { NwcWallet, closeNwcConnections } = await import("./nwc.js"); const wallet = new NwcWallet(nwc, { timeoutMs: 15_000 }); const [balance_sats, budget] = await Promise.all([wallet.getBalanceSats(), wallet.getBudgetSats().catch(() => null)]); closeNwcConnections(); return { balance_sats, budget_remaining_sats: budget && budget.totalSats !== null ? budget.totalSats - budget.usedSats : null, }; } catch { return null; } } function storePairing(localWallet, wallet) { const next = { ...localWallet, rail: "lightning", lightning: { wallet_id: wallet.wallet_id, nwc: wallet.pairing, lightning_address: wallet.lightning_address ?? null, budget_sats: wallet.budget_sats, starter_sats: wallet.starter_sats, custody: "run402_hub", minted_at: wallet.activated_at ?? new Date().toISOString(), }, }; saveWallet(next); return next; } /** * Ensure the active profile holds an active Lightning wallet. `outcome` is * `stored` (pairing just saved), `present` (already held locally), `minting` * (the platform is still minting; rerun later), `unavailable` (no Hub on this * gateway), `pairing_lost` (active, but its one-time pairing went to another * machine: revoke and mint again), or the wallet's own non-active status. */ export async function ensureLightningWallet(agent, options = {}) { let localWallet = readWallet(); if (!localWallet) { throw new LocalError("no local wallet configured; run `run402 init` first", "ensuring the Lightning wallet", { code: "LIGHTNING_WALLET_FAILED" }); } if (localWallet.lightning?.nwc) { let wallet = null; try { wallet = await agent.lightningWallet.get(); } catch { /* an offline read is fine */ } if (wallet && wallet.status !== "active") return { localWallet, wallet, outcome: wallet.status }; return { localWallet, wallet, outcome: "present" }; } let wallet; try { wallet = await agent.lightningWallet.mint({ timeoutMs: options.timeoutMs ?? LIGHTNING_MINT_TIMEOUT_MS }); } catch (err) { const code = err?.body?.code ?? err?.code; if (code === "LIGHTNING_WALLET_NOT_AVAILABLE") return { localWallet, wallet: null, outcome: "unavailable", error: err }; if (code === "LIGHTNING_WALLET_STILL_MINTING") return { localWallet, wallet: null, outcome: "minting", error: err }; throw err; } if (wallet.status === "minting") return { localWallet, wallet, outcome: "minting" }; if (wallet.status !== "active") return { localWallet, wallet, outcome: wallet.status }; if (typeof wallet.pairing !== "string") return { localWallet, wallet, outcome: "pairing_lost" }; localWallet = storePairing(localWallet, wallet); return { localWallet, wallet, outcome: "stored" }; } /** Revoke on the platform and forget the pairing locally; the rail returns to x402. */ export async function revokeLightningWallet(agent) { const localWallet = readWallet(); let wallet = null; try { wallet = await agent.lightningWallet.revoke(); } catch (err) { const code = err?.body?.code ?? err?.code; if (code !== "LIGHTNING_WALLET_NOT_FOUND") throw err; } if (localWallet) { const { lightning: _dropped, ...rest } = localWallet; saveWallet({ ...rest, rail: rest.rail === "lightning" ? "x402" : rest.rail }); } return wallet; } //# sourceMappingURL=lightning-wallet.js.map