@paybond/kit 0.11.10 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +2 -2
  2. package/completion-presets/catalog.json +374 -3
  3. package/completion-presets/catalog.sha256 +1 -1
  4. package/dist/agent/facade.js +13 -0
  5. package/dist/agent/guarded-agent.d.ts +1 -1
  6. package/dist/agent/guarded-agent.js +19 -0
  7. package/dist/agent/index.d.ts +2 -1
  8. package/dist/agent/index.js +1 -0
  9. package/dist/agent/instrument.d.ts +6 -0
  10. package/dist/agent/instrument.js +6 -0
  11. package/dist/agent/interceptor.d.ts +9 -0
  12. package/dist/agent/interceptor.js +177 -1
  13. package/dist/agent/receipt-client.d.ts +49 -0
  14. package/dist/agent/receipt-client.js +45 -0
  15. package/dist/agent/registry.js +1 -0
  16. package/dist/agent/run.d.ts +2 -0
  17. package/dist/agent/run.js +52 -0
  18. package/dist/agent/types.d.ts +70 -0
  19. package/dist/agent-receipt-external-attestations.d.ts +29 -0
  20. package/dist/agent-receipt-external-attestations.js +124 -0
  21. package/dist/agent-receipt.d.ts +134 -0
  22. package/dist/agent-receipt.js +580 -0
  23. package/dist/agent-recognition.d.ts +25 -0
  24. package/dist/agent-recognition.js +36 -0
  25. package/dist/audit/exports.d.ts +72 -0
  26. package/dist/audit/exports.js +185 -0
  27. package/dist/audit/index.d.ts +3 -0
  28. package/dist/audit/index.js +3 -0
  29. package/dist/audit/verify.d.ts +8 -0
  30. package/dist/audit/verify.js +113 -0
  31. package/dist/audit/wire.d.ts +54 -0
  32. package/dist/audit/wire.js +80 -0
  33. package/dist/cli/agent/demo-loaders.d.ts +2 -0
  34. package/dist/cli/agent/demo-loaders.js +24 -0
  35. package/dist/cli/agent/production-evidence.d.ts +11 -0
  36. package/dist/cli/agent/production-evidence.js +17 -0
  37. package/dist/cli/audit-export.d.ts +2 -7
  38. package/dist/cli/audit-export.js +2 -120
  39. package/dist/cli/command-spec.js +43 -10
  40. package/dist/cli/commands/agent.d.ts +3 -0
  41. package/dist/cli/commands/agent.js +143 -1
  42. package/dist/cli/commands/discovery.js +39 -36
  43. package/dist/cli/commands/workflows.js +116 -18
  44. package/dist/cli/help.d.ts +1 -1
  45. package/dist/cli/help.js +12 -8
  46. package/dist/cli/intents-harbor-mutation.d.ts +18 -0
  47. package/dist/cli/intents-harbor-mutation.js +33 -0
  48. package/dist/cli/paybond.d.ts +9 -0
  49. package/dist/cli/paybond.js +23 -0
  50. package/dist/cli/redact.js +3 -0
  51. package/dist/cloudflare-agents/config.d.ts +24 -0
  52. package/dist/cloudflare-agents/config.js +23 -0
  53. package/dist/cloudflare-agents/index.d.ts +3 -0
  54. package/dist/cloudflare-agents/index.js +3 -0
  55. package/dist/cloudflare-agents/peer.d.ts +9 -0
  56. package/dist/cloudflare-agents/peer.js +20 -0
  57. package/dist/cloudflare-agents/sandbox-demo.d.ts +36 -0
  58. package/dist/cloudflare-agents/sandbox-demo.js +89 -0
  59. package/dist/completion-catalog-digest.d.ts +1 -1
  60. package/dist/completion-catalog-digest.js +1 -1
  61. package/dist/completion-init.js +8 -3
  62. package/dist/dev/offline-gateway.d.ts +2 -1
  63. package/dist/dev/offline-gateway.js +34 -3
  64. package/dist/dev/x402-fund-mock.d.ts +28 -0
  65. package/dist/dev/x402-fund-mock.js +124 -0
  66. package/dist/index.d.ts +125 -9
  67. package/dist/index.js +264 -17
  68. package/dist/init.js +113 -2
  69. package/dist/mastra/config.d.ts +30 -0
  70. package/dist/mastra/config.js +58 -0
  71. package/dist/mastra/index.d.ts +2 -0
  72. package/dist/mastra/index.js +2 -0
  73. package/dist/mastra/peer.d.ts +11 -0
  74. package/dist/mastra/peer.js +24 -0
  75. package/dist/mastra/sandbox-demo.d.ts +36 -0
  76. package/dist/mastra/sandbox-demo.js +87 -0
  77. package/dist/mcp/index.d.ts +1 -0
  78. package/dist/mcp/index.js +1 -0
  79. package/dist/mcp/sandbox-demo.d.ts +38 -0
  80. package/dist/mcp/sandbox-demo.js +112 -0
  81. package/dist/mcp-receipt-resource.d.ts +10 -0
  82. package/dist/mcp-receipt-resource.js +32 -0
  83. package/dist/mcp-server.d.ts +6 -0
  84. package/dist/mcp-server.js +124 -1
  85. package/dist/mpp-commercial.d.ts +19 -0
  86. package/dist/mpp-commercial.js +34 -0
  87. package/dist/mpp-funding.d.ts +71 -0
  88. package/dist/mpp-funding.js +192 -0
  89. package/dist/payment-transport.d.ts +30 -0
  90. package/dist/payment-transport.js +56 -0
  91. package/dist/policy/init.js +2 -0
  92. package/dist/policy/intent-spec.js +2 -0
  93. package/dist/principal-intent.d.ts +1 -1
  94. package/dist/principal-intent.js +4 -1
  95. package/dist/project-init.js +8 -0
  96. package/dist/template-init.d.ts +2 -2
  97. package/dist/template-init.js +6 -2
  98. package/dist/x402-funding.d.ts +52 -0
  99. package/dist/x402-funding.js +124 -0
  100. package/package.json +20 -2
  101. package/templates/manifest.json +28 -9
  102. package/templates/openai-shopping-agent/package-lock.json +16 -8
  103. package/templates/openai-shopping-agent/package.json +1 -1
  104. package/templates/paybond-aws-operator/package-lock.json +13 -5
  105. package/templates/paybond-aws-operator/package.json +1 -1
  106. package/templates/paybond-claude-agents-demo/package-lock.json +16 -8
  107. package/templates/paybond-claude-agents-demo/package.json +1 -1
  108. package/templates/paybond-invoice-agent/requirements.txt +1 -1
  109. package/templates/paybond-mastra-travel-agent/.env.example +3 -0
  110. package/templates/paybond-mastra-travel-agent/.github/workflows/smoke.yml +20 -0
  111. package/templates/paybond-mastra-travel-agent/LICENSE +201 -0
  112. package/templates/paybond-mastra-travel-agent/README.md +29 -0
  113. package/templates/paybond-mastra-travel-agent/package-lock.json +3377 -0
  114. package/templates/paybond-mastra-travel-agent/package.json +22 -0
  115. package/templates/paybond-mastra-travel-agent/paybond.policy.yaml +22 -0
  116. package/templates/paybond-mastra-travel-agent/src/index.ts +22 -0
  117. package/templates/paybond-mastra-travel-agent/src/paybond.config.ts +51 -0
  118. package/templates/paybond-mastra-travel-agent/tsconfig.json +13 -0
  119. package/templates/paybond-mcp-coding-agent/package-lock.json +13 -5
  120. package/templates/paybond-mcp-coding-agent/package.json +1 -1
  121. package/templates/paybond-openai-agents-demo/package-lock.json +16 -8
  122. package/templates/paybond-openai-agents-demo/package.json +1 -1
  123. package/templates/paybond-procurement-agent/package-lock.json +13 -5
  124. package/templates/paybond-procurement-agent/package.json +1 -1
  125. package/templates/paybond-travel-agent/package-lock.json +16 -8
  126. package/templates/paybond-travel-agent/package.json +1 -1
  127. package/templates/paybond-vercel-shopping-agent/package-lock.json +13 -5
  128. package/templates/paybond-vercel-shopping-agent/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { createRequire } from "node:module";
