@key0ai/key0 0.1.0 → 0.2.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 (54) hide show
  1. package/README.md +66 -21
  2. package/dist/__tests__/e2e.test.js +1 -1
  3. package/dist/__tests__/e2e.test.js.map +1 -1
  4. package/dist/__tests__/x402-http-middleware.test.js +4 -207
  5. package/dist/__tests__/x402-http-middleware.test.js.map +1 -1
  6. package/dist/core/__tests__/agent-card.test.js +14 -12
  7. package/dist/core/__tests__/agent-card.test.js.map +1 -1
  8. package/dist/core/__tests__/storage-postgres.test.js +0 -1
  9. package/dist/core/__tests__/storage-postgres.test.js.map +1 -1
  10. package/dist/core/agent-card.d.ts.map +1 -1
  11. package/dist/core/agent-card.js +25 -10
  12. package/dist/core/agent-card.js.map +1 -1
  13. package/dist/core/challenge-engine.js +1 -1
  14. package/dist/core/challenge-engine.js.map +1 -1
  15. package/dist/core/index.d.ts +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/core/index.js +1 -1
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/integrations/express.d.ts +6 -4
  20. package/dist/integrations/express.d.ts.map +1 -1
  21. package/dist/integrations/express.js +27 -29
  22. package/dist/integrations/express.js.map +1 -1
  23. package/dist/integrations/fastify.d.ts +5 -1
  24. package/dist/integrations/fastify.d.ts.map +1 -1
  25. package/dist/integrations/fastify.js +117 -8
  26. package/dist/integrations/fastify.js.map +1 -1
  27. package/dist/integrations/hono.d.ts +8 -2
  28. package/dist/integrations/hono.d.ts.map +1 -1
  29. package/dist/integrations/hono.js +116 -12
  30. package/dist/integrations/hono.js.map +1 -1
  31. package/dist/integrations/settlement.d.ts.map +1 -1
  32. package/dist/integrations/settlement.js +1 -3
  33. package/dist/integrations/settlement.js.map +1 -1
  34. package/dist/types/agent-card.d.ts +16 -0
  35. package/dist/types/agent-card.d.ts.map +1 -1
  36. package/package.json +3 -1
  37. package/src/__tests__/e2e.test.ts +1 -1
  38. package/src/__tests__/x402-http-middleware.test.ts +4 -256
  39. package/src/core/__tests__/agent-card.test.ts +15 -12
  40. package/src/core/__tests__/storage-postgres.test.ts +0 -2
  41. package/src/core/agent-card.ts +26 -10
  42. package/src/core/challenge-engine.ts +1 -1
  43. package/src/core/index.ts +1 -1
  44. package/src/integrations/express.ts +221 -235
  45. package/src/integrations/fastify.ts +160 -8
  46. package/src/integrations/hono.ts +168 -12
  47. package/src/integrations/settlement.ts +1 -3
  48. package/src/types/agent-card.ts +13 -2
  49. package/src/types/config.ts +1 -1
  50. package/dist/integrations/x402-http-middleware.d.ts +0 -15
  51. package/dist/integrations/x402-http-middleware.d.ts.map +0 -1
  52. package/dist/integrations/x402-http-middleware.js +0 -171
  53. package/dist/integrations/x402-http-middleware.js.map +0 -1
  54. package/src/integrations/x402-http-middleware.ts +0 -246
@@ -3,27 +3,179 @@ import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
3
3
  import { createKey0, type Key0Config } from "../factory.js";
4
4
  import type { ValidateAccessTokenConfig } from "../middleware.js";
5
5
  import { validateToken } from "../middleware.js";
6
- import { Key0Error } from "../types/index.js";
6
+ import type { X402PaymentRequiredResponse } from "../types/index.js";
7
+ import { CHAIN_CONFIGS, Key0Error } from "../types/index.js";
8
+ import {
9
+ buildDiscoveryResponse,
10
+ buildHttpPaymentRequirements,
11
+ decodePaymentSignature,
12
+ settlePayment,
13
+ } from "./settlement.js";
7
14
 
