import { DigestStore, Currency } from '@t2000/sui-x402'; export { DigestStore, InMemoryDigestStore } from '@t2000/sui-x402'; /** Sui network the seller settles on. */ type ServeNetwork = 'mainnet' | 'testnet'; interface ServeConfig { /** The seller's Sui address — every payment settles here. Required. */ payTo: string; /** Defaults to 'mainnet'. */ network?: ServeNetwork; /** * Public base URL of the deployed app (e.g. https://api.example.com). * Used for the `resource` field in 402 challenges and discovery docs. * When omitted, the per-request URL origin is used. */ baseUrl?: string; /** * Replay/challenge store. Defaults to an in-memory store — fine for a * single long-lived process, NOT for serverless (each instance gets its * own memory). Pass an UpstashDigestStore (or any DigestStore) in * production on serverless hosts. */ store?: DigestStore; /** Human-readable service name (discovery docs, slice 2). */ name?: string; /** One-line description (discovery docs, slice 2). */ description?: string; /** * Opt-in attributed activity reporting (SPEC_T2_ACTIVITY_X402 B2). After a * SUCCESSFUL settle, serve fire-and-forgets the settlement digest to this * URL (`https://t2000.ai/api/activity/x402` — the endpoint chain-verifies * before anything is recorded). Strictly best-effort: a dead report can * never change the buyer's response. Unset = no report. * * (The old `report` option — removed 2026-08-01 — posted to the retired * mpp gateway; this posts to the receipt-backed t2000.ai ledger instead.) */ activityReportUrl?: string; /** Override the Sui fullnode gRPC URL (default: mainnet/testnet fullnode). */ rpcUrl?: string; } /** * Minimal validation contract. Accepts anything implementing * Standard Schema v1 (zod v4, valibot, arktype…) or exposing a * zod-style `safeParse`. serve has no schema dependency of its own. */ type ServeSchema = StandardSchemaLike | SafeParseSchemaLike; interface StandardSchemaLike { '~standard': { version: 1; validate: (value: unknown) => StandardSchemaResult | Promise>; }; } type StandardSchemaResult = { value: T; issues?: undefined; } | { issues: ReadonlyArray<{ message: string; /** Standard Schema path segments (plain keys or `{ key }` objects). */ path?: ReadonlyArray; }>; }; interface SafeParseSchemaLike { safeParse: (value: unknown) => { success: true; data: T; } | { success: false; error: { message?: string; }; }; } /** What a paid handler receives. */ interface HandlerContext { /** The validated request body (undefined when no .body() schema was set). */ body: TBody; /** The original Request. */ req: Request; /** * The buyer's Sui address from the structurally-verified payment payload. * Undefined on unprotected (free) routes. */ payer?: string; } /** Handlers may return a Response directly or any JSON-serializable value. */ type HandlerResult = Response | unknown; interface RouteMeta { path: string; /** Human-units USDC price string, e.g. "0.01". Undefined = free route. */ priceUsdc?: string; description?: string; /** The runtime validation schema. */ bodySchema?: ServeSchema; /** * JSON Schema of the request body, emitted into /openapi.json + /llms.txt * so buyers' agents can build request bodies without guessing (a wrong * guess against a direct seller is a paid error). zod v4: * `z.toJSONSchema(schema)`. */ inputSchema?: Record; /** * JSON Schema of the 200 response — the deliverable's TYPE contract. * Annotate fields with standard JSON Schema media hints so buyer surfaces * can render (and buyer agents can consume) the deliverable without * sniffing: `contentMediaType: "image/svg+xml"` on an SVG string, * `format: "color"` on a hex color, `contentMediaType: "text/markdown"` * on prose. Declaration-only — serve never validates responses. */ outputSchema?: Record; } /** The built route — a fetch-compatible handler plus its metadata. */ type BuiltRoute = ((req: Request) => Promise) & { meta: RouteMeta; }; interface RouteRuntime { payTo: string; network: ServeNetwork; currency: Currency; store: DigestStore; baseUrl?: string; rpcUrl?: string; /** B2 attributed reporting — unset = no report (see ServeConfig). */ activityReportUrl?: string; } interface RouteOptions { path: string; description?: string; } declare class RouteBuilder { private readonly runtime; private readonly options; private readonly register; private priceUsdc?; private bodySchema?; private inputSchema?; private outputSchema?; private isFree; constructor(runtime: RouteRuntime, options: RouteOptions, register: (route: BuiltRoute) => void); /** Charge this many USDC per call (human units, e.g. "0.01"). */ paid(priceUsdc: string): this; /** Serve without payment (health checks, previews, docs). */ unprotected(): this; /** * Validate the JSON request body. zod v4 / valibot / arktype / anything * Standard-Schema, or anything with a zod-style safeParse. * * Pass the JSON Schema as the second argument to publish it in * /openapi.json + /llms.txt (zod v4: `z.toJSONSchema(schema)`) — buyers' * agents build request bodies from it, and the catalog grades listings * without one. */ body(schema: ServeSchema, jsonSchema?: Record): RouteBuilder; /** * Declare the 200 response's JSON Schema (zod v4: `z.toJSONSchema(schema)`). * Published in /openapi.json + /llms.txt so buyer agents know what they're * buying and buyer UIs can render the deliverable by TYPE instead of * sniffing it — annotate with `contentMediaType` (e.g. "image/svg+xml", * "text/markdown") and `format: "color"` where they apply. Declaration * only; responses are never validated at runtime. */ response(jsonSchema: Record): this; handler(fn: (ctx: HandlerContext) => HandlerResult | Promise): BuiltRoute; } /** The t2000.ai attributed-activity report endpoint — ONE canonical string * (matches the sdk's DEFAULT_ACTIVITY_REPORT_URL; tests assert equality). */ declare const DEFAULT_ACTIVITY_REPORT_URL = "https://t2000.ai/api/activity/x402"; declare class Serve { private readonly runtime; /** Every route built through this instance, keyed by path (discovery). */ readonly routes: Map; readonly payTo: string; readonly network: ServeNetwork; readonly name?: string; readonly description?: string; readonly baseUrl?: string; /** Where settled payments are reported (B2) — undefined = no report. */ readonly activityReportUrl?: string; constructor(config: ServeConfig); /** Start building a route. Chain `.paid()` / `.body()` / `.handler()`. */ route(options: RouteOptions): RouteBuilder; /** * Discovery: GET handler for /openapi.json. OpenAPI 3.1 with the * `x-payment-info` pricing extension on every paid operation — the shape * x402 tooling (and any catalog built on it) indexes. * * export const GET = serve.openapi(); // app/openapi.json/route.ts */ openapi(): (req: Request) => Response; /** * Discovery: GET handler for /llms.txt — plain-text guidance agents read * to understand what the API sells, what it costs, and how to pay. * * export const GET = serve.llms(); // app/llms.txt/route.ts */ llms(): (req: Request) => Response; /** * One fetch handler for the whole app — routes + discovery docs. For * fetch-native runtimes (Bun.serve, Deno.serve, Hono, Cloudflare Workers): * * Bun.serve({ fetch: serve.fetch }); * app.all('*', (c) => serve.fetch(c.req.raw)); // Hono * * Next.js apps can skip this and export route handlers directly. */ readonly fetch: (req: Request) => Promise; /** * The command that sells this API as an x402 Service on your Agent ID, * once it is deployed. Listing is a separate, explicit step: the endpoint * is live-probed (it must answer 402 with a Sui challenge that pays YOUR * wallet) and then recorded on-chain, sponsored and gasless. * * This used to emit curls against the hosted mpp.t2000.ai catalog. That * proxy mall was purged 2026-08-01 (SPEC_T2_CLEANUP_USDC_ONLY) — there is * no central catalog to submit to. Your Agent ID IS the listing, and * buyers find it with `t2 services`. */ catalogSubmitCommand(deployedUrl?: string): string; } declare function createServe(config: ServeConfig): Serve; /** * Build a Serve from environment variables — the template-app path where * config lives in the host's env UI, not code. * * T2000_PAY_TO required — the seller's Sui address * T2000_NETWORK optional — 'mainnet' (default) | 'testnet' * T2000_BASE_URL optional — public URL of the deployed app * T2000_NAME optional — service name for discovery docs * T2000_DESCRIPTION optional — one-liner for discovery docs * T2000_ACTIVITY_REPORT_URL * optional — where settled payments are reported * (fire-and-forget x402.paid after each successful settle; * chain-verified server-side, never affects buyer * responses). DEFAULT-ON for FromEnv: unset → * https://t2000.ai/api/activity/x402 so new sellers appear * on the t2000.ai home stream + their profile's recent * activity (no /activity page — S.1370). Opt out with * `false` | `0` | `off` | `none` (case-insensitive), or * set a custom URL. (`new Serve({...})` without * activityReportUrl stays silent — only FromEnv defaults.) * KV_REST_API_URL / KV_REST_API_TOKEN * optional — enables the durable Upstash replay store * (REQUIRED in serverless production; without it replay * state is per-instance memory) * * Empty strings are treated as unset (the Vercel empty-env bug class). */ declare function createServeFromEnv(env?: Record): Serve; /** * Export a built route the way Next.js App Router needs it. * * Next dispatches ONLY the methods a route.ts exports — serve's own CORS * handling (OPTIONS → 204 with the ACAO headers) never runs if the file * exports just `POST`: the browser preflight gets Next's bare 204 with no * CORS headers, and every browser buyer (Passport, the store's Try it) * fails while CLI/server buyers — who never preflight — work fine. Fetch * runtimes mounted via a single handler (`serve.fetch`-style) don't have * this hole; it is a Next export contract, not a serve one. * * export const { POST, OPTIONS } = asNextRoute( * serve.route({ path: 'haiku' }).paid('0.01').handler(fn), * ); */ declare function asNextRoute(route: BuiltRoute): { POST: BuiltRoute; OPTIONS: BuiltRoute; }; declare function buildOpenApiDocument(serve: Serve, origin?: string): Record; declare function buildLlmsTxt(serve: Serve, origin?: string): string; interface UpstashDigestStoreOptions { url: string; token: string; /** Key TTL in seconds. Default 72h — do not lower below ~50h (see above). */ ttlSeconds?: number; } declare class UpstashDigestStore implements DigestStore { private readonly url; private readonly token; private readonly ttlSeconds; constructor(options: UpstashDigestStoreOptions); private command; has(digest: string): Promise; set(digest: string): Promise; } /** Test seam — reset module caches between cases. */ declare function __resetChainCaches(): void; /** Test seam — pre-seed chain info so tests never hit the network. */ declare function __seedChainInfo(network: ServeNetwork, chain: string, epoch: string): void; export { type BuiltRoute, DEFAULT_ACTIVITY_REPORT_URL, type HandlerContext, type HandlerResult, RouteBuilder, type RouteMeta, Serve, type ServeConfig, type ServeNetwork, type ServeSchema, UpstashDigestStore, type UpstashDigestStoreOptions, __resetChainCaches, __seedChainInfo, asNextRoute, buildLlmsTxt, buildOpenApiDocument, createServe, createServeFromEnv };