2
+ let cachedAgents;
3
+ /**
4
+ * Lazily resolve the optional `agents` peer dependency (Cloudflare Agents SDK).
5
+ *
6
+ * Importing `@paybond/kit/cloudflare-agents` must not require the SDK installed —
7
+ * the peer is only needed when adapter functions or the sandbox demo actually run.
8
+ */
9
+ export function loadCloudflareAgentsSdk() {
10
+ if (cachedAgents === undefined) {
11
+ try {
12
+ const require = createRequire(import.meta.url);
13
+ cachedAgents = require("agents");
14
+ }
15
+ catch (err) {
16
+ throw new Error('The Cloudflare Agents integration requires the optional peer dependency "agents"; install it with: npm install agents', { cause: err });
17
+ }
18
+ }
19
+ return cachedAgents;
20
+ }
@@ -0,0 +1,36 @@
1
+ import type { Paybond } from "../index.js";
2
+ export type RunCloudflareAgentsSandboxDemoInput = {
3
+ paybond: Paybond;
4
+ operation?: string;
5
+ requestedSpendCents?: number;
6
+ evidencePreset?: string;
7
+ toolCallId?: string;
8
+ };
9
+ export type RunCloudflareAgentsSandboxDemoResult = {
10
+ bind: {
11
+ run_id: string;
12
+ tenant_id: string;
13
+ intent_id: string;
14
+ capability_token: string;
15
+ operation: string;
16
+ sandbox_lifecycle_status?: string;
17
+ };
18
+ authorization: {
19
+ allow: boolean;
20
+ audit_id?: string;
21
+ decision_id?: string;
22
+ };
23
+ execute: {
24
+ tool_result?: unknown;
25
+ evidence?: {
26
+ submitted: boolean;
27
+ sandbox_lifecycle_status?: string;
28
+ predicate_passed?: boolean | null;
29
+ };
30
+ };
31
+ };
32
+ /**
33
+ * No-LLM Cloudflare Agents sandbox demo: wrapped AI SDK `tool()` execute + auto-evidence.
34
+ * Requires optional peer `ai` (Cloudflare Agents `getTools()` uses AI SDK tools).
35
+ */
36
+ export declare function runCloudflareAgentsSandboxDemo(input: RunCloudflareAgentsSandboxDemoInput): Promise<RunCloudflareAgentsSandboxDemoResult>;
@@ -0,0 +1,89 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { createPaybondToolRegistry } from "../agent/registry.js";
4
+ import { createPaybondCloudflareAgentsConfig } from "./config.js";
5
+ async function executePaidTool(args) {
6
+ return { status: "completed", cost_cents: args.estimatedPriceCents };
7
+ }
8
+ /**
9
+ * No-LLM Cloudflare Agents sandbox demo: wrapped AI SDK `tool()` execute + auto-evidence.
10
+ * Requires optional peer `ai` (Cloudflare Agents `getTools()` uses AI SDK tools).
11
+ */
12
+ export async function runCloudflareAgentsSandboxDemo(input) {
13
+ const operation = (input.operation ?? "paid-tool").trim();
14
+ const requestedSpendCents = input.requestedSpendCents ?? 100;
15
+ const evidencePreset = (input.evidencePreset ?? "cost_and_completion").trim();
16
+ const toolCallId = (input.toolCallId ?? "cloudflare-agents-demo-1").trim();
17
+ const registry = createPaybondToolRegistry({
18
+ defaultDeny: true,
19
+ sideEffecting: {
20
+ [operation]: {
21
+ operation,
22
+ evidencePreset,
23
+ spendCents: (args) => typeof args === "object" &&
24
+ args !== null &&
25
+ "estimatedPriceCents" in args &&
26
+ typeof args.estimatedPriceCents === "number"
27
+ ? args.estimatedPriceCents
28
+ : requestedSpendCents,
29
+ evidenceMapper: (result) => {
30
+ const payload = result;
31
+ return {
32
+ status: payload.status,
33
+ cost_cents: payload.cost_cents,
34
+ };
35
+ },
36
+ },
37
+ },
38
+ });
39
+ const run = await input.paybond.agentRun.bind({
40
+ bootstrap: {
41
+ kind: "sandbox",
42
+ operation,
43
+ requestedSpendCents,
44
+ completionPreset: evidencePreset,
45
+ },
46
+ registry,
47
+ });
48
+ const rawTools = {
49
+ [operation]: tool({
50
+ description: `Paid operation ${operation}`,
51
+ inputSchema: z.object({
52
+ estimatedPriceCents: z.number().int().nonnegative(),
53
+ }),
54
+ execute: async (inputData) => executePaidTool(inputData),
55
+ }),
56
+ };
57
+ const { tools } = createPaybondCloudflareAgentsConfig(run, rawTools);
58
+ const guardedTool = tools[operation];
59
+ if (!guardedTool || typeof guardedTool.execute !== "function") {
60
+ throw new Error("Cloudflare Agents sandbox demo missing guarded tool execute handler");
61
+ }
62
+ const toolResult = await guardedTool.execute({ estimatedPriceCents: requestedSpendCents }, {
63
+ toolCallId,
64
+ messages: [],
65
+ context: {},
66
+ });
67
+ const sandboxStatus = run.binding.sandbox?.sandboxLifecycleStatus;
68
+ return {
69
+ bind: {
70
+ run_id: run.runId,
71
+ tenant_id: run.tenantId,
72
+ intent_id: run.intentId,
73
+ capability_token: run.capabilityToken,
74
+ operation,
75
+ sandbox_lifecycle_status: sandboxStatus,
76
+ },
77
+ authorization: {
78
+ allow: true,
79
+ },
80
+ execute: {
81
+ tool_result: toolResult,
82
+ evidence: {
83
+ submitted: true,
84
+ sandbox_lifecycle_status: sandboxStatus,
85
+ predicate_passed: true,
86
+ },
87
+ },
88
+ };
89
+ }
@@ -1,2 +1,2 @@
1
1
  /** Generated by kit/scripts/sync-completion-catalog.mjs — do not edit. */