8
15
  /**
9
- * Fastify plugin that serves the agent card and A2A endpoint.
16
+ * Fastify plugin that serves the agent card and the unified x402 endpoint.
10
17
  *
11
18
  * Usage:
12
19
  * fastify.register(key0Plugin, { config, adapter });
20
+ *
21
+ * This auto-serves:
22
+ * GET /.well-known/agent.json — A2A agent card (discovery)
23
+ * POST /x402/access — unified x402 HTTP endpoint
13
24
  */
14
25
  export async function key0Plugin(fastify: FastifyInstance, opts: Key0Config): Promise<void> {
15
- const { requestHandler: _requestHandler, agentCard } = createKey0(opts);
26
+ const { engine, agentCard } = createKey0(opts);
27
+ const networkConfig = CHAIN_CONFIGS[opts.config.network];
16
28
 
17
29
  // Agent Card
18
30
  fastify.get(`/${AGENT_CARD_PATH}`, async (_request: FastifyRequest, reply: FastifyReply) => {
19
31
  return reply.send(agentCard);
20
32
  });
33
+ fastify.get("/.well-known/agent.json", async (_request: FastifyRequest, reply: FastifyReply) => {
34
+ return reply.send(agentCard);
35
+ });
36
+
37
+ // Unified x402 endpoint
38
+ fastify.post("/x402/access", async (request: FastifyRequest, reply: FastifyReply) => {
39
+ const startTime = Date.now();
40
+ try {
41
+ console.log("\n[x402-access/fastify] ========== NEW REQUEST ==========");
42
+
43
+ const body = (request.body as Record<string, unknown>) || {};
44
+ let planId = body["planId"] as string | undefined;
45
+ let requestId = body["requestId"] as string | undefined;
46
+ const resourceId = (body["resourceId"] as string) || "default";
47
+
48
+ const paymentSignature = request.headers["payment-signature"] as string | undefined;
49
+
50
+ // Extract planId from PAYMENT-SIGNATURE if not in body
51
+ if (!planId && paymentSignature) {
52
+ try {
53
+ const decoded = decodePaymentSignature(paymentSignature);
54
+ const sigPlanId = decoded.accepted?.extra?.["planId"] as string | undefined;
55
+ if (sigPlanId) {
56
+ console.log(
57
+ `[x402-access/fastify] Extracted planId="${sigPlanId}" from PAYMENT-SIGNATURE`,
58
+ );
59
+ planId = sigPlanId;
60
+ }
61
+ } catch {
62
+ // Fall through to discovery
63
+ }
64
+ }
65
+
66
+ // CASE 1: No planId → 400 pointing to GET /discovery
67
+ if (!planId) {
68
+ return reply.code(400).send({
69
+ error:
70
+ "Please select a plan from the discovery API response to purchase access. Endpoint: GET /discovery",
71
+ });
72
+ }
73
+
74
+ // Auto-generate requestId
75
+ if (!requestId) {
76
+ requestId = `http-${crypto.randomUUID()}`;
77
+ }
78
+
79
+ // CASE 2: planId, no PAYMENT-SIGNATURE → Challenge
80
+ if (!paymentSignature) {
81
+ console.log("[x402-access/fastify] → CASE 2: Challenge 402");
82
+ const { challengeId } = await engine.requestHttpAccess(requestId, planId, resourceId);
83
+
84
+ const requirements: X402PaymentRequiredResponse = buildHttpPaymentRequirements(
85
+ planId,
86
+ resourceId,
87
+ opts.config,
88
+ networkConfig,
89
+ {
90
+ inputSchema: {
91
+ type: "object",
92
+ properties: {
93
+ planId: { type: "string", description: `Tier to purchase. Must be '${planId}'` },
94
+ requestId: { type: "string", description: "Client-generated UUID for idempotency" },
95
+ resourceId: { type: "string", description: "Optional resource identifier" },
96
+ },
97
+ required: ["planId"],
98
+ },
99
+ outputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ accessToken: { type: "string", description: "JWT token for API access" },
103
+ tokenType: { type: "string", description: "Token type (usually 'Bearer')" },
104
+ expiresAt: { type: "string", description: "ISO 8601 expiration timestamp" },
105
+ resourceEndpoint: {
106
+ type: "string",
107
+ description: "URL to access the protected resource",
108
+ },
109
+ txHash: { type: "string", description: "On-chain transaction hash" },
110
+ explorerUrl: { type: "string", description: "Blockchain explorer URL" },
111
+ },
112
+ },
113
+ description: `Access to ${resourceId} via ${planId} tier`,
114
+ },
115
+ );
116
+
117
+ const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
118
+ reply.header("payment-required", encoded);
119
+ reply.header(
120
+ "www-authenticate",
121
+ `Payment realm="${opts.config.agentUrl}", accept="exact", challenge="${challengeId}"`,
122
+ );
123
+
124
+ return reply
125
+ .code(402)
126
+ .send({ ...requirements, challengeId, requestId, error: "Payment required" });
127
+ }
128
+
129
+ // CASE 3: planId + PAYMENT-SIGNATURE → Settle
130
+ console.log("[x402-access/fastify] → CASE 3: Settlement");
131
+
132
+ const existingGrant = await engine.preSettlementCheck(requestId);
133
+ if (existingGrant) {
134
+ return reply.code(200).send(existingGrant);
135
+ }
136
+
137
+ const paymentPayload = decodePaymentSignature(paymentSignature);
138
+ const { txHash, settleResponse, payer } = await settlePayment(
139
+ paymentPayload,
140
+ opts.config,
141
+ networkConfig,
142
+ );
143
+
144
+ const grant = await engine.processHttpPayment(
145
+ requestId,
146
+ planId,
147
+ resourceId,
148
+ txHash,
149
+ payer as `0x${string}` | undefined,
150
+ );
151
+
152
+ const paymentResponse = Buffer.from(JSON.stringify(settleResponse)).toString("base64");
153
+ reply.header("payment-response", paymentResponse);
154
+
155
+ return reply.code(200).send(grant);
156
+ } catch (err: unknown) {
157
+ const elapsed = Date.now() - startTime;
158
+ console.error(`[x402-access/fastify] Error after ${elapsed}ms:`, err);
159
+
160
+ if (err instanceof Key0Error) {
161
+ if (err.code === "PROOF_ALREADY_REDEEMED" && err.details?.["grant"]) {
162
+ return reply.code(200).send(err.details["grant"]);
163
+ }
164
+ return reply.code(err.httpStatus).send(err.toJSON());
165
+ }
166
+
167
+ return reply.code(500).send({
168
+ error: "INTERNAL_ERROR",
169
+ message: err instanceof Error ? err.message : "Internal server error",
170
+ });
171
+ } finally {
172
+ console.log(`[x402-access/fastify] Request completed in ${Date.now() - startTime}ms`);
173
+ }
174
+ });
21
175
 
