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.

65 lines • 2.89 kB
/** * `writeApproval` — the passkey-signed, target-scoped credential a person * signed in on the command line needs to provision, deploy, or write secrets * without a wallet. Reached as `r.writeApproval.*`. * * The ceremony: {@link WriteApproval.requestChallenge} opens a passkey approval * scoped to one `(action, target)` and returns a same-origin `confirm_url`; the * person approves with their passkey in the browser; the confirm page redirects * to the CLI's `127.0.0.1` loopback with a one-time `code`; * {@link WriteApproval.exchangeClaimCode} (PKCE) mints the token. The token is * sent as `X-Run402-Write-Approval: Bearer <write_approval_token>` beside the * sign-in session bearer, and dies with that session. A write approval never * satisfies step-up. * * Isomorphic seams — the loopback server and PKCE generation live in the Node * CLI (`run402 approve`). */ import { gateSecret } from "../secret-gate.js"; export class WriteApproval { client; constructor(client) { this.client = client; } /** * Open a scoped approval (`POST /agent/v1/control-plane/write-approval/challenges`). * Carries the sign-in session bearer; a `device`-grade session is refused. */ async requestChallenge(input) { gateSecret(this.client, "writeApproval.requestChallenge", input); const body = { action: input.action, cli_redirect_uri: input.cliRedirectUri, code_challenge: input.codeChallenge, state: input.state, }; if (input.orgId !== undefined) body.org_id = input.orgId; if (input.projectId !== undefined) body.project_id = input.projectId; return this.client.request("/agent/v1/control-plane/write-approval/challenges", { method: "POST", body, ...(input.token ? { headers: { Authorization: `Bearer ${input.token}` }, withAuth: false } : {}), context: "requesting a write approval challenge", }); } /** * Exchange the loopback claim `code` (+ PKCE verifier + `state`) for the * write-approval token (`POST …/write-approval/cli/token`). No `redirect_uri` * — it is bound at challenge time. Unauthenticated (code + verifier are the * credential); the minted token is still bound to the issuing sign-in session. */ async exchangeClaimCode(input) { gateSecret(this.client, "writeApproval.exchangeClaimCode", input); return this.client.request("/agent/v1/control-plane/write-approval/cli/token", { method: "POST", body: { code: input.code, code_verifier: input.codeVerifier, state: input.state }, withAuth: false, context: "exchanging a write approval claim code", }); } } //# sourceMappingURL=write-approval.js.map