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.
326 lines • 15.4 kB
TypeScript
/**
* `org` namespace — the org-owned control plane (gateway v1.77+, first-class in
* v1.82). Mirrors the `r.projects` / `r.project(id)` idiom:
*
* - `r.orgs` — collection + identity: `create`, `list`, `whoami`.
* - `r.org(id)` — a resource-scoped sub-client (id pre-bound): `get`,
* `rename`, `members.*`, `invites.*`, `audit`.
*
* All routes use the existing SIWX (or control-plane-session) auth; authorization
* is a membership lookup, never `wallet == signer`. Mutations (`create`,
* `rename`, member/invite changes) are step-up gated server-side and may return
* `STEP_UP_REQUIRED` when driven by a stale control-plane session.
*/
import type { Client } from "../kernel.js";
import type { AddMemberInput, AuditOptions, AuditResult, SetOrgSlugResult, CreateInviteInput, CreateOrgInput, MemberMutationResult, MemberRevokeResult, MemberRevokeEncryptionKeyResult, OrgDetail, OrgInvite, OrgInviteRevokeResult, OrgMember, OrgMembership, OrgSummary, SetPayoutWalletInput, SetPayoutWalletResult, SetMemberRoleOptions, WhoAmIResult } from "./org.types.js";
/** Member management — `r.org(id).members.*`. The org id is bound at construction. */
export declare class OrgMembers {
private readonly client;
private readonly orgId;
constructor(client: Client, orgId: string);
/** List members of the org (`GET /orgs/v1/:org_id/members`). Any active member. */
list(): Promise<OrgMember[]>;
/**
* Add a member by wallet (`POST /orgs/v1/:org_id/members`). Requires an active
* `owner` membership. A brand-new wallet is provisioned as a `human` principal;
* `role` defaults to `"developer"` server-side.
*/
add(input: AddMemberInput): Promise<MemberMutationResult>;
/**
* Change a member's role (`PATCH …/members/:principal_id`). Requires `owner`;
* demoting the org's only active owner fails with `409 LAST_OWNER`.
*/
setRole(principalId: string, opts: SetMemberRoleOptions): Promise<MemberMutationResult>;
/**
* Revoke a member (`DELETE …/members/:principal_id`) — one row, status
* `revoked`, no key rotation. Requires `owner`; revoking the org's only active
* owner fails with `409 LAST_OWNER`.
*/
revoke(principalId: string): Promise<MemberRevokeResult>;
/**
* vault-agent-envelopes D3 — an OWNER revokes a member's current vault
* encryption key (`DELETE /orgs/v1/:org_id/members/:principal_id/encryption-key`,
* owner + step-up). The independent-credential rotation path: a member whose
* keystore was lost or rebuilt cannot self-rotate (the asking credential is
* the one a thief would hold); after this, the member's next vault
* operation enrolls its current keystore key afresh and a key-holder's next
* operation wraps each vault to it. Audited, org-feed row + mandatory
* security notification (`gitvault_encryption_key_revoked`).
*/
revokeEncryptionKey(principalId: string, input?: {
reason?: string;
}): Promise<MemberRevokeEncryptionKeyResult>;
}
/** Email-invite management — `r.org(id).invites.*`. The org id is bound at construction. */
export declare class OrgInvites {
private readonly client;
private readonly orgId;
constructor(client: Client, orgId: string);
/** List pending email invites (`GET /orgs/v1/:org_id/invites`). Any active member. */
list(): Promise<OrgInvite[]>;
/**
* Invite a person by email (`POST …/invites`); claimed at their first login.
* Requires `owner` (plus step-up when driven by a control-plane session).
*/
create(input: CreateInviteInput): Promise<OrgInvite>;
/** Revoke a pending invite by its pending principal id (`DELETE …/invites/:principal_id`). Requires `owner`. */
revoke(principalId: string): Promise<OrgInviteRevokeResult>;
}
/**
* A resource-scoped org sub-client returned by `r.org(id)`. The org id is bound
* at construction; instance operations take no repeated id argument. Mirrors the
* project-scoped `r.project(id)` shape (narrower scope).
*/
export declare class ScopedOrg {
private readonly client;
/** Member management (`r.org(id).members.*`). */
readonly members: OrgMembers;
/** Email-invite management (`r.org(id).invites.*`). */
readonly invites: OrgInvites;
/** The org id this sub-client is bound to. Read-only. */
readonly orgId: string;
constructor(client: Client, orgId: string);
/**
* Read this org (`GET /orgs/v1/:org_id`) — `{ org_id, display_name, tier,
* lease_started_at, lease_expires_at, role }`.
* Any active member may view; a non-member (including a guessed id) gets the
* same non-revealing `403`.
*/
get(): Promise<OrgDetail>;
/**
* Rename this org (`PATCH /orgs/v1/:org_id`). Owner-only + step-up gated. Pass
* `null` or `""` to clear the label. Returns the updated `{ org_id,
* display_name, tier, lease_started_at, lease_expires_at }`.
*/
rename(displayName: string | null): Promise<OrgSummary>;
/**
* Claim or rename this org's address-form slug
* (`POST /orgs/v1/:org_id/slug`, repo-first-onramp design D6). Owner-only.
* A genesis claim (this org had no prior slug) debits `~$1` (the payable
* claim fee, ruins bulk-squatting economics) and is a paid, side-effecting
* mutation — REQUIRES `Idempotency-Key`, which this method generates
* client-side (a fresh one per call) unless `opts.idempotencyKey` is
* supplied, so a retried call after a dropped response cannot double-bill.
* A rename releases the OLD slug into its ~90-day cooldown (typed
* `SLUG_RELEASED` refusal on the old slug thereafter, naming this org's
* new one as successor — never a redirect). `created` is `true` for a
* genesis claim, `false` for a rename or an idempotent no-op replay of the
* SAME target slug.
*/
setSlug(slug: string, opts?: {
idempotencyKey?: string;
}): Promise<SetOrgSlugResult>;
/**
* Set or clear the default payout wallet for tenant priced routes
* (`PATCH /orgs/v1/:org_id/payout-wallet`). Requires org admin/owner plus
* server-side step-up or fresh SIWX. The wallet must already be active and
* linked to this org; pass `null` to clear the explicit default.
*/
setPayoutWallet(input: SetPayoutWalletInput): Promise<SetPayoutWalletResult>;
/**
* Control-plane audit trail for this org (`GET /orgs/v1/:org_id/audit`).
* admin+. Newest-first; page forward with the `after` keyset cursor
* (`next_cursor` from a prior page) — the legacy `before` cursor is still
* accepted. Returns `{ events, has_more, next_cursor }`.
*/
audit(opts?: AuditOptions): Promise<AuditResult>;
}
/**
* Org collection + identity — `r.orgs.*`. Operations that are not bound to a
* single org: create a new org, list the caller's orgs, resolve the principal.
*/
/**
* An adopt challenge (`POST /orgs/v1/adopt/challenge`). The wallet must sign a
* fresh SIWX message carrying {@link AdoptChallenge.nonce}; that signed message
* becomes the `SIGN-IN-WITH-X` header on {@link OrgAdopt.submit}. Reveals
* nothing about the wallet's orgs — control is proven only at adopt time.
*/
export interface AdoptChallenge {
challenge_id: string;
nonce: string;
expires_at: string;
sign_instructions?: {
scheme?: string;
nonce?: string;
note?: string;
[key: string]: unknown;
};
[key: string]: unknown;
}
/** Input to {@link OrgAdopt.challenge}. */
export interface AdoptChallengeInput {
/** The wallet (0x EVM address) whose agent-owned org is being adopted. */
wallet: string;
/**
* The human's write-capable control-plane session bearer. Falls back to the
* client's default auth when omitted — pass it explicitly unless the client was
* constructed with control-plane-session credentials.
*/
token?: string;
}
/** Input to {@link OrgAdopt.submit}. */
export interface AdoptSubmitInput {
/**
* The fresh SIWX proof over the challenge nonce — the value of the
* `SIGN-IN-WITH-X` header (the wallet proof). In Node, build it with
* `signOrgAdopt` from `@run402/sdk/node`.
*/
siwx: string;
/** The human's control-plane session bearer (see {@link AdoptChallengeInput.token}). */
token?: string;
/**
* Target org id. Omit on the first submit; supply it on the second round when
* the first returned `select_org` (the wallet's agent owns more than one org).
* The same `token` + `siwx` are reused — no re-challenge, no re-sign.
*/
orgId?: string;
/** Optional label to set on the adopted org at the same time. `null`/`""` clears. */
displayName?: string | null;
}
/** One org offered for selection when a wallet's agent owns more than one (`select_org`). */
export interface SelectableOrg {
org_id: string;
display_name: string | null;
tier: string;
[key: string]: unknown;
}
/**
* Result of {@link OrgAdopt.submit}. A discriminated union: `"adopted"` on
* success, or `"select_org"` when the wallet's agent owns more than one org and
* the caller must re-submit with a chosen `orgId`. `select_org` is a normal
* (non-error) result — it is returned, never thrown.
*/
export type AdoptResult = {
status: "adopted";
org_id: string;
display_name: string | null;
role: string;
/** True when the human already owned the org (idempotent re-adopt). */
already_owned?: boolean;
[key: string]: unknown;
} | {
status: "select_org";
selectable_orgs: SelectableOrg[];
[key: string]: unknown;
};
/**
* Adopt the org your wallet's agent owns — `r.orgs.adopt.*`. The isomorphic
* (raw-proof) seam: `challenge` issues a nonce; `submit` posts the dual proof
* (control-plane session bearer + a fresh `SIGN-IN-WITH-X` wallet signature). The
* Node convenience `signOrgAdopt` / `adoptOrg` in `@run402/sdk/node`
* runs the whole dance (read session → challenge → sign → submit).
*/
export declare class OrgAdopt {
private readonly client;
constructor(client: Client);
/** Request a single-use challenge nonce the wallet must sign (`POST /orgs/v1/adopt/challenge`). */
challenge(input: AdoptChallengeInput): Promise<AdoptChallenge>;
/**
* Execute the adopt (`POST /orgs/v1/adopt`) carrying both proofs: the
* control-plane session bearer (the human) and the `SIGN-IN-WITH-X` wallet
* signature. Returns a discriminated {@link AdoptResult}; a `select_org` result
* is returned (not thrown). Throws {@link StepUpRequiredError} when the session
* is not passkey-fresh, and `ApiError` (`WALLET_PROOF_INVALID`) on a bad proof.
*/
submit(input: AdoptSubmitInput): Promise<AdoptResult>;
}
/** The organization's receive-only Lightning receiver, as the gateway reports it (never the pairing). */
export interface LightningReceiver {
org_id: string;
receiver_id: string;
/** The node's compressed public key (hex): every Lightning entry's `payTo`. */
node_pubkey: string;
/** `lnbtc:000000000019d6689c085ae165831e93` (mainnet) or `lnbtc:000000000933ea01ad0ee984209779ba` (testnet). */
network: string;
methods: string[];
probed_at: string;
last_probe_status: "ok" | "failing";
last_error: string | null;
custody: "organization";
created_at: string;
updated_at: string;
}
export interface SetLightningReceiverInput {
orgId: string;
/** A `nostr+walletconnect://` pairing permitting `get_info` and `make_invoice` and no spending method. */
nwcUri: string;
}
export interface LightningReceiverRemoved {
status: "removed";
org_id: string;
receiver_id: string;
node_pubkey: string;
network: string;
warnings: Array<{
code: "LIGHTNING_ROUTES_STILL_ACTIVE";
project_id: string;
release_id: string;
route_patterns: string[];
message: string;
}>;
}
/**
* `r.orgs.lightningReceiver.*`: the organization's bring-your-own, receive-only
* Lightning receiver for routes priced on `lightning` / `lightning-testnet`.
* The sats land on the organization's own node; Run402 holds a pairing that
* can only mint invoices, never a preimage. `set` consumes a secret and is
* CLI-only on the MCP surface (`run402 orgs lightning-receiver set`).
*/
export declare class OrgLightningReceiver {
private readonly client;
constructor(client: Client);
/**
* Register or replace the receiver (`POST /orgs/v1/:org_id/lightning-receiver`).
* Org admin/owner plus step-up. The gateway probes the pairing before storing
* it: a pairing that can spend, or whose test invoice does not honor a
* description hash, is refused with `LIGHTNING_RECEIVER_PROBE_FAILED` and
* `details.check`. A replacement rotates `receiver_id`.
*/
set(input: SetLightningReceiverInput): Promise<LightningReceiver>;
/** The receiver's non-secret facts and health (`GET …/lightning-receiver`); any active member. */
get(input: {
orgId: string;
}): Promise<LightningReceiver>;
/** Delete the receiver (`DELETE …/lightning-receiver`); admin/owner plus step-up. Warns about releases still priced on Lightning. */
delete(input: {
orgId: string;
}): Promise<LightningReceiverRemoved>;
}
export declare class Orgs {
private readonly client;
/**
* Adopt the org your wallet's agent owns: `r.orgs.adopt.challenge()` +
* `.submit()`. The raw dual-proof seam; the Node convenience `adoptOrg` in
* `@run402/sdk/node` runs the full dance.
*/
readonly adopt: OrgAdopt;
/** The organization's receive-only Lightning receiver for routes priced on Lightning: `r.orgs.lightningReceiver.set/get/remove`. */
readonly lightningReceiver: OrgLightningReceiver;
constructor(client: Client);
/**
* Create an empty org on the `prototype` tier (`POST /orgs/v1`); the caller
* becomes `owner`. Accepts only an optional `displayName` — there is no tier
* input at create. The response reports the created org's tier/lease state.
* Step-up gated; the soft per-owner free-org cap answers 403
* `FREE_ORG_OWNER_LIMIT_EXCEEDED` with `next_actions` (a policy refusal,
* never a 429 — raised as {@link Unauthorized} carrying that `code`).
*/
create(input?: CreateOrgInput): Promise<OrgSummary>;
/** List the orgs the caller is an active member of (`GET /orgs/v1`), as memberships. */
list(): Promise<OrgMembership[]>;
/**
* Resolve the caller's control-plane principal and its org memberships
* (`GET /agent/v1/whoami`). This is the REMOTE identity; for the local,
* network-free wallet/profile identity use `r.whoami()`.
*/
whoami(): Promise<WhoAmIResult>;
/**
* Set the caller's own display name (`PATCH /agent/v1/me`) — the name
* promotion credit (`hand_to_member.credited_as`), `up`'s room presence,
* and audit surfaces show for this principal. 1–64 characters after
* trimming, no control characters. Self-only: a grant key names the
* principal it acts for. Returns the same body `whoami()` returns.
*/
setDisplayName(displayName: string): Promise<WhoAmIResult>;
}
//# sourceMappingURL=org.d.ts.map