@tangle-network/agent-gateway 0.1.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.
@@ -0,0 +1,398 @@
1
+ import {
2
+ MemoryNonceStore
3
+ } from "./chunk-Z22ALGHW.js";
4
+ import {
5
+ MemoryRateLimitStore,
6
+ checkRateLimit
7
+ } from "./chunk-4FULF5LW.js";
8
+
9
+ // src/middleware.ts
10
+ import { Hono } from "hono";
11
+
12
+ // src/verify.ts
13
+ async function verifyX402(spendAuthHeader, config, nonceStore) {
14
+ try {
15
+ const raw = JSON.parse(spendAuthHeader);
16
+ if (!raw.commitment || !raw.signature || !raw.amount) return null;
17
+ if (raw.operator?.toLowerCase() !== config.operatorAddress.toLowerCase()) return null;
18
+ const amount = BigInt(raw.amount);
19
+ const nonce = BigInt(raw.nonce);
20
+ const expiry = BigInt(raw.expiry);
21
+ if (expiry < BigInt(Math.floor(Date.now() / 1e3))) return null;
22
+ if (amount <= 0n) return null;
23
+ const nonceKey = `${raw.commitment}:${nonce.toString()}`;
24
+ if (nonceStore) {
25
+ if (await nonceStore.hasSeen(nonceKey)) return null;
26
+ const ttl = Math.min(Number(expiry) - Math.floor(Date.now() / 1e3), 3600);
27
+ await nonceStore.markSeen(nonceKey, Math.max(ttl, 60));
28
+ }
29
+ if (config.verifySigner) {
30
+ const verified = await config.verifySigner(raw);
31
+ if (!verified) return null;
32
+ } else if (!config.demoMode) {
33
+ console.warn("[agent-gateway] x402 verification running without verifySigner \u2014 set demoMode: true to suppress this warning");
34
+ }
35
+ return raw.commitment;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ async function verifyMpp(authHeader, _config, x402Config) {
41
+ const match = authHeader.match(/^Payment\s+(\S+)\s+(\S+)$/i);
42
+ if (!match) return null;
43
+ const [, , credentialB64] = match;
44
+ try {
45
+ const decoded = Buffer.from(credentialB64, "base64url").toString("utf-8");
46
+ const credential = JSON.parse(decoded);
47
+ const payload = credential.payload ?? credential;
48
+ if (!payload.commitment && !payload.from) return null;
49
+ const operator = payload.operator ?? payload.to;
50
+ if (operator && operator.toLowerCase() !== x402Config.operatorAddress.toLowerCase()) return null;
51
+ if (payload.amount) BigInt(payload.amount);
52
+ if (payload.nonce) BigInt(payload.nonce);
53
+ return payload.commitment ?? payload.from ?? null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ async function defaultVerifyApiKey(authHeader) {
59
+ if (!authHeader.startsWith("Bearer sk_agent_")) return null;
60
+ const key = authHeader.slice(7);
61
+ return {
62
+ keyId: key.slice(0, 16),
63
+ consumerId: `apikey:${key.slice(0, 16)}`
64
+ };
65
+ }
66
+
67
+ // src/filter.ts
68
+ var INJECTION_PATTERNS = [
69
+ // Direct instruction override
70
+ /ignore\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions?|prompts?|rules?|directives?)/i,
71
+ /disregard\s+(all\s+)?(previous|prior|system)/i,
72
+ /forget\s+(everything|all|your)\s+(previous|instructions?|training)/i,
73
+ // Role assumption
74
+ /you\s+are\s+now\s+(a|an|the)\s+/i,
75
+ /pretend\s+(you\s+are|to\s+be)\s+/i,
76
+ /act\s+as\s+(if\s+you\s+are|a|an|the)\s+/i,
77
+ /new\s+instructions?:/i,
78
+ /\[system\]/i,
79
+ /\[INST\]/i,
80
+ // Prompt extraction
81
+ /what\s+(is|are)\s+your\s+(system\s+)?(prompt|instructions?|rules?|directives?)/i,
82
+ /repeat\s+(your|the)\s+(system\s+)?(prompt|instructions?)/i,
83
+ /output\s+(your|the)\s+(system\s+)?(prompt|instructions?)/i,
84
+ /show\s+me\s+(your|the)\s+(system|hidden|secret)\s+(prompt|instructions?|message)/i,
85
+ // Data exfiltration
86
+ /read\s+(the\s+)?(vault|workspace|config|secret|\.env)/i,
87
+ /cat\s+\/home\/agent\/(vault|config|\.env|secrets?)/i,
88
+ /list\s+(all\s+)?(vault|workspace|secret)\s+(files?|contents?|data)/i
89
+ ];
90
+ function normalizeUnicode(text) {
91
+ return text.replace(/[\u200B-\u200F\u2028-\u202F\u2060\uFEFF]/g, "").normalize("NFKC");
92
+ }
93
+ function detectInjection(content) {
94
+ const normalized = normalizeUnicode(content);
95
+ const matches = [];
96
+ for (const pattern of INJECTION_PATTERNS) {
97
+ if (pattern.test(normalized)) {
98
+ matches.push(pattern.source.slice(0, 60));
99
+ }
100
+ }
101
+ const b64Matches = normalized.match(/[A-Za-z0-9+/]{40,}={0,2}/g);
102
+ if (b64Matches) {
103
+ for (const b64 of b64Matches) {
104
+ try {
105
+ const decoded = atob(b64);
106
+ if (INJECTION_PATTERNS.some((p) => p.test(decoded))) {
107
+ matches.push("base64-encoded injection");
108
+ }
109
+ } catch {
110
+ }
111
+ }
112
+ }
113
+ return matches;
114
+ }
115
+ function filterConsumerMessages(messages, maxLength = 8e3) {
116
+ return messages.filter((m) => m.role !== "system").map((m) => {
117
+ const normalized = normalizeUnicode(m.content);
118
+ const redacted = normalized.replace(/\b(vault|workspace|owner|admin|secret|\.env|config\.json)[\s/:][^\s]*/gi, "[REDACTED]").slice(0, maxLength);
119
+ return { role: m.role, content: redacted };
120
+ });
121
+ }
122
+ function filterConsumerMessagesStrict(messages, maxLength = 8e3) {
123
+ const filtered = filterConsumerMessages(messages, maxLength);
124
+ const allContent = filtered.map((m) => m.content).join(" ");
125
+ const injectionWarnings = detectInjection(allContent);
126
+ return { messages: filtered, injectionWarnings };
127
+ }
128
+ function redactSystemPromptFromOutput(output, systemPrompt) {
129
+ if (!systemPrompt || systemPrompt.length < 40) return output;
130
+ const chunks = systemPrompt.split(/[.\n]/).map((s) => s.trim()).filter((s) => s.length >= 40);
131
+ let redacted = output;
132
+ for (const chunk of chunks) {
133
+ const idx = redacted.toLowerCase().indexOf(chunk.toLowerCase());
134
+ if (idx >= 0) {
135
+ redacted = redacted.slice(0, idx) + "[REDACTED \u2014 system instructions]" + redacted.slice(idx + chunk.length);
136
+ }
137
+ }
138
+ return redacted;
139
+ }
140
+
141
+ // src/middleware.ts
142
+ function createAgentGateway(config) {
143
+ const gw = new Hono();
144
+ const maxLen = config.maxMessageLength ?? 8e3;
145
+ const rateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore();
146
+ const globalRateLimit = config.rateLimit ?? { limit: 60, windowSeconds: 60 };
147
+ const nonceStore = config.nonceStore ?? new MemoryNonceStore();
148
+ const requiredScope = config.requiredScope ?? "chat";
149
+ gw.get("/:slug/chat/completions", async (c) => {
150
+ const slug = c.req.param("slug");
151
+ const agent = await config.resolveAgent(slug);
152
+ if (!agent) return c.json({ error: "Agent not found or not published" }, 404);
153
+ const paymentMethods = [
154
+ {
155
+ type: "x402",
156
+ operator: config.x402.operatorAddress,
157
+ chain_id: config.x402.chainId,
158
+ credits_contract: config.x402.creditsAddress
159
+ }
160
+ ];
161
+ if (config.mpp) {
162
+ paymentMethods.push({
163
+ type: "mpp",
164
+ realm: config.mpp.realm,
165
+ method: config.mpp.method ?? "blueprintevm"
166
+ });
167
+ }
168
+ paymentMethods.push({ type: "api_key", prefix: "sk_agent_" });
169
+ return c.json({
170
+ slug: agent.slug,
171
+ pricing: {
172
+ per_token_usd: agent.pricePerTokenUsd,
173
+ currency: "USD",
174
+ platform_fee_percent: agent.platformFeePercent
175
+ },
176
+ hosting: {
177
+ mode: agent.sandboxEndpoint ? "sovereign" : "centralized",
178
+ endpoint: agent.sandboxEndpoint ?? config.baseUrl ?? "tangle.tools"
179
+ },
180
+ payment_methods: paymentMethods,
181
+ capabilities: ["chat.completions", "streaming"],
182
+ openai_compatible: true
183
+ });
184
+ });
185
+ gw.post("/:slug/chat/completions", async (c) => {
186
+ const slug = c.req.param("slug");
187
+ const startMs = Date.now();
188
+ const agent = await config.resolveAgent(slug);
189
+ if (!agent) {
190
+ return c.json({ error: { message: "Agent not found", type: "not_found" } }, 404);
191
+ }
192
+ const contentLength = parseInt(c.req.header("Content-Length") ?? "0", 10);
193
+ if (contentLength > 65536) {
194
+ return c.json(
195
+ { error: { message: "Request body too large (max 64KB)", type: "invalid_request" } },
196
+ 413
197
+ );
198
+ }
199
+ let body;
200
+ try {
201
+ body = await c.req.json();
202
+ } catch {
203
+ return c.json({ error: { message: "Invalid JSON", type: "invalid_request" } }, 400);
204
+ }
205
+ if (!body.messages?.length) {
206
+ return c.json({ error: { message: "messages array required", type: "invalid_request" } }, 400);
207
+ }
208
+ const spendAuthHeader = c.req.header("X-Payment-Signature");
209
+ const authHeader = c.req.header("Authorization") ?? "";
210
+ let consumerId = null;
211
+ let paymentMethod = "none";
212
+ let keyInfo = null;
213
+ if (spendAuthHeader) {
214
+ const signer = await verifyX402(spendAuthHeader, config.x402, nonceStore);
215
+ if (!signer) {
216
+ return c.json(
217
+ { error: { message: "Invalid X-Payment-Signature", type: "authentication_error", code: "invalid_spend_auth" } },
218
+ { status: 402, headers: { "X-Payment-Required": "spendauth" } }
219
+ );
220
+ }
221
+ consumerId = signer;
222
+ paymentMethod = "x402";
223
+ } else if (config.mpp && authHeader.toLowerCase().startsWith("payment ")) {
224
+ const signer = await verifyMpp(authHeader, config.mpp, config.x402);
225
+ if (!signer) {
226
+ const realm = config.mpp.realm;
227
+ const method = config.mpp.method ?? "blueprintevm";
228
+ return c.json(
229
+ { error: { message: "Invalid Payment credential", type: "authentication_error", code: "invalid_mpp_credential" } },
230
+ { status: 401, headers: { "WWW-Authenticate": `Payment realm="${realm}", method="${method}"` } }
231
+ );
232
+ }
233
+ consumerId = signer;
234
+ paymentMethod = "mpp";
235
+ } else if (authHeader.startsWith("Bearer ")) {
236
+ const verify = config.verifyApiKey ?? defaultVerifyApiKey;
237
+ const key = await verify(authHeader);
238
+ if (!key) {
239
+ return c.json({ error: { message: "Invalid API key", type: "authentication_error" } }, 401);
240
+ }
241
+ if (key.scopes && key.scopes.length > 0 && !key.scopes.includes(requiredScope)) {
242
+ return c.json(
243
+ { error: { message: `API key missing required scope: ${requiredScope}`, type: "forbidden", code: "insufficient_scope" } },
244
+ 403
245
+ );
246
+ }
247
+ consumerId = key.consumerId;
248
+ paymentMethod = "apikey";
249
+ keyInfo = key;
250
+ } else {
251
+ const methods = ["x402"];
252
+ if (config.mpp) methods.push("mpp");
253
+ methods.push("api_key");
254
+ const headers = { "X-Payment-Required": methods.join(", ") };
255
+ if (config.mpp) {
256
+ headers["WWW-Authenticate"] = `Payment realm="${config.mpp.realm}", method="${config.mpp.method ?? "blueprintevm"}"`;
257
+ }
258
+ return c.json({
259
+ error: {
260
+ message: "Payment required",
261
+ type: "payment_required",
262
+ payment_methods: methods,
263
+ x402: {
264
+ operator: config.x402.operatorAddress,
265
+ chain_id: config.x402.chainId,
266
+ credits_address: config.x402.creditsAddress,
267
+ estimated_amount_per_request: "20000"
268
+ },
269
+ ...config.mpp ? {
270
+ mpp: { realm: config.mpp.realm, method: config.mpp.method ?? "blueprintevm" }
271
+ } : {},
272
+ api_key: {
273
+ purchase_url: config.baseUrl ? `${config.baseUrl}/agents/${slug}/api-keys` : void 0
274
+ }
275
+ }
276
+ }, { status: 402, headers });
277
+ }
278
+ const effectiveRateLimit = keyInfo?.rateLimitPerMinute ? { limit: keyInfo.rateLimitPerMinute, windowSeconds: 60 } : globalRateLimit;
279
+ const rl = await checkRateLimit(consumerId, effectiveRateLimit, rateLimitStore);
280
+ if (!rl.allowed) {
281
+ return c.json(
282
+ { error: { message: "Rate limit exceeded", type: "rate_limit_error", retry_after: rl.retryAfterSeconds } },
283
+ { status: 429, headers: { "Retry-After": String(rl.retryAfterSeconds ?? 60) } }
284
+ );
285
+ }
286
+ const { messages: filtered, injectionWarnings } = filterConsumerMessagesStrict(body.messages, maxLen);
287
+ if (injectionWarnings.length > 0) {
288
+ console.warn(`[agent-gateway] injection detected from ${consumerId}: ${injectionWarnings.join(", ")}`);
289
+ if (config.blockInjection) {
290
+ return c.json(
291
+ { error: { message: "Request rejected: potential prompt injection detected", type: "content_policy_violation" } },
292
+ 400
293
+ );
294
+ }
295
+ }
296
+ const userMessage = filtered.filter((m) => m.role === "user").map((m) => m.content).join("\n\n");
297
+ if (!userMessage) {
298
+ return c.json({ error: { message: "No user message provided", type: "invalid_request" } }, 400);
299
+ }
300
+ let inputTokens = Math.ceil(userMessage.length / 4);
301
+ let outputTokens = 0;
302
+ const stream = new ReadableStream({
303
+ async start(controller) {
304
+ const encoder = new TextEncoder();
305
+ const sendChunk = (rawDelta) => {
306
+ const delta = redactSystemPromptFromOutput(rawDelta, agent.systemPrompt);
307
+ outputTokens += Math.ceil(delta.length / 4);
308
+ const chunk = {
309
+ id: `chatcmpl-${Date.now()}`,
310
+ object: "chat.completion.chunk",
311
+ created: Math.floor(Date.now() / 1e3),
312
+ model: agent.slug,
313
+ choices: [{ index: 0, delta: { content: delta }, finish_reason: null }]
314
+ };
315
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
316
+
317
+ `));
318
+ };
319
+ try {
320
+ const box = await config.getSandbox(agent);
321
+ const promptStream = box.streamPrompt(userMessage, {
322
+ sessionId: `consumer:${consumerId}`,
323
+ systemPrompt: agent.systemPrompt
324
+ });
325
+ for await (const event of promptStream) {
326
+ if (event.type === "message.part.updated" && event.data?.part?.type === "text" && event.data.delta) {
327
+ sendChunk(event.data.delta);
328
+ }
329
+ }
330
+ const done = {
331
+ id: `chatcmpl-${Date.now()}`,
332
+ object: "chat.completion.chunk",
333
+ created: Math.floor(Date.now() / 1e3),
334
+ model: agent.slug,
335
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }]
336
+ };
337
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}
338
+
339
+ `));
340
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
341
+ const totalCost = (inputTokens + outputTokens) * agent.pricePerTokenUsd;
342
+ const ownerEarned = totalCost * (1 - agent.platformFeePercent);
343
+ const platformFee = totalCost * agent.platformFeePercent;
344
+ await config.recordUsage({
345
+ agentId: agent.id,
346
+ agentSlug: agent.slug,
347
+ consumerId,
348
+ paymentMethod,
349
+ inputTokens,
350
+ outputTokens,
351
+ totalCostUsd: totalCost,
352
+ ownerEarnedUsd: ownerEarned,
353
+ platformFeeUsd: platformFee,
354
+ durationMs: Date.now() - startMs
355
+ });
356
+ if (config.settlePayment) {
357
+ await config.settlePayment({ method: paymentMethod, consumerId }, totalCost).catch((err) => {
358
+ console.error(`[agent-gateway] settlement failed for ${consumerId}: ${err instanceof Error ? err.message : err}`);
359
+ });
360
+ }
361
+ } catch (err) {
362
+ const safeMessage = err instanceof Error ? err.message.includes("/") || err.message.includes("\\") ? "Internal agent error" : err.message : "Internal agent error";
363
+ controller.enqueue(
364
+ encoder.encode(`data: ${JSON.stringify({ error: { message: safeMessage, type: "server_error" } })}
365
+
366
+ `)
367
+ );
368
+ } finally {
369
+ controller.close();
370
+ }
371
+ }
372
+ });
373
+ return new Response(stream, {
374
+ headers: {
375
+ "Content-Type": "text/event-stream",
376
+ "Cache-Control": "no-cache",
377
+ "X-Agent-Slug": agent.slug,
378
+ "X-Agent-Hosting": agent.sandboxEndpoint ? "sovereign" : "centralized",
379
+ "X-Payment-Method": paymentMethod,
380
+ "X-Payment-Settled": paymentMethod === "x402" ? "pending" : "true",
381
+ ...rl.remaining !== void 0 ? { "X-RateLimit-Remaining": String(rl.remaining) } : {}
382
+ }
383
+ });
384
+ });
385
+ return gw;
386
+ }
387
+
388
+ export {
389
+ verifyX402,
390
+ verifyMpp,
391
+ defaultVerifyApiKey,
392
+ detectInjection,
393
+ filterConsumerMessages,
394
+ filterConsumerMessagesStrict,
395
+ redactSystemPromptFromOutput,
396
+ createAgentGateway
397
+ };
398
+ //# sourceMappingURL=chunk-EMGS63QE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts","../src/verify.ts","../src/filter.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport type {\n GatewayConfig,\n ChatCompletionRequest,\n ChatCompletionChunk,\n PaymentMethod,\n ApiKeyInfo,\n} from './types'\nimport { verifyX402, verifyMpp, defaultVerifyApiKey } from './verify'\nimport { filterConsumerMessagesStrict, redactSystemPromptFromOutput } from './filter'\nimport { checkRateLimit, MemoryRateLimitStore, type RateLimitStore } from './rate-limit'\nimport { MemoryNonceStore } from './nonce-store'\n\n/**\n * Create a Hono router that serves the agent gateway.\n *\n * Mount at any path:\n * app.route('/v1/agents', createAgentGateway(config))\n *\n * Exposes:\n * GET /:slug/chat/completions — agent discovery metadata\n * POST /:slug/chat/completions — OpenAI-compatible chat endpoint (paid)\n */\nexport function createAgentGateway(config: GatewayConfig) {\n const gw = new Hono()\n const maxLen = config.maxMessageLength ?? 8000\n const rateLimitStore: RateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore()\n const globalRateLimit = config.rateLimit ?? { limit: 60, windowSeconds: 60 }\n const nonceStore = config.nonceStore ?? new MemoryNonceStore()\n const requiredScope = config.requiredScope ?? 'chat'\n\n // --- Discovery endpoint (no auth) ---\n\n gw.get('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const agent = await config.resolveAgent(slug)\n if (!agent) return c.json({ error: 'Agent not found or not published' }, 404)\n\n const paymentMethods: Array<Record<string, unknown>> = [\n {\n type: 'x402',\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_contract: config.x402.creditsAddress,\n },\n ]\n if (config.mpp) {\n paymentMethods.push({\n type: 'mpp',\n realm: config.mpp.realm,\n method: config.mpp.method ?? 'blueprintevm',\n })\n }\n paymentMethods.push({ type: 'api_key', prefix: 'sk_agent_' })\n\n return c.json({\n slug: agent.slug,\n pricing: {\n per_token_usd: agent.pricePerTokenUsd,\n currency: 'USD',\n platform_fee_percent: agent.platformFeePercent,\n },\n hosting: {\n mode: agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n endpoint: agent.sandboxEndpoint ?? config.baseUrl ?? 'tangle.tools',\n },\n payment_methods: paymentMethods,\n capabilities: ['chat.completions', 'streaming'],\n openai_compatible: true,\n })\n })\n\n // --- Chat completions endpoint (paid) ---\n\n gw.post('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const startMs = Date.now()\n\n // 1. Resolve agent\n const agent = await config.resolveAgent(slug)\n if (!agent) {\n return c.json({ error: { message: 'Agent not found', type: 'not_found' } }, 404)\n }\n\n // 2. Body size limit (before parsing — DoS prevention)\n const contentLength = parseInt(c.req.header('Content-Length') ?? '0', 10)\n if (contentLength > 65536) {\n return c.json(\n { error: { message: 'Request body too large (max 64KB)', type: 'invalid_request' } },\n 413,\n )\n }\n\n let body: ChatCompletionRequest\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: { message: 'Invalid JSON', type: 'invalid_request' } }, 400)\n }\n if (!body.messages?.length) {\n return c.json({ error: { message: 'messages array required', type: 'invalid_request' } }, 400)\n }\n\n // 3. Authenticate — x402 SpendAuth, MPP, or API key\n const spendAuthHeader = c.req.header('X-Payment-Signature')\n const authHeader = c.req.header('Authorization') ?? ''\n let consumerId: string | null = null\n let paymentMethod: PaymentMethod = 'none'\n let keyInfo: ApiKeyInfo | null = null\n\n if (spendAuthHeader) {\n const signer = await verifyX402(spendAuthHeader, config.x402, nonceStore)\n if (!signer) {\n return c.json(\n { error: { message: 'Invalid X-Payment-Signature', type: 'authentication_error', code: 'invalid_spend_auth' } },\n { status: 402, headers: { 'X-Payment-Required': 'spendauth' } },\n )\n }\n consumerId = signer\n paymentMethod = 'x402'\n } else if (config.mpp && authHeader.toLowerCase().startsWith('payment ')) {\n const signer = await verifyMpp(authHeader, config.mpp, config.x402)\n if (!signer) {\n const realm = config.mpp.realm\n const method = config.mpp.method ?? 'blueprintevm'\n return c.json(\n { error: { message: 'Invalid Payment credential', type: 'authentication_error', code: 'invalid_mpp_credential' } },\n { status: 401, headers: { 'WWW-Authenticate': `Payment realm=\"${realm}\", method=\"${method}\"` } },\n )\n }\n consumerId = signer\n paymentMethod = 'mpp'\n } else if (authHeader.startsWith('Bearer ')) {\n const verify = config.verifyApiKey ?? defaultVerifyApiKey\n const key = await verify(authHeader)\n if (!key) {\n return c.json({ error: { message: 'Invalid API key', type: 'authentication_error' } }, 401)\n }\n\n // Scope enforcement — API key must include the required scope\n if (key.scopes && key.scopes.length > 0 && !key.scopes.includes(requiredScope)) {\n return c.json(\n { error: { message: `API key missing required scope: ${requiredScope}`, type: 'forbidden', code: 'insufficient_scope' } },\n 403,\n )\n }\n\n consumerId = key.consumerId\n paymentMethod = 'apikey'\n keyInfo = key\n } else {\n // No payment — return 402 with instructions\n const methods: string[] = ['x402']\n if (config.mpp) methods.push('mpp')\n methods.push('api_key')\n\n const headers: Record<string, string> = { 'X-Payment-Required': methods.join(', ') }\n if (config.mpp) {\n headers['WWW-Authenticate'] = `Payment realm=\"${config.mpp.realm}\", method=\"${config.mpp.method ?? 'blueprintevm'}\"`\n }\n\n return c.json({\n error: {\n message: 'Payment required',\n type: 'payment_required',\n payment_methods: methods,\n x402: {\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_address: config.x402.creditsAddress,\n estimated_amount_per_request: '20000',\n },\n ...(config.mpp ? {\n mpp: { realm: config.mpp.realm, method: config.mpp.method ?? 'blueprintevm' },\n } : {}),\n api_key: {\n purchase_url: config.baseUrl ? `${config.baseUrl}/agents/${slug}/api-keys` : undefined,\n },\n },\n }, { status: 402, headers })\n }\n\n // 4. Rate limit — per-key override or global\n const effectiveRateLimit = keyInfo?.rateLimitPerMinute\n ? { limit: keyInfo.rateLimitPerMinute, windowSeconds: 60 }\n : globalRateLimit\n\n const rl = await checkRateLimit(consumerId!, effectiveRateLimit, rateLimitStore)\n if (!rl.allowed) {\n return c.json(\n { error: { message: 'Rate limit exceeded', type: 'rate_limit_error', retry_after: rl.retryAfterSeconds } },\n { status: 429, headers: { 'Retry-After': String(rl.retryAfterSeconds ?? 60) } },\n )\n }\n\n // 5. Filter messages — injection detection + sanitization\n const { messages: filtered, injectionWarnings } = filterConsumerMessagesStrict(body.messages, maxLen)\n\n if (injectionWarnings.length > 0) {\n // Log injection attempt\n console.warn(`[agent-gateway] injection detected from ${consumerId}: ${injectionWarnings.join(', ')}`)\n\n if (config.blockInjection) {\n return c.json(\n { error: { message: 'Request rejected: potential prompt injection detected', type: 'content_policy_violation' } },\n 400,\n )\n }\n // In non-blocking mode, continue but the warning is logged for auditing\n }\n\n const userMessage = filtered\n .filter((m) => m.role === 'user')\n .map((m) => m.content)\n .join('\\n\\n')\n\n if (!userMessage) {\n return c.json({ error: { message: 'No user message provided', type: 'invalid_request' } }, 400)\n }\n\n // 6. Get sandbox and stream response with output filtering\n let inputTokens = Math.ceil(userMessage.length / 4)\n let outputTokens = 0\n\n const stream = new ReadableStream({\n async start(controller) {\n const encoder = new TextEncoder()\n const sendChunk = (rawDelta: string) => {\n // Redact system prompt leakage from output\n const delta = redactSystemPromptFromOutput(rawDelta, agent.systemPrompt)\n outputTokens += Math.ceil(delta.length / 4)\n const chunk: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: { content: delta }, finish_reason: null }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`))\n }\n\n try {\n const box = await config.getSandbox(agent)\n const promptStream = box.streamPrompt(userMessage, {\n sessionId: `consumer:${consumerId}`,\n systemPrompt: agent.systemPrompt,\n })\n\n for await (const event of promptStream) {\n if (\n event.type === 'message.part.updated' &&\n event.data?.part?.type === 'text' &&\n event.data.delta\n ) {\n sendChunk(event.data.delta)\n }\n }\n\n // Final chunk\n const done: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}\\n\\n`))\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n\n // 7. Record usage + settle payment\n const totalCost = (inputTokens + outputTokens) * agent.pricePerTokenUsd\n const ownerEarned = totalCost * (1 - agent.platformFeePercent)\n const platformFee = totalCost * agent.platformFeePercent\n\n await config.recordUsage({\n agentId: agent.id,\n agentSlug: agent.slug,\n consumerId: consumerId!,\n paymentMethod,\n inputTokens,\n outputTokens,\n totalCostUsd: totalCost,\n ownerEarnedUsd: ownerEarned,\n platformFeeUsd: platformFee,\n durationMs: Date.now() - startMs,\n })\n\n if (config.settlePayment) {\n await config.settlePayment({ method: paymentMethod, consumerId: consumerId! }, totalCost).catch(err => {\n console.error(`[agent-gateway] settlement failed for ${consumerId}: ${err instanceof Error ? err.message : err}`)\n })\n }\n } catch (err) {\n // Sanitize error — never expose stack traces or internal paths\n const safeMessage = err instanceof Error\n ? (err.message.includes('/') || err.message.includes('\\\\') ? 'Internal agent error' : err.message)\n : 'Internal agent error'\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ error: { message: safeMessage, type: 'server_error' } })}\\n\\n`),\n )\n } finally {\n controller.close()\n }\n },\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'X-Agent-Slug': agent.slug,\n 'X-Agent-Hosting': agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n 'X-Payment-Method': paymentMethod,\n 'X-Payment-Settled': paymentMethod === 'x402' ? 'pending' : 'true',\n ...(rl.remaining !== undefined ? { 'X-RateLimit-Remaining': String(rl.remaining) } : {}),\n },\n })\n })\n\n return gw\n}\n","import type { X402Config, MppConfig, ApiKeyInfo } from './types'\nimport type { NonceStore } from './nonce-store'\n\n/**\n * Verify x402 SpendAuth signature (EIP-712).\n * Returns the signer address (commitment) if valid, null otherwise.\n *\n * DEMO MODE (demoMode: true): accepts any well-formed header structure.\n * PRODUCTION: requires config.verifySigner callback for on-chain verification.\n */\nexport async function verifyX402(\n spendAuthHeader: string,\n config: X402Config,\n nonceStore?: NonceStore,\n): Promise<string | null> {\n try {\n const raw = JSON.parse(spendAuthHeader)\n if (!raw.commitment || !raw.signature || !raw.amount) return null\n if (raw.operator?.toLowerCase() !== config.operatorAddress.toLowerCase()) return null\n\n const amount = BigInt(raw.amount)\n const nonce = BigInt(raw.nonce)\n const expiry = BigInt(raw.expiry)\n\n // Reject expired payments\n if (expiry < BigInt(Math.floor(Date.now() / 1000))) return null\n\n // Reject zero-amount payments\n if (amount <= 0n) return null\n\n // Reject replayed nonces\n const nonceKey = `${raw.commitment}:${nonce.toString()}`\n if (nonceStore) {\n if (await nonceStore.hasSeen(nonceKey)) return null\n // Mark seen with TTL matching the expiry window (max 1 hour)\n const ttl = Math.min(Number(expiry) - Math.floor(Date.now() / 1000), 3600)\n await nonceStore.markSeen(nonceKey, Math.max(ttl, 60))\n }\n\n // Production: delegate to on-chain verification\n if (config.verifySigner) {\n const verified = await config.verifySigner(raw)\n if (!verified) return null\n } else if (!config.demoMode) {\n console.warn('[agent-gateway] x402 verification running without verifySigner — set demoMode: true to suppress this warning')\n }\n\n return raw.commitment\n } catch {\n return null\n }\n}\n\n/**\n * Verify MPP (Machine Payments Protocol) Authorization: Payment header.\n *\n * MPP uses `Authorization: Payment <method> <credential>` format where\n * the credential is a base64url-encoded JSON wrapping the same EIP-3009\n * payment payload that x402 uses. This means existing x402 wallets work\n * unchanged over the MPP wire format.\n *\n * Returns the signer address if valid, null otherwise.\n * In demo mode, accepts any well-formed Payment header.\n */\nexport async function verifyMpp(\n authHeader: string,\n _config: MppConfig,\n x402Config: X402Config,\n): Promise<string | null> {\n // MPP format: \"Payment <method> <base64url-credential>\"\n const match = authHeader.match(/^Payment\\s+(\\S+)\\s+(\\S+)$/i)\n if (!match) return null\n\n const [, , credentialB64] = match\n\n try {\n // Decode base64url credential → JSON with the same EIP-3009 payload\n const decoded = Buffer.from(credentialB64, 'base64url').toString('utf-8')\n const credential = JSON.parse(decoded)\n\n // The credential payload wraps the same fields x402 uses\n const payload = credential.payload ?? credential\n if (!payload.commitment && !payload.from) return null\n\n // Validate operator match (same as x402)\n const operator = payload.operator ?? payload.to\n if (operator && operator.toLowerCase() !== x402Config.operatorAddress.toLowerCase()) return null\n\n // Validate bigint fields if present\n if (payload.amount) BigInt(payload.amount)\n if (payload.nonce) BigInt(payload.nonce)\n\n return payload.commitment ?? payload.from ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Default API key verifier — accepts any `sk_agent_*` key (demo mode).\n * Override in GatewayConfig.verifyApiKey for production.\n */\nexport async function defaultVerifyApiKey(\n authHeader: string,\n): Promise<ApiKeyInfo | null> {\n if (!authHeader.startsWith('Bearer sk_agent_')) return null\n const key = authHeader.slice(7)\n return {\n keyId: key.slice(0, 16),\n consumerId: `apikey:${key.slice(0, 16)}`,\n }\n}\n","import type { ChatMessage } from './types'\n\n// --- Injection detection patterns ---\n\nconst INJECTION_PATTERNS = [\n // Direct instruction override\n /ignore\\s+(all\\s+)?(previous|prior|above|earlier)\\s+(instructions?|prompts?|rules?|directives?)/i,\n /disregard\\s+(all\\s+)?(previous|prior|system)/i,\n /forget\\s+(everything|all|your)\\s+(previous|instructions?|training)/i,\n // Role assumption\n /you\\s+are\\s+now\\s+(a|an|the)\\s+/i,\n /pretend\\s+(you\\s+are|to\\s+be)\\s+/i,\n /act\\s+as\\s+(if\\s+you\\s+are|a|an|the)\\s+/i,\n /new\\s+instructions?:/i,\n /\\[system\\]/i,\n /\\[INST\\]/i,\n // Prompt extraction\n /what\\s+(is|are)\\s+your\\s+(system\\s+)?(prompt|instructions?|rules?|directives?)/i,\n /repeat\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?)/i,\n /output\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?)/i,\n /show\\s+me\\s+(your|the)\\s+(system|hidden|secret)\\s+(prompt|instructions?|message)/i,\n // Data exfiltration\n /read\\s+(the\\s+)?(vault|workspace|config|secret|\\.env)/i,\n /cat\\s+\\/home\\/agent\\/(vault|config|\\.env|secrets?)/i,\n /list\\s+(all\\s+)?(vault|workspace|secret)\\s+(files?|contents?|data)/i,\n]\n\n// Unicode normalization — collapse homoglyphs and zero-width chars\nfunction normalizeUnicode(text: string): string {\n return text\n // Remove zero-width chars (ZWJ, ZWNJ, ZWS, ZWSP)\n .replace(/[\\u200B-\\u200F\\u2028-\\u202F\\u2060\\uFEFF]/g, '')\n // Normalize to NFKC (collapses homoglyphs like а→a, е→e)\n .normalize('NFKC')\n}\n\n/**\n * Detect prompt injection attempts.\n * Returns array of matched pattern descriptions, empty if clean.\n */\nexport function detectInjection(content: string): string[] {\n const normalized = normalizeUnicode(content)\n const matches: string[] = []\n\n for (const pattern of INJECTION_PATTERNS) {\n if (pattern.test(normalized)) {\n matches.push(pattern.source.slice(0, 60))\n }\n }\n\n // Check for base64-encoded injection attempts\n const b64Matches = normalized.match(/[A-Za-z0-9+/]{40,}={0,2}/g)\n if (b64Matches) {\n for (const b64 of b64Matches) {\n try {\n const decoded = atob(b64)\n if (INJECTION_PATTERNS.some(p => p.test(decoded))) {\n matches.push('base64-encoded injection')\n }\n } catch { /* not valid b64 */ }\n }\n }\n\n return matches\n}\n\n/**\n * Security boundary — filter consumer messages before forwarding to agent.\n *\n * Defense in depth:\n * 1. Strip system messages (consumers cannot set system prompt)\n * 2. Normalize Unicode (collapse homoglyphs, remove zero-width chars)\n * 3. Detect injection patterns (instruction override, prompt extraction, data exfil)\n * 4. Redact sensitive keywords\n * 5. Cap message length\n *\n * Returns filtered messages and any injection warnings detected.\n */\nexport function filterConsumerMessages(\n messages: ChatMessage[],\n maxLength = 8000,\n): ChatMessage[] {\n return messages\n .filter((m) => m.role !== 'system')\n .map((m) => {\n const normalized = normalizeUnicode(m.content)\n const redacted = normalized\n .replace(/\\b(vault|workspace|owner|admin|secret|\\.env|config\\.json)[\\s/:][^\\s]*/gi, '[REDACTED]')\n .slice(0, maxLength)\n return { role: m.role, content: redacted }\n })\n}\n\n/**\n * Filter consumer messages with injection detection.\n * Returns { messages, injectionWarnings }.\n * If injectionWarnings is non-empty, the gateway should log and optionally reject.\n */\nexport function filterConsumerMessagesStrict(\n messages: ChatMessage[],\n maxLength = 8000,\n): { messages: ChatMessage[]; injectionWarnings: string[] } {\n const filtered = filterConsumerMessages(messages, maxLength)\n const allContent = filtered.map(m => m.content).join(' ')\n const injectionWarnings = detectInjection(allContent)\n return { messages: filtered, injectionWarnings }\n}\n\n/**\n * Redact system prompt content from agent output.\n * Prevents the agent from leaking its own instructions in responses.\n *\n * Strategy: if any chunk of the system prompt appears verbatim (>40 chars)\n * in the output, replace it with [REDACTED].\n */\nexport function redactSystemPromptFromOutput(\n output: string,\n systemPrompt: string | undefined,\n): string {\n if (!systemPrompt || systemPrompt.length < 40) return output\n\n // Split system prompt into meaningful chunks (sentences or lines)\n const chunks = systemPrompt\n .split(/[.\\n]/)\n .map(s => s.trim())\n .filter(s => s.length >= 40)\n\n let redacted = output\n for (const chunk of chunks) {\n // Case-insensitive substring match\n const idx = redacted.toLowerCase().indexOf(chunk.toLowerCase())\n if (idx >= 0) {\n redacted = redacted.slice(0, idx) + '[REDACTED — system instructions]' + redacted.slice(idx + chunk.length)\n }\n }\n\n return redacted\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,33 @@
1
+ // src/nonce-store.ts
2
+ var MemoryNonceStore = class {
3
+ seen = /* @__PURE__ */ new Map();
4
+ // nonce → expiresAt
5
+ lastEviction = Date.now();
6
+ async hasSeen(nonce) {
7
+ this.evictExpired();
8
+ const expiresAt = this.seen.get(nonce);
9
+ if (!expiresAt) return false;
10
+ if (expiresAt < Date.now()) {
11
+ this.seen.delete(nonce);
12
+ return false;
13
+ }
14
+ return true;
15
+ }
16
+ async markSeen(nonce, ttlSeconds) {
17
+ this.seen.set(nonce, Date.now() + ttlSeconds * 1e3);
18
+ this.evictExpired();
19
+ }
20
+ evictExpired() {
21
+ const now = Date.now();
22
+ if (now - this.lastEviction < 6e4) return;
23
+ this.lastEviction = now;
24
+ for (const [nonce, expiresAt] of this.seen) {
25
+ if (expiresAt < now) this.seen.delete(nonce);
26
+ }
27
+ }
28
+ };
29
+
30
+ export {
31
+ MemoryNonceStore
32
+ };
33
+ //# sourceMappingURL=chunk-Z22ALGHW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/nonce-store.ts"],"sourcesContent":["/**\n * Nonce replay protection for x402/MPP payments.\n * Tracks seen nonces to prevent the same payment from being used twice.\n */\n\nexport interface NonceStore {\n /** Check if nonce has been seen. Returns true if already used (reject). */\n hasSeen(nonce: string): Promise<boolean>\n /** Mark nonce as used. TTL = how long to remember it (seconds). */\n markSeen(nonce: string, ttlSeconds: number): Promise<void>\n}\n\n/** In-memory nonce store with automatic eviction */\nexport class MemoryNonceStore implements NonceStore {\n private seen = new Map<string, number>() // nonce → expiresAt\n private lastEviction = Date.now()\n\n async hasSeen(nonce: string): Promise<boolean> {\n this.evictExpired()\n const expiresAt = this.seen.get(nonce)\n if (!expiresAt) return false\n if (expiresAt < Date.now()) {\n this.seen.delete(nonce)\n return false\n }\n return true\n }\n\n async markSeen(nonce: string, ttlSeconds: number): Promise<void> {\n this.seen.set(nonce, Date.now() + ttlSeconds * 1000)\n this.evictExpired()\n }\n\n private evictExpired() {\n const now = Date.now()\n // Evict at most every 60 seconds to avoid O(n) on every request\n if (now - this.lastEviction < 60_000) return\n this.lastEviction = now\n for (const [nonce, expiresAt] of this.seen) {\n if (expiresAt < now) this.seen.delete(nonce)\n }\n }\n}\n"],"mappings":";AAaO,IAAM,mBAAN,MAA6C;AAAA,EAC1C,OAAO,oBAAI,IAAoB;AAAA;AAAA,EAC/B,eAAe,KAAK,IAAI;AAAA,EAEhC,MAAM,QAAQ,OAAiC;AAC7C,SAAK,aAAa;AAClB,UAAM,YAAY,KAAK,KAAK,IAAI,KAAK;AACrC,QAAI,CAAC,UAAW,QAAO;AACvB,QAAI,YAAY,KAAK,IAAI,GAAG;AAC1B,WAAK,KAAK,OAAO,KAAK;AACtB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,OAAe,YAAmC;AAC/D,SAAK,KAAK,IAAI,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI;AACnD,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAe;AACrB,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,MAAM,KAAK,eAAe,IAAQ;AACtC,SAAK,eAAe;AACpB,eAAW,CAAC,OAAO,SAAS,KAAK,KAAK,MAAM;AAC1C,UAAI,YAAY,IAAK,MAAK,KAAK,OAAO,KAAK;AAAA,IAC7C;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,74 @@
1
+ export { createAgentGateway } from './middleware.js';
2
+ import { ApiKeyInfo, MppConfig, X402Config, ChatMessage } from './types.js';
3
+ export { AgentMeta, ChatCompletionChunk, ChatCompletionRequest, GatewayConfig, GatewayUsageEvent, PaymentMethod, PaymentResult, SandboxBox, SandboxStreamEvent } from './types.js';
4
+ import { NonceStore } from './nonce-store.js';
5
+ export { MemoryNonceStore } from './nonce-store.js';
6
+ export { MemoryRateLimitStore, RateLimitConfig, RateLimitResult, RateLimitStore, checkRateLimit } from './rate-limit.js';
7
+ export { ApiKey, ApiKeyCreateRequest, ApiKeyRoutesConfig, ApiKeyStore, createApiKeyRoutes, verifyApiKeyFromStore } from './api-keys.js';
8
+ export { PublishRequest, PublishRoutesConfig, PublishStore, PublishedConfig, createPublishRoutes } from './publish.js';
9
+ import 'hono/types';
10
+ import 'hono';
11
+
12
+ /**
13
+ * Verify x402 SpendAuth signature (EIP-712).
14
+ * Returns the signer address (commitment) if valid, null otherwise.
15
+ *
16
+ * DEMO MODE (demoMode: true): accepts any well-formed header structure.
17
+ * PRODUCTION: requires config.verifySigner callback for on-chain verification.
18
+ */
19
+ declare function verifyX402(spendAuthHeader: string, config: X402Config, nonceStore?: NonceStore): Promise<string | null>;
20
+ /**
21
+ * Verify MPP (Machine Payments Protocol) Authorization: Payment header.
22
+ *
23
+ * MPP uses `Authorization: Payment <method> <credential>` format where
24
+ * the credential is a base64url-encoded JSON wrapping the same EIP-3009
25
+ * payment payload that x402 uses. This means existing x402 wallets work
26
+ * unchanged over the MPP wire format.
27
+ *
28
+ * Returns the signer address if valid, null otherwise.
29
+ * In demo mode, accepts any well-formed Payment header.
30
+ */
31
+ declare function verifyMpp(authHeader: string, _config: MppConfig, x402Config: X402Config): Promise<string | null>;
32
+ /**
33
+ * Default API key verifier — accepts any `sk_agent_*` key (demo mode).
34
+ * Override in GatewayConfig.verifyApiKey for production.
35
+ */
36
+ declare function defaultVerifyApiKey(authHeader: string): Promise<ApiKeyInfo | null>;
37
+
38
+ /**
39
+ * Detect prompt injection attempts.
40
+ * Returns array of matched pattern descriptions, empty if clean.
41
+ */
42
+ declare function detectInjection(content: string): string[];
43
+ /**
44
+ * Security boundary — filter consumer messages before forwarding to agent.
45
+ *
46
+ * Defense in depth:
47
+ * 1. Strip system messages (consumers cannot set system prompt)
48
+ * 2. Normalize Unicode (collapse homoglyphs, remove zero-width chars)
49
+ * 3. Detect injection patterns (instruction override, prompt extraction, data exfil)
50
+ * 4. Redact sensitive keywords
51
+ * 5. Cap message length
52
+ *
53
+ * Returns filtered messages and any injection warnings detected.
54
+ */
55
+ declare function filterConsumerMessages(messages: ChatMessage[], maxLength?: number): ChatMessage[];
56
+ /**
57
+ * Filter consumer messages with injection detection.
58
+ * Returns { messages, injectionWarnings }.
59
+ * If injectionWarnings is non-empty, the gateway should log and optionally reject.
60
+ */
61
+ declare function filterConsumerMessagesStrict(messages: ChatMessage[], maxLength?: number): {
62
+ messages: ChatMessage[];
63
+ injectionWarnings: string[];
64
+ };
65
+ /**
66
+ * Redact system prompt content from agent output.
67
+ * Prevents the agent from leaking its own instructions in responses.
68
+ *
69
+ * Strategy: if any chunk of the system prompt appears verbatim (>40 chars)
70
+ * in the output, replace it with [REDACTED].
71
+ */
72
+ declare function redactSystemPromptFromOutput(output: string, systemPrompt: string | undefined): string;
73
+
74
+ export { ApiKeyInfo, ChatMessage, MppConfig, NonceStore, X402Config, defaultVerifyApiKey, detectInjection, filterConsumerMessages, filterConsumerMessagesStrict, redactSystemPromptFromOutput, verifyMpp, verifyX402 };
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import {
2
+ createApiKeyRoutes,
3
+ verifyApiKeyFromStore
4
+ } from "./chunk-5O75YDQP.js";
5
+ import {
6
+ createAgentGateway,
7
+ defaultVerifyApiKey,
8
+ detectInjection,
9
+ filterConsumerMessages,
10
+ filterConsumerMessagesStrict,
11
+ redactSystemPromptFromOutput,
12
+ verifyMpp,
13
+ verifyX402
14
+ } from "./chunk-EMGS63QE.js";
15
+ import {
16
+ MemoryNonceStore
17
+ } from "./chunk-Z22ALGHW.js";
18
+ import {
19
+ createPublishRoutes
20
+ } from "./chunk-5ZJPIIIV.js";
21
+ import {
22
+ MemoryRateLimitStore,
23
+ checkRateLimit
24
+ } from "./chunk-4FULF5LW.js";
25
+ export {
26
+ MemoryNonceStore,
27
+ MemoryRateLimitStore,
28
+ checkRateLimit,
29
+ createAgentGateway,
30
+ createApiKeyRoutes,
31
+ createPublishRoutes,
32
+ defaultVerifyApiKey,
33
+ detectInjection,
34
+ filterConsumerMessages,
35
+ filterConsumerMessagesStrict,
36
+ redactSystemPromptFromOutput,
37
+ verifyApiKeyFromStore,
38
+ verifyMpp,
39
+ verifyX402
40
+ };
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,19 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+ import { GatewayConfig } from './types.js';
4
+ import './nonce-store.js';
5
+ import './rate-limit.js';
6
+
7
+ /**
8
+ * Create a Hono router that serves the agent gateway.
9
+ *
10
+ * Mount at any path:
11
+ * app.route('/v1/agents', createAgentGateway(config))
12
+ *
13
+ * Exposes:
14
+ * GET /:slug/chat/completions — agent discovery metadata
15
+ * POST /:slug/chat/completions — OpenAI-compatible chat endpoint (paid)
16
+ */
17
+ declare function createAgentGateway(config: GatewayConfig): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
18
+
19
+ export { createAgentGateway };
@@ -0,0 +1,9 @@
1
+ import {
2
+ createAgentGateway
3
+ } from "./chunk-EMGS63QE.js";
4
+ import "./chunk-Z22ALGHW.js";
5
+ import "./chunk-4FULF5LW.js";
6
+ export {
7
+ createAgentGateway
8
+ };
9
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}