2
- export declare const BUNDLED_COMPLETION_CATALOG_SHA256_HEX: "6b3fc4f2c568bd9de61e96c18b345c815a4ffea73fc1212f2e6fe601817a8483";
2
+ export declare const BUNDLED_COMPLETION_CATALOG_SHA256_HEX: "a738febc9ee8b5b66f05991c4582ba24e6857cdc574196b0aeefc5c77e192ea9";
@@ -1,2 +1,2 @@
1
1
  /** Generated by kit/scripts/sync-completion-catalog.mjs — do not edit. */
2
- export const BUNDLED_COMPLETION_CATALOG_SHA256_HEX = "6b3fc4f2c568bd9de61e96c18b345c815a4ffea73fc1212f2e6fe601817a8483";
2
+ export const BUNDLED_COMPLETION_CATALOG_SHA256_HEX = "a738febc9ee8b5b66f05991c4582ba24e6857cdc574196b0aeefc5c77e192ea9";
@@ -100,7 +100,7 @@ function evidenceFieldComments(preset) {
100
100
  if (required.length > 0) {
101
101
  lines.push(`// Required evidence fields: ${required.join(", ")}`);
102
102
  }
103
- lines.push("// Sandbox: bootstrap with completionPreset to evaluate a strong Harbor predicate on evidence submit.", "// Production: publish the managed template head, then create intents with policy_binding (signing v5).", `// paybond policy templates`, `// paybond policy preview --template ${preset.harbor_template_id} --parameters-file parameters.json --evidence-file evidence.json`);
103
+ lines.push("// Sandbox: bootstrap with completionPreset to evaluate a strong Harbor predicate on evidence submit.", "// Production: publish the managed template head, then create intents with policy_binding (signing v7).", `// paybond policy templates`, `// paybond policy preview --template ${preset.harbor_template_id} --parameters-file parameters.json --evidence-file evidence.json`);
104
104
  return lines;
105
105
  }