22
- // A2A endpoint
23
- const basePath = opts.config.basePath ?? "/a2a";
24
- fastify.post(basePath, async (_request: FastifyRequest, reply: FastifyReply) => {
25
- // TODO: Use official A2A Fastify handler when available
26
- return reply.code(501).send({ error: "Fastify support pending A2A SDK update" });
176
+ fastify.get("/discovery", async (_request: FastifyRequest, reply: FastifyReply) => {
177
+ const discoveryResponse = buildDiscoveryResponse(opts.config, networkConfig);
178
+ return reply.send({ discoveryResponse });
27
179
  });
28
180
  }
29
181
 
@@ -3,26 +3,182 @@ import { Hono } from "hono";
3
3
  import { createKey0, type Key0Config } from "../factory.js";
4
4
  import type { ValidateAccessTokenConfig } from "../middleware.js";
5
5
  import { validateToken } from "../middleware.js";
6
- import { Key0Error } from "../types/index.js";
6
+ import type { X402PaymentRequiredResponse } from "../types/index.js";
7
+ import { CHAIN_CONFIGS, Key0Error } from "../types/index.js";
8
+ import {
9
+ buildDiscoveryResponse,
10
+ buildHttpPaymentRequirements,
11
+ decodePaymentSignature,
12
+ settlePayment,
13
+ } from "./settlement.js";
7
14
 
8
15
  /**
9
- * Create a Hono app that serves the agent card and A2A endpoint.
10
- * Mount it as a sub-app: mainApp.route("/", key0App(opts));
16
+ * Create a Hono app that serves the agent card and the unified x402 endpoint.
17
+ *
18
+ * Usage:
19
+ * mainApp.route("/", key0App(opts));
20
+ *
21
+ * This auto-serves:
22
+ * GET /.well-known/agent.json — A2A agent card (discovery)
23
+ * POST /x402/access — unified x402 HTTP endpoint
11
24
  */
12
25
  export function key0App(opts: Key0Config): Hono {
13
- const { requestHandler: _requestHandler, agentCard } = createKey0(opts);
26
+ const { engine, agentCard } = createKey0(opts);
14
27
  const app = new Hono();
28
+ const networkConfig = CHAIN_CONFIGS[opts.config.network];
15
29
 
30
+ // Agent Card
16
31
  app.get(`/${AGENT_CARD_PATH}`, (c) => c.json(agentCard));
32
+ app.get("/.well-known/agent.json", (c) => c.json(agentCard));
17
33
 
18
- const basePath = opts.config.basePath ?? "/a2a";
19
- app.post(basePath, async (c) => {
20
- // TODO: Use official A2A Hono handler when available
21
- // For now, this is a placeholder or we need to bridge manually.
22
- // Since we want clean code, and manual bridging is verbose,
23
- // we'll leave this as a basic implementation returning 501 for now
24
- // or better, throw an error saying "Use Express integration for A2A support currently".
25
- return c.json({ error: "Hono support pending A2A SDK update" }, 501);
34
+ // Unified x402 endpoint
35
+ app.post("/x402/access", async (c) => {
36
+ const startTime = Date.now();
37
+ try {
38
+ console.log("\n[x402-access/hono] ========== NEW REQUEST ==========");
39
+
40
+ const body = await c.req.json().catch(() => ({}));
41
+ let { planId, resourceId = "default" } = body as {
42
+ planId?: string;
43
+ resourceId?: string;
44
+ };
45
+ let { requestId } = body as { requestId?: string };
46
+
47
+ const paymentSignature = c.req.header("payment-signature");
48
+
49
+ // Extract planId from PAYMENT-SIGNATURE if not in body
50
+ if (!planId && paymentSignature) {
51
+ try {
52
+ const decoded = decodePaymentSignature(paymentSignature);
53
+ const sigPlanId = decoded.accepted?.extra?.["planId"] as string | undefined;
54
+ if (sigPlanId) {
55
+ console.log(
56
+ `[x402-access/hono] Extracted planId="${sigPlanId}" from PAYMENT-SIGNATURE`,
57
+ );
58
+ planId = sigPlanId;
59
+ }
60
+ } catch {
61
+ // Fall through to discovery
62
+ }
63
+ }
64
+
65
+ // CASE 1: No planId → 400 pointing to GET /discovery
66
+ if (!planId) {
67
+ return c.json(
68
+ {
69
+ error:
70
+ "Please select a plan from the discovery API response to purchase access. Endpoint: GET /discovery",
71
+ },
72
+ 400,
73
+ );
74
+ }
75
+
76
+ // Auto-generate requestId
77
+ if (!requestId) {
78
+ requestId = `http-${crypto.randomUUID()}`;
79
+ }
80
+
81
+ // CASE 2: planId, no PAYMENT-SIGNATURE → Challenge
82
+ if (!paymentSignature) {
83
+ console.log("[x402-access/hono] → CASE 2: Challenge 402");
84
+ const { challengeId } = await engine.requestHttpAccess(requestId, planId, resourceId);
85
+
86
+ const requirements: X402PaymentRequiredResponse = buildHttpPaymentRequirements(
87
+ planId,
88
+ resourceId,
89
+ opts.config,
90
+ networkConfig,
91
+ {
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ planId: { type: "string", description: `Tier to purchase. Must be '${planId}'` },
96
+ requestId: { type: "string", description: "Client-generated UUID for idempotency" },
97
+ resourceId: { type: "string", description: "Optional resource identifier" },
98
+ },
99
+ required: ["planId"],
100
+ },
101
+ outputSchema: {
102
+ type: "object",
103
+ properties: {
104
+ accessToken: { type: "string", description: "JWT token for API access" },
105
+ tokenType: { type: "string", description: "Token type (usually 'Bearer')" },
106
+ expiresAt: { type: "string", description: "ISO 8601 expiration timestamp" },
107
+ resourceEndpoint: {
108
+ type: "string",
109
+ description: "URL to access the protected resource",
110
+ },
111
+ txHash: { type: "string", description: "On-chain transaction hash" },
112
+ explorerUrl: { type: "string", description: "Blockchain explorer URL" },
113
+ },
114
+ },
115
+ description: `Access to ${resourceId} via ${planId} tier`,
116
+ },
117
+ );
118
+
119
+ const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
120
+ c.header("payment-required", encoded);
121
+ c.header(
122
+ "www-authenticate",
123
+ `Payment realm="${opts.config.agentUrl}", accept="exact", challenge="${challengeId}"`,
124
+ );
125
+
126
+ return c.json({ ...requirements, challengeId, requestId, error: "Payment required" }, 402);
127
+ }
128
+
129
+ // CASE 3: planId + PAYMENT-SIGNATURE → Settle
130
+ console.log("[x402-access/hono] → CASE 3: Settlement");
131
+
132
+ const existingGrant = await engine.preSettlementCheck(requestId);
133
+ if (existingGrant) {
134
+ return c.json(existingGrant, 200);
135
+ }
136
+
137
+ const paymentPayload = decodePaymentSignature(paymentSignature);
138
+ const { txHash, settleResponse, payer } = await settlePayment(
139
+ paymentPayload,
140
+ opts.config,
141
+ networkConfig,
142
+ );
143
+
144
+ const grant = await engine.processHttpPayment(
145
+ requestId,
146
+ planId,
147
+ resourceId,
148
+ txHash,
149
+ payer as `0x${string}` | undefined,
150
+ );
151
+
152
+ const paymentResponse = Buffer.from(JSON.stringify(settleResponse)).toString("base64");
153
+ c.header("payment-response", paymentResponse);
154
+
155
+ return c.json(grant, 200);
156
+ } catch (err: unknown) {
157
+ const elapsed = Date.now() - startTime;
158
+ console.error(`[x402-access/hono] Error after ${elapsed}ms:`, err);
159
+
160
+ if (err instanceof Key0Error) {
161
+ if (err.code === "PROOF_ALREADY_REDEEMED" && err.details?.["grant"]) {
162
+ return c.json(err.details["grant"], 200);
163
+ }
164
+ return c.json(err.toJSON(), err.httpStatus as any);
165
+ }
166
+
167
+ return c.json(
168
+ {
169
+ error: "INTERNAL_ERROR",
170
+ message: err instanceof Error ? err.message : "Internal server error",
171
+ },
172
+ 500,
173
+ );
174
+ } finally {
175
+ console.log(`[x402-access/hono] Request completed in ${Date.now() - startTime}ms`);
176
+ }
177
+ });
178
+
179
+ app.get("/discovery", (c) => {
180
+ const discoveryResponse = buildDiscoveryResponse(opts.config, networkConfig);
181
+ return c.json({ discoveryResponse });
26
182
  });
27
183
 
28
184
  return app;
@@ -111,9 +111,8 @@ export function buildHttpPaymentRequirements(
111
111
  throw new Key0Error("TIER_NOT_FOUND", `Plan "${planId}" not found`, 400);
112
112
  }
113
113
 
114
- const basePath = config.basePath ?? "/a2a";
115
114
  const baseUrl = config.agentUrl.replace(/\/$/, "");
116
- const resourceUrl = `${baseUrl}${basePath}/jsonrpc`;
115
+ const resourceUrl = `${baseUrl}/x402/access`;
117
116
 
118
117
  const amountRaw = parseDollarToUsdcMicro(tier.unitAmount);
119
118
  const network = `eip155:${networkConfig.chainId}`;
@@ -165,7 +164,6 @@ export function buildDiscoveryResponse(
165
164
  config: SellerConfig,
166
165
  networkConfig: NetworkConfig,
167
166
  ): X402PaymentRequiredResponse {
168
- const _basePath = config.basePath ?? "/a2a";
169
167
  const baseUrl = config.agentUrl.replace(/\/$/, "");
170
168
  const resourceUrl = `${baseUrl}/x402/access`;
171
169
 
@@ -1,7 +1,12 @@
1
1
  export type PaymentProtocol = "x402" | "stripe" | "lightning";
2
2
 
3
- // A2A spec-compliant AgentSkill type
4
- // See: https://a2a-protocol.org/latest/specification/#44-agent-discovery-objects
3
+ /**
4
+ * A2A AgentSkill with Key0 extensions.
5
+ *
6
+ * Standard A2A fields: id, name, description, tags, examples, inputModes, outputModes, security.
7
+ * Key0 extensions: endpoint, inputSchema, workflow — these provide machine-readable
8
+ * metadata for automated agent clients.
9
+ */
5
10
  export type AgentSkill = {
6
11
  readonly id: string;
7
12
  readonly name: string;
@@ -11,6 +16,12 @@ export type AgentSkill = {
11
16
  readonly inputModes?: readonly string[];
12
17
  readonly outputModes?: readonly string[];
13
18
  readonly security?: Record<string, string[]>;
19
+ /** @key0 Direct endpoint URL and HTTP method for this skill. */
20
+ readonly endpoint?: { readonly url: string; readonly method: "GET" | "POST" };
21
+ /** @key0 JSON Schema for the skill's input parameters. */
22
+ readonly inputSchema?: Record<string, unknown>;
23
+ /** @key0 Step-by-step workflow instructions for automated clients. */
24
+ readonly workflow?: readonly string[];
14
25
  };
15
26
 
16
27
  export type AgentInterface = {
@@ -83,7 +83,7 @@ export type SellerConfig = {
83
83
  readonly mcp?: boolean | undefined;
84
84
 
85
85
  // Customization
86
- readonly basePath?: string; // defaults to "/a2a"
86
+ readonly basePath?: string; // defaults to "/agent"
87
87
  readonly resourceEndpointTemplate?: string; // e.g. "https://api.example.com/photos/{resourceId}"
88
88
 
89
89
  // Settlement strategy (optional — defaults to facilitatorUrl mode)
@@ -1,15 +0,0 @@
1
- import type { NextFunction, Request, Response } from "express";
2
- import type { ChallengeEngine } from "../core/index.js";
3
- import type { SellerConfig } from "../types/index.js";
4
- export { buildDiscoveryResponse, buildHttpPaymentRequirements, decodePaymentSignature, settlePayment, settleViaFacilitator, settleViaGasWallet, } from "./settlement.js";
5
- /**
6
- * Express middleware that intercepts AccessRequest calls on the JSON-RPC endpoint
7
- * and implements the x402 HTTP flow for clients that do NOT send X-A2A-Extensions.
8
- *
9
- * Routing:
10
- * X-A2A-Extensions present → A2A-native client → pass through to A2A JSON-RPC handler
11
- * message/send + no payment-signature → HTTP 402 with PaymentRequirements
12
- * message/send + payment-signature → settle → HTTP 200 with AccessGrant
13
- */
14
- export declare function createX402HttpMiddleware(engine: ChallengeEngine, config: SellerConfig): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
15
- //# sourceMappingURL=x402-http-middleware.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"x402-http-middleware.d.ts","sourceRoot":"","sources":["../../src/integrations/x402-http-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAGX,YAAY,EAEZ,MAAM,mBAAmB,CAAC;AAgB3B,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,IAGvE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wDAwM7D"}
@@ -1,171 +0,0 @@
1
- import { CHAIN_CONFIGS } from "../types/config-shared.js";
2
- import { Key0Error } from "../types/index.js";
3
- import { buildDiscoveryResponse as buildDiscoveryResponseImpl, buildHttpPaymentRequirements, decodePaymentSignature, settlePayment, } from "./settlement.js";
4
- // x402 v2 headers
5
- const PAYMENT_SIGNATURE_HEADER = "payment-signature";
6
- const PAYMENT_REQUIRED_HEADER = "payment-required";
7
- const PAYMENT_RESPONSE_HEADER = "payment-response";
8
- const X_A2A_EXTENSIONS_HEADER = "x-a2a-extensions";
9
- // Re-export shared settlement utilities so callers can import from a single place
10
- export { buildDiscoveryResponse, buildHttpPaymentRequirements, decodePaymentSignature, settlePayment, settleViaFacilitator, settleViaGasWallet, } from "./settlement.js";
11
- /**
12
- * Express middleware that intercepts AccessRequest calls on the JSON-RPC endpoint
13
- * and implements the x402 HTTP flow for clients that do NOT send X-A2A-Extensions.
14
- *
15
- * Routing:
16
- * X-A2A-Extensions present → A2A-native client → pass through to A2A JSON-RPC handler
17
- * message/send + no payment-signature → HTTP 402 with PaymentRequirements
18
- * message/send + payment-signature → settle → HTTP 200 with AccessGrant
19
- */
20
- export function createX402HttpMiddleware(engine, config) {
21
- const networkConfig = CHAIN_CONFIGS[config.network];
22
- return async (req, res, next) => {
23
- console.log("\n[x402-http-middleware] ========== NEW REQUEST ==========");
24
- console.log("[x402-http-middleware] Method:", req.method);
25
- console.log("[x402-http-middleware] Path:", req.path);
26
- console.log("[x402-http-middleware] Headers:", JSON.stringify(req.headers, null, 2));
27
- // Intercept response to log the body
28
- const originalJson = res.json.bind(res);
29
- const originalSend = res.send.bind(res);
30
- res.json = (body) => {
31
- console.log("[x402-http-middleware] Response Status:", res.statusCode);
32
- console.log("[x402-http-middleware] Response Body:", JSON.stringify(body, null, 2));
33
- return originalJson(body);
34
- };
35
- res.send = (body) => {
36
- console.log("[x402-http-middleware] Response Status:", res.statusCode);
37
- console.log("[x402-http-middleware] Response Body:", typeof body === "string" ? body : JSON.stringify(body, null, 2));
38
- return originalSend(body);
39
- };
40
- try {
41
- // 1. If X-A2A-Extensions header present, this is an A2A client → pass through
42
- const hasA2AExtensions = req.headers[X_A2A_EXTENSIONS_HEADER];
43
- console.log(`[x402-http-middleware] X-A2A-Extensions header present: ${!!hasA2AExtensions}`);
44
- if (hasA2AExtensions) {
45
- console.log("[x402-http-middleware] → Passing through to A2A JSON-RPC handler");
46
- return next();
47
- }
48
- // 2. Parse JSON-RPC body
49
- const body = req.body;
50
- console.log("[x402-http-middleware] Body:", JSON.stringify(body, null, 2));
51
- if (!body || typeof body !== "object") {
52
- console.log("[x402-http-middleware] → No valid body, passing through");
53
- return next();
54
- }
55
- // 3. Only intercept message/send
56
- console.log(`[x402-http-middleware] Method in body: ${body.method}`);
57
- if (body.method !== "message/send") {
58
- console.log("[x402-http-middleware] → Not a message/send call, passing through");
59
- return next();
60
- }
61
- // 4. Extract AccessRequest from message parts
62
- // Route on tierId presence, not on type field
63
- const params = body.params;
64
- if (!params || !params.message || !params.message.parts) {
65
- console.log("[x402-http-middleware] → No valid message parts, passing through");
66
- return next();
67
- }
68
- let accessRequest = null;
69
- console.log(`[x402-http-middleware] Parsing ${params.message.parts.length} message parts...`);
70
- for (const part of params.message.parts) {
71
- console.log(`[x402-http-middleware] - Part kind: ${part.kind}`);
72
- if (part.kind === "data" && part.data && part.data.type === "AccessRequest") {
73
- accessRequest = part.data;
74
- console.log("[x402-http-middleware] ✓ Found AccessRequest data part");
75
- break;
76
- }
77
- if (part.kind === "text") {
78
- try {
79
- const parsed = JSON.parse(part.text);
80
- if (parsed.type === "AccessRequest") {
81
- accessRequest = parsed;
82
- console.log("[x402-http-middleware] ✓ Found AccessRequest in text part");
83
- break;
84
- }
85
- }
86
- catch {
87
- continue;
88
- }
89
- }
90
- }
91
- if (!accessRequest) {
92
- console.log("[x402-http-middleware] → No data found in message parts, passing through");
93
- return next();
94
- }
95
- const resourceId = accessRequest.resourceId || "default";
96
- const planId = accessRequest.planId;
97
- const requestId = accessRequest.requestId || `http-${crypto.randomUUID()}`;
98
- console.log(`[x402-http-middleware] AccessRequest: planId=${planId}, resourceId=${resourceId}, requestId=${requestId}`);
99
- // 5a. Discovery case: no planId → return 402 with full product catalog
100
- if (!planId) {
101
- console.log("[x402-http-middleware] → No planId provided, returning discovery 402");
102
- const discoveryResponse = buildDiscoveryResponseImpl(config, networkConfig);
103
- const encoded = Buffer.from(JSON.stringify(discoveryResponse)).toString("base64");
104
- res.setHeader(PAYMENT_REQUIRED_HEADER, encoded);
105
- return res.status(402).json({
106
- ...discoveryResponse,
107
- error: "Payment required",
108
- });
109
- }
110
- // 5b. Check for PAYMENT-SIGNATURE header
111
- const paymentSignatureRaw = req.headers[PAYMENT_SIGNATURE_HEADER];
112
- console.log(`[x402-http-middleware] PAYMENT-SIGNATURE header present: ${!!paymentSignatureRaw}`);
113
- if (!paymentSignatureRaw) {
114
- // ===== STEP 1: No payment → create PENDING record and return HTTP 402 =====
115
- console.log("[x402-http-middleware] → STEP 1: Issuing 402 challenge");
116
- const { challengeId } = await engine.requestHttpAccess(requestId, planId, resourceId);
117
- console.log(`[x402-http-middleware] ✓ PENDING record created, challengeId=${challengeId}`);
118
- const requirements = buildHttpPaymentRequirements(planId, resourceId, config, networkConfig);
119
- console.log("[x402-http-middleware] Payment requirements:", JSON.stringify(requirements, null, 2));
120
- const base64Requirements = Buffer.from(JSON.stringify(requirements)).toString("base64");
121
- res.setHeader(PAYMENT_REQUIRED_HEADER, base64Requirements);
122
- return res.status(402).json({
123
- ...requirements,
124
- challengeId,
125
- error: "PAYMENT-SIGNATURE header is required",
126
- });
127
- }
128
- // ===== STEP 2: Has PAYMENT-SIGNATURE → settle and return access grant =====
129
- console.log("[x402-http-middleware] → STEP 2: Processing payment");
130
- console.log(`[x402-http-middleware] PAYMENT-SIGNATURE (first 50 chars): ${paymentSignatureRaw.substring(0, 50)}...`);
131
- // Pre-settlement check: avoid burning USDC if already delivered/expired/cancelled
132
- const existingGrant = await engine.preSettlementCheck(requestId);
133
- if (existingGrant) {
134
- console.log("[x402-http-middleware] ✓ Already delivered, returning cached grant");
135
- return res.status(200).json(existingGrant);
136
- }
137
- // Decode the header then settle via shared settlement layer
138
- const paymentPayload = decodePaymentSignature(paymentSignatureRaw);
139
- const { txHash, settleResponse: _settleResponse, payer, } = await settlePayment(paymentPayload, config, networkConfig);
140
- console.log(`[x402-http-middleware] ✓ Payment settled, txHash: ${txHash}`);
141
- const grant = await engine.processHttpPayment(requestId, planId, resourceId, txHash, payer);
142
- console.log("[x402-http-middleware] ✓ Access grant issued:", JSON.stringify(grant, null, 2));
143
- const settlementResponse = {
144
- success: true,
145
- transaction: txHash,
146
- network: `eip155:${networkConfig.chainId}`,
147
- ...(payer && { payer }),
148
- };
149
- res.setHeader(PAYMENT_RESPONSE_HEADER, Buffer.from(JSON.stringify(settlementResponse)).toString("base64"));
150
- return res.status(200).json(grant);
151
- }
152
- catch (err) {
153
- console.error("[x402-http-middleware] ✗ ERROR:", err);
154
- if (err instanceof Key0Error) {
155
- // Return the grant directly for PROOF_ALREADY_REDEEMED (status 200)
156
- if (err.code === "PROOF_ALREADY_REDEEMED" && err.details?.["grant"]) {
157
- return res.status(200).json(err.details["grant"]);
158
- }
159
- return res.status(err.httpStatus).json(err.toJSON());
160
- }
161
- return res.status(500).json({
162
- error: "INTERNAL_ERROR",
163
- message: err instanceof Error ? err.message : "Internal server error",
164
- });
165
- }
166
- finally {
167
- console.log("[x402-http-middleware] ========== REQUEST COMPLETE ==========\n");
168
- }
169
- };
170
- }
171
- //# sourceMappingURL=x402-http-middleware.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"x402-http-middleware.js","sourceRoot":"","sources":["../../src/integrations/x402-http-middleware.ts"],"names":[],"mappings":"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"}