@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.
- package/README.md +66 -21
- package/dist/__tests__/e2e.test.js +1 -1
- package/dist/__tests__/e2e.test.js.map +1 -1
- package/dist/__tests__/x402-http-middleware.test.js +4 -207
- package/dist/__tests__/x402-http-middleware.test.js.map +1 -1
- package/dist/core/__tests__/agent-card.test.js +14 -12
- package/dist/core/__tests__/agent-card.test.js.map +1 -1
- package/dist/core/__tests__/storage-postgres.test.js +0 -1
- package/dist/core/__tests__/storage-postgres.test.js.map +1 -1
- package/dist/core/agent-card.d.ts.map +1 -1
- package/dist/core/agent-card.js +25 -10
- package/dist/core/agent-card.js.map +1 -1
- package/dist/core/challenge-engine.js +1 -1
- package/dist/core/challenge-engine.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/integrations/express.d.ts +6 -4
- package/dist/integrations/express.d.ts.map +1 -1
- package/dist/integrations/express.js +27 -29
- package/dist/integrations/express.js.map +1 -1
- package/dist/integrations/fastify.d.ts +5 -1
- package/dist/integrations/fastify.d.ts.map +1 -1
- package/dist/integrations/fastify.js +117 -8
- package/dist/integrations/fastify.js.map +1 -1
- package/dist/integrations/hono.d.ts +8 -2
- package/dist/integrations/hono.d.ts.map +1 -1
- package/dist/integrations/hono.js +116 -12
- package/dist/integrations/hono.js.map +1 -1
- package/dist/integrations/settlement.d.ts.map +1 -1
- package/dist/integrations/settlement.js +1 -3
- package/dist/integrations/settlement.js.map +1 -1
- package/dist/types/agent-card.d.ts +16 -0
- package/dist/types/agent-card.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/__tests__/e2e.test.ts +1 -1
- package/src/__tests__/x402-http-middleware.test.ts +4 -256
- package/src/core/__tests__/agent-card.test.ts +15 -12
- package/src/core/__tests__/storage-postgres.test.ts +0 -2
- package/src/core/agent-card.ts +26 -10
- package/src/core/challenge-engine.ts +1 -1
- package/src/core/index.ts +1 -1
- package/src/integrations/express.ts +221 -235
- package/src/integrations/fastify.ts +160 -8
- package/src/integrations/hono.ts +168 -12
- package/src/integrations/settlement.ts +1 -3
- package/src/types/agent-card.ts +13 -2
- package/src/types/config.ts +1 -1
- package/dist/integrations/x402-http-middleware.d.ts +0 -15
- package/dist/integrations/x402-http-middleware.d.ts.map +0 -1
- package/dist/integrations/x402-http-middleware.js +0 -171
- package/dist/integrations/x402-http-middleware.js.map +0 -1
- 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 {
|
|
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
|
|
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 {
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
package/src/integrations/hono.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
10
|
-
*
|
|
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 {
|
|
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
|
-
|
|
19
|
-
app.post(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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}
|
|
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
|
|
package/src/types/agent-card.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export type PaymentProtocol = "x402" | "stripe" | "lightning";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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 = {
|
package/src/types/config.ts
CHANGED
|
@@ -83,7 +83,7 @@ export type SellerConfig = {
|
|
|
83
83
|
readonly mcp?: boolean | undefined;
|
|
84
84
|
|
|
85
85
|
// Customization
|
|
86
|
-
readonly basePath?: string; // defaults to "/
|
|
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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"}
|