106
106
  function typescriptTemplate(preset) {
@@ -178,11 +178,16 @@ ${buildTsFieldLines(preset.evidence_schema).join("\n")}
178
178
  ${vendorPackHelpers}
179
179
  ${buildFn}
180
180
 
181
- // Production: use buildSignedCreateIntentBodyWithPolicyBinding from @paybond/kit after publishing ${preset.harbor_template_id}.
181
+ // Production (signing v7): publish managed template head for ${preset.harbor_template_id}, then:
182
+ // import { PaybondPolicy } from "@paybond/kit";
183
+ // const policy = await PaybondPolicy.load("./paybond.policy.yaml");
184
+ // const created = await paybond.intents.createWithPolicyBinding(
185
+ // policy.toIntentCreateInput({ principalDid, principalSigningSeed, payeeDid, payeeSigningSeed, deadlineRfc3339, settlementRail: "stripe_connect", recognitionProof, publishedPolicyHead }),
186
+ // );
182
187
  export const policyBindingStub = {
183
188
  template_id: HARBOR_TEMPLATE_ID,
184
189
  parameters: completionTemplateParameters,
185
- // head_seq and digest_hex are assigned after: paybond policy publish ...
190
+ // version_seq and head_digest are assigned after publishing the managed template head.
186
191
  };
187
192
 
188
193
  const DEFAULT_OPERATION = "paid_tool.operation";
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * In-process offline Gateway mock for `paybond dev --offline`.
3
- * Simulates sandbox capability bootstrap, verify, evidence, and settlement completion.
3
+ * Simulates sandbox capability bootstrap, verify, evidence, settlement completion,
4
+ * and an optional x402 `/fund` state machine for Harbor intent funding smoke.
4
5
  */
5
6
  export declare const OFFLINE_DEV_INTENT_ID = "00000000-0000-4000-8000-000000000001";
6
7
  export declare const OFFLINE_DEV_TENANT_ID = "tenant-dev-offline";
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * In-process offline Gateway mock for `paybond dev --offline`.
3
- * Simulates sandbox capability bootstrap, verify, evidence, and settlement completion.
3
+ * Simulates sandbox capability bootstrap, verify, evidence, settlement completion,
4
+ * and an optional x402 `/fund` state machine for Harbor intent funding smoke.
4
5
  */
6
+ import { X402FundStateMachine } from "./x402-fund-mock.js";
5
7
  export const OFFLINE_DEV_INTENT_ID = "00000000-0000-4000-8000-000000000001";
6
8
  export const OFFLINE_DEV_TENANT_ID = "tenant-dev-offline";
9
+ const HARBOR_FUND_PATH = /^\/harbor\/intents\/([^/]+)\/fund$/;
7
10
  /** Synthetic sandbox API key shape accepted by offline mocks (never validated remotely). */
8
11
  export const OFFLINE_SANDBOX_API_KEY = "paybond_sk_sandbox_0123456789abcdef0123456789abcdef_" +
9
12
  "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
@@ -15,18 +18,46 @@ export function isProductionApiKey(apiKey) {
15
18
  }
16
19
  export const DEV_WIREMOCK_DEFAULT_PORT = 18089;
17
20
  export const DEV_WIREMOCK_CONTAINER_NAME = "paybond-dev-wiremock";
18
- function jsonResponse(body, status = 200) {
21
+ function jsonResponse(body, status = 200, extraHeaders = {}) {
19
22
  return new Response(JSON.stringify(body), {
20
23
  status,
21
- headers: { "content-type": "application/json" },
24
+ headers: { "content-type": "application/json", ...extraHeaders },
22
25
  });
23
26
  }
27
+ function readRequestHeader(init, name) {
28
+ if (!init?.headers) {
29
+ return undefined;
30
+ }
31
+ const headers = new Headers(init.headers);
32
+ return headers.get(name) ?? undefined;
33
+ }
34
+ function parseHarborFundPath(url) {
35
+ try {
36
+ const pathname = new URL(url).pathname;
37
+ const match = pathname.match(HARBOR_FUND_PATH);
38
+ return match?.[1] ?? null;
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
24
44
  /** Build a fetch implementation that stubs Gateway routes for local dev smoke. */
25
45
  export function createOfflineDevGatewayFetch(options = {}) {
26
46
  const allowVerify = options.allowVerify ?? true;
47
+ const x402FundState = new X402FundStateMachine();
27
48
  return (async (input, init) => {
28
49
  const url = input.toString();
50
+ const method = (init?.method ?? "GET").toUpperCase();
29
51
  const body = init?.body ? JSON.parse(String(init.body)) : {};
52
+ const harborFundIntentId = method === "POST" ? parseHarborFundPath(url) : null;
53
+ if (harborFundIntentId) {
54
+ const paymentSignature = readRequestHeader(init, "payment-signature");
55
+ const mock = x402FundState.next(harborFundIntentId, OFFLINE_DEV_TENANT_ID, paymentSignature);
56
+ if (mock) {
57
+ return jsonResponse(mock.body, mock.status, mock.headers);
58
+ }
59
+ return jsonResponse({}, 404);
60
+ }
30
61
  if (url.endsWith("/v1/auth/principal")) {
31
62
  return jsonResponse({
32
63
  tenant_id: OFFLINE_DEV_TENANT_ID,
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Deterministic x402 `/fund` sequence for WireMock and offline dev gateway mocks.
3
+ * Simulates Harbor: 402 challenge → 202 pending → 200 funded with capability token.
4
+ */
5
+ export declare const X402_DEV_WIREMOCK_INTENT_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
6
+ export declare const X402_DEV_WIREMOCK_TENANT_ID = "dry-run-tenant";
7
+ export declare const X402_DEV_PAYMENT_REQUIRED = "{\"asset\":\"usdc\",\"network\":\"base\",\"amount\":\"0.02\",\"payTo\":\"0xabc1230000000000000000000000000000000000\"}";
8
+ export declare const X402_DEV_CAPABILITY_TOKEN = "cap-x402-dev-mock-1";
9
+ export type X402FundMockPhase = "challenge" | "pending" | "funded";
10
+ export type X402FundMockResponse = {
11
+ status: number;
12
+ headers: Record<string, string>;
13
+ body: Record<string, unknown>;
14
+ phase: X402FundMockPhase;
15
+ };
16
+ /** Build the 402 payment challenge response body. */
17
+ export declare function buildX402FundChallengeBody(intentId: string, tenantId: string): Record<string, unknown>;
18
+ /** Build the 202 authorization_pending response body. */
19
+ export declare function buildX402FundPendingBody(intentId: string, tenantId: string): Record<string, unknown>;
20
+ /** Build the 200 funded response body with capability token. */
21
+ export declare function buildX402FundSuccessBody(intentId: string, tenantId: string, capabilityToken?: string): Record<string, unknown>;
22
+ /** In-memory x402 fund sequence keyed by intent id (one sequence per intent). */
23
+ export declare class X402FundStateMachine {
24
+ private readonly phases;
25
+ reset(intentId?: string): void;
26
+ /** Advance the mock fund sequence for ``intentId`` and return the next Harbor-shaped response. */
27
+ next(intentId: string, tenantId: string, paymentSignature: string | undefined): X402FundMockResponse | null;
28
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Deterministic x402 `/fund` sequence for WireMock and offline dev gateway mocks.
3
+ * Simulates Harbor: 402 challenge → 202 pending → 200 funded with capability token.
4
+ */
5
+ export const X402_DEV_WIREMOCK_INTENT_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
6
+ export const X402_DEV_WIREMOCK_TENANT_ID = "dry-run-tenant";
7
+ export const X402_DEV_PAYMENT_REQUIRED = '{"asset":"usdc","network":"base","amount":"0.02","payTo":"0xabc1230000000000000000000000000000000000"}';
8
+ export const X402_DEV_CAPABILITY_TOKEN = "cap-x402-dev-mock-1";
9
+ function fundingBase(intentId) {
10
+ return {
11
+ settlement_rail: "x402_usdc_base",
12
+ harbor_fund_endpoint: `/harbor/intents/${intentId}/fund`,
13
+ payment_session_id: `paymentSession_${intentId}`,
14
+ payment_url: `https://pay.coinbase.com/payment-sessions/paymentSession_${intentId}`,
15
+ asset: "usdc",
16
+ network: "base",
17
+ capture_expires_at: "2027-12-31T23:59:59Z",
18
+ refund_expires_at: "2028-01-31T23:59:59Z",
19
+ };
20
+ }
21
+ function intentShell(intentId, tenantId, extra) {
22
+ return {
23
+ intent_id: intentId,
24
+ tenant: tenantId,
25
+ settlement_rail: "x402_usdc_base",
26
+ currency: "usd",
27
+ amount_cents: 2000,
28
+ ...extra,
29
+ };
30
+ }
31
+ /** Build the 402 payment challenge response body. */
32
+ export function buildX402FundChallengeBody(intentId, tenantId) {
33
+ return intentShell(intentId, tenantId, {
34
+ state: "open",
35
+ funded: false,
36
+ funding: {
37
+ ...fundingBase(intentId),
38
+ status: "created",
39
+ },
40
+ });
41
+ }
42
+ /** Build the 202 authorization_pending response body. */
43
+ export function buildX402FundPendingBody(intentId, tenantId) {
44
+ return intentShell(intentId, tenantId, {
45
+ state: "open",
46
+ funded: false,
47
+ funding: {
48
+ ...fundingBase(intentId),
49
+ status: "authorization_pending",
50
+ authorization_id: `auth_${intentId}`,
51
+ source_address: "0xsource0000000000000000000000000000000001",
52
+ },
53
+ });
54
+ }
55
+ /** Build the 200 funded response body with capability token. */
56
+ export function buildX402FundSuccessBody(intentId, tenantId, capabilityToken = X402_DEV_CAPABILITY_TOKEN) {
57
+ return intentShell(intentId, tenantId, {
58
+ state: "funded",
59
+ funded: true,
60
+ capability_token: capabilityToken,
61
+ funding: {
62
+ ...fundingBase(intentId),
63
+ status: "authorization_succeeded",
64
+ authorization_id: `auth_${intentId}`,
65
+ source_address: "0xsource0000000000000000000000000000000001",
66
+ },
67
+ });
68
+ }
69
+ /** In-memory x402 fund sequence keyed by intent id (one sequence per intent). */
70
+ export class X402FundStateMachine {
71
+ phases = new Map();
72
+ reset(intentId) {
73
+ if (intentId === undefined) {
74
+ this.phases.clear();
75
+ return;
76
+ }
77
+ this.phases.delete(intentId);
78
+ }
79
+ /** Advance the mock fund sequence for ``intentId`` and return the next Harbor-shaped response. */
80
+ next(intentId, tenantId, paymentSignature) {
81
+ const trimmedIntent = intentId.trim();
82
+ if (!trimmedIntent) {
83
+ return null;
84
+ }
85
+ const phase = this.phases.get(trimmedIntent) ?? "challenge";
86
+ const hasSignature = Boolean(paymentSignature?.trim());
87
+ if (phase === "challenge" && !hasSignature) {
88
+ this.phases.set(trimmedIntent, "pending");
89
+ return {
90
+ status: 402,
91
+ headers: {
92
+ "content-type": "application/json",
93
+ "payment-required": X402_DEV_PAYMENT_REQUIRED,
94
+ },
95
+ body: buildX402FundChallengeBody(trimmedIntent, tenantId),
96
+ phase: "challenge",
97
+ };
98
+ }
99
+ if (phase === "pending" && hasSignature) {
100
+ this.phases.set(trimmedIntent, "funded");
101
+ return {
102
+ status: 202,
103
+ headers: {
104
+ "content-type": "application/json",
105
+ "payment-response": "simulated-x402-payment-response",
106
+ },
107
+ body: buildX402FundPendingBody(trimmedIntent, tenantId),
108
+ phase: "pending",
109
+ };
110
+ }
111
+ if (phase === "funded" && hasSignature) {
112
+ return {
113
+ status: 200,
114
+ headers: {
115
+ "content-type": "application/json",
116
+ "payment-response": "simulated-x402-payment-response",
117
+ },
118
+ body: buildX402FundSuccessBody(trimmedIntent, tenantId),
119
+ phase: "funded",
120
+ };
121
+ }
122
+ return null;
123
+ }
124
+ }