@clawcash/forge 0.1.3 → 0.1.5

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 CHANGED
@@ -1,9 +1,13 @@
1
1
  # @clawcash/forge
2
2
 
3
- Define agent-native services on top of an existing backend. Forge wraps a high-level action, handles polling helpers, mounts Express routes, and attaches x402 payment.
3
+ The fastest way to make an **existing** SaaS/API app agent-native.
4
+
5
+ Define a high-level agent action on top of your backend, mount Express routes, attach x402, and register with the ClawCash gateway. Humans keep the same product; agents get a paid one-shot.
4
6
 
5
7
  Published under the [ClawCash](https://www.npmjs.com/org/clawcash) npm org.
6
8
 
9
+ **Stack note:** `defineAgentService` / payTo / heartbeat are framework-agnostic. `mountAgentServices` is the **Express** adapter today. Next/Nest ship later as thin adapters (`@clawcash/forge/next`, etc.) — not one mega environment-agnostic package. A separate greenfield “build agent-native apps” SDK is a future product, not this one.
10
+
7
11
  ## Install
8
12
 
9
13
  ```bash
package/dist/index.cjs CHANGED
@@ -20,12 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ FORGE_GATEWAY_USER_ID: () => FORGE_GATEWAY_USER_ID,
23
24
  createContext: () => createContext,
24
25
  createForgeRouter: () => createForgeRouter,
25
26
  defineAgentService: () => defineAgentService,
27
+ forgeGatewayLoopbackHeaders: () => forgeGatewayLoopbackHeaders,
26
28
  generateSkillMarkdown: () => generateSkillMarkdown,
29
+ getForgeGatewaySecret: () => getForgeGatewaySecret,
30
+ heartbeatGateway: () => heartbeatGateway,
31
+ isForgeGatewayRequest: () => isForgeGatewayRequest,
27
32
  isZeroAddress: () => isZeroAddress,
28
33
  mountAgentServices: () => mountAgentServices,
34
+ requireForgeGateway: () => requireForgeGateway,
29
35
  resolvePayTo: () => resolvePayTo,
30
36
  toKebabCase: () => toKebabCase,
31
37
  waitUntil: () => waitUntil
@@ -76,6 +82,63 @@ var import_express2 = require("@x402/express");
76
82
  var import_server = require("@x402/core/server");
77
83
  var import_server2 = require("@x402/evm/exact/server");
78
84
 
85
+ // src/gateway.ts
86
+ async function heartbeatGateway(opts) {
87
+ const gateway = opts.gatewayUrl.replace(/\/$/, "");
88
+ const res = await fetch(`${gateway}/register/heartbeat`, {
89
+ method: "POST",
90
+ headers: {
91
+ "content-type": "application/json",
92
+ authorization: `Bearer ${opts.registerSecret}`
93
+ },
94
+ body: JSON.stringify({
95
+ handle: opts.handle.trim().toLowerCase(),
96
+ baseUrl: opts.baseUrl.replace(/\/$/, "")
97
+ })
98
+ });
99
+ if (!res.ok) {
100
+ const text = await res.text();
101
+ throw new Error(
102
+ `gateway heartbeat failed (${res.status}): ${text.slice(0, 240)}`
103
+ );
104
+ }
105
+ }
106
+
107
+ // src/gateway-auth.ts
108
+ function getForgeGatewaySecret(explicit) {
109
+ const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
110
+ if (!value || value.length < 16) return null;
111
+ return value;
112
+ }
113
+ function isForgeGatewayRequest(req, secret) {
114
+ const expected = getForgeGatewaySecret(secret);
115
+ if (!expected) return false;
116
+ const header = req.header("authorization") ?? "";
117
+ const bearer = header.toLowerCase().startsWith("bearer ") ? header.slice(7).trim() : "";
118
+ const alt = req.header("x-clawcash-gateway")?.trim() ?? "";
119
+ return bearer === expected || alt === expected;
120
+ }
121
+ var FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
122
+ function requireForgeGateway(secret) {
123
+ return (req, res, next) => {
124
+ if (!isForgeGatewayRequest(req, secret)) {
125
+ res.status(401).json({
126
+ error: "Unauthorized \u2014 ClawCash gateway secret required (GATEWAY_REGISTER_SECRET)"
127
+ });
128
+ return;
129
+ }
130
+ next();
131
+ };
132
+ }
133
+ function forgeGatewayLoopbackHeaders(secret) {
134
+ const expected = getForgeGatewaySecret(secret);
135
+ if (!expected) return {};
136
+ return {
137
+ Authorization: `Bearer ${expected}`,
138
+ "X-ClawCash-Gateway": expected
139
+ };
140
+ }
141
+
79
142
  // src/payto.ts
80
143
  var ZERO = /^0x0{40}$/i;
81
144
  function isZeroAddress(address) {
@@ -197,6 +260,44 @@ function formatPrice(amount) {
197
260
  const normalized = amount.startsWith("$") ? amount.slice(1) : amount;
198
261
  return `$${normalized}`;
199
262
  }
263
+ function mountFulfillmentRouter(app, services, options) {
264
+ const secret = getForgeGatewaySecret(options.gateway?.registerSecret);
265
+ if (!secret) {
266
+ console.warn(
267
+ "[forge] /forge/fulfill not mounted \u2014 set GATEWAY_REGISTER_SECRET (min 16 chars) so the gateway can fulfill after payment"
268
+ );
269
+ return;
270
+ }
271
+ const fulfillPath = (options.fulfillPath ?? "/forge/fulfill").replace(/\/$/, "") || "/forge/fulfill";
272
+ const router = (0, import_express.Router)();
273
+ const context = createContext();
274
+ const gate = requireForgeGateway(secret);
275
+ for (const service of services) {
276
+ router.post(
277
+ `/${service.path}`,
278
+ gate,
279
+ async (req, res) => {
280
+ try {
281
+ const validated = validateInput(req.body, service.input);
282
+ if (!validated.ok) {
283
+ res.status(400).json({ error: validated.error });
284
+ return;
285
+ }
286
+ const output = await service.execute(validated.value, context);
287
+ res.json(output);
288
+ } catch (err) {
289
+ const message = err instanceof Error ? err.message : "Internal error";
290
+ console.error(`[forge] fulfill ${service.name} failed:`, err);
291
+ res.status(500).json({ error: message });
292
+ }
293
+ }
294
+ );
295
+ }
296
+ app.use(fulfillPath, router);
297
+ console.log(
298
+ `[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`
299
+ );
300
+ }
200
301
  async function mountAgentServices(app, services, options) {
201
302
  let payTo = "";
202
303
  if (!options.skipPayment) {
@@ -260,6 +361,21 @@ async function mountAgentServices(app, services, options) {
260
361
  app.use((0, import_express2.paymentMiddleware)(routes, resourceServer, void 0, void 0, true));
261
362
  }
262
363
  app.use(basePath, router);
364
+ mountFulfillmentRouter(app, services, options);
365
+ const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();
366
+ const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();
367
+ const registerSecret = options.gateway?.registerSecret?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
368
+ const publicBaseUrl = options.gateway?.publicBaseUrl?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.RAILWAY_PUBLIC_DOMAIN ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}` : "");
369
+ if (gatewayUrl && handle && registerSecret && publicBaseUrl) {
370
+ void heartbeatGateway({
371
+ gatewayUrl,
372
+ handle,
373
+ baseUrl: publicBaseUrl,
374
+ registerSecret
375
+ }).catch((err) => {
376
+ console.warn("[forge] gateway heartbeat skipped:", err);
377
+ });
378
+ }
263
379
  return router;
264
380
  }
265
381
  function createForgeRouter(services, options) {
@@ -351,12 +467,18 @@ function generateSkillMarkdown(services, meta) {
351
467
  }
352
468
  // Annotate the CommonJS export names for ESM import in node:
353
469
  0 && (module.exports = {
470
+ FORGE_GATEWAY_USER_ID,
354
471
  createContext,
355
472
  createForgeRouter,
356
473
  defineAgentService,
474
+ forgeGatewayLoopbackHeaders,
357
475
  generateSkillMarkdown,
476
+ getForgeGatewaySecret,
477
+ heartbeatGateway,
478
+ isForgeGatewayRequest,
358
479
  isZeroAddress,
359
480
  mountAgentServices,
481
+ requireForgeGateway,
360
482
  resolvePayTo,
361
483
  toKebabCase,
362
484
  waitUntil
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/define.ts","../src/mount.ts","../src/payto.ts","../src/skill.ts"],"sourcesContent":["export { defineAgentService, waitUntil, createContext, toKebabCase } from \"./define.js\";\nexport { mountAgentServices, createForgeRouter } from \"./mount.js\";\nexport { generateSkillMarkdown } from \"./skill.js\";\nexport { resolvePayTo, isZeroAddress } from \"./payto.js\";\nexport type {\n SchemaFieldType,\n InputSchema,\n OutputSchema,\n PaymentConfig,\n WaitUntilOptions,\n AgentServiceContext,\n AgentServiceConfig,\n DefinedAgentService,\n MountOptions,\n ForgePayToSource,\n SkillMeta,\n} from \"./types.js\";\n","import type {\n AgentServiceConfig,\n AgentServiceContext,\n DefinedAgentService,\n WaitUntilOptions,\n} from \"./types.js\";\n\nexport async function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 60_000;\n const started = Date.now();\n\n for (;;) {\n const value = await options.run();\n if (options.completedWhen(value)) {\n return value;\n }\n if (Date.now() - started >= timeoutMs) {\n throw new Error(`waitUntil timed out after ${timeoutMs}ms`);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function createContext(): AgentServiceContext {\n return {\n waitUntil,\n };\n}\n\n/** Convert snake_case or camelCase service name to kebab-case path segment. */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\nexport function defineAgentService<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TOutput extends Record<string, unknown> = Record<string, unknown>,\n>(\n config: AgentServiceConfig<TInput, TOutput>,\n): DefinedAgentService<TInput, TOutput> {\n if (!config.name?.trim()) {\n throw new Error(\"defineAgentService: name is required\");\n }\n if (!config.execute) {\n throw new Error(\"defineAgentService: execute is required\");\n }\n\n return {\n ...config,\n __forge: true,\n path: toKebabCase(config.name),\n };\n}\n","import type { Application, Request, Response, NextFunction, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { paymentMiddleware, x402ResourceServer } from \"@x402/express\";\nimport type { Network } from \"@x402/express\";\nimport { HTTPFacilitatorClient } from \"@x402/core/server\";\nimport type { RoutesConfig } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\nimport { createContext } from \"./define.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport type {\n DefinedAgentService,\n InputSchema,\n MountOptions,\n SchemaFieldType,\n} from \"./types.js\";\n\n/** Base mainnet — settled via the public XPay facilitator (no API keys). */\nconst DEFAULT_NETWORK = \"eip155:8453\" as const satisfies Network;\n/** Open facilitator: Base mainnet + Sepolia, no API keys. https://github.com/xpaysh/xpay-x402 */\nconst DEFAULT_FACILITATOR_URL = \"https://facilitator.xpay.sh\";\n\nfunction asNetwork(value: string | undefined): Network {\n return (value ?? DEFAULT_NETWORK) as Network;\n}\n\nfunction createFacilitatorClient(options: MountOptions): HTTPFacilitatorClient {\n return new HTTPFacilitatorClient({\n url: options.facilitatorUrl ?? DEFAULT_FACILITATOR_URL,\n createAuthHeaders: options.createAuthHeaders,\n });\n}\n\nfunction validateInput(\n body: unknown,\n schema: InputSchema,\n): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) {\n return { ok: false, error: \"Request body must be a JSON object\" };\n }\n\n const input = body as Record<string, unknown>;\n const value: Record<string, unknown> = {};\n\n for (const [key, type] of Object.entries(schema)) {\n if (!(key in input) || input[key] === undefined || input[key] === null) {\n return { ok: false, error: `Missing required field: ${key}` };\n }\n const err = checkType(key, input[key], type);\n if (err) return { ok: false, error: err };\n value[key] = input[key];\n }\n\n return { ok: true, value };\n}\n\nfunction checkType(key: string, value: unknown, type: SchemaFieldType): string | null {\n switch (type) {\n case \"string\":\n case \"url\":\n if (typeof value !== \"string\" || !value.trim()) {\n return `Field ${key} must be a non-empty string`;\n }\n if (type === \"url\") {\n try {\n new URL(value);\n } catch {\n return `Field ${key} must be a valid URL`;\n }\n }\n return null;\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return `Field ${key} must be a number`;\n }\n return null;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return `Field ${key} must be a boolean`;\n }\n return null;\n default:\n return null;\n }\n}\n\nfunction formatPrice(amount: string): string {\n const normalized = amount.startsWith(\"$\") ? amount.slice(1) : amount;\n return `$${normalized}`;\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * Each service is exposed as POST {basePath}/{kebab-name} with optional x402.\n *\n * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.\n * Merchants should not need to configure PAY_TO on Railway for the happy path.\n */\nexport async function mountAgentServices(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): Promise<Router> {\n let payTo = \"\";\n if (!options.skipPayment) {\n const fallback =\n options.payToFallback?.trim() || options.payTo?.trim() || \"\";\n payTo = await resolvePayTo({\n fallback,\n payTo: options.payTo,\n forge: options.forge,\n });\n if (isZeroAddress(payTo)) {\n throw new Error(\n \"mountAgentServices: resolved payTo is the zero address. Re-run Forge to provision a wallet.\",\n );\n }\n }\n\n const basePath = (options.basePath ?? \"/agent\").replace(/\\/$/, \"\") || \"/agent\";\n const router = createRouter();\n const context = createContext();\n\n const routes: RoutesConfig = {};\n\n for (const service of services) {\n const routePath = `/${service.path}`;\n const fullPath = `${basePath}${routePath}`;\n const network = asNetwork(service.payment.network);\n\n if (!options.skipPayment) {\n routes[`POST ${fullPath}`] = {\n accepts: {\n scheme: \"exact\",\n price: formatPrice(service.payment.amount),\n network,\n payTo,\n },\n description: service.description,\n mimeType: \"application/json\",\n };\n }\n\n router.post(routePath, async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n });\n }\n\n if (!options.skipPayment && Object.keys(routes).length > 0) {\n const facilitator = createFacilitatorClient(options);\n const networks = new Set(\n services.map((s) => asNetwork(s.payment.network)),\n );\n const resourceServer = new x402ResourceServer(facilitator);\n for (const network of networks) {\n resourceServer.register(network, new ExactEvmScheme());\n }\n app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, true));\n }\n\n app.use(basePath, router);\n return router;\n}\n\n/** Express-compatible middleware alias used in docs. */\nexport function createForgeRouter(\n services: DefinedAgentService[],\n options: MountOptions,\n): (app: Application) => Promise<Router> {\n return (app) => mountAgentServices(app, services, options);\n}\n\n/** Optional no-op next for typing convenience in tests. */\nexport function forgeErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction,\n): void {\n const message = err instanceof Error ? err.message : \"Internal error\";\n res.status(500).json({ error: message });\n}\n","import type { ForgePayToSource } from \"./types.js\";\n\nexport type { ForgePayToSource };\n\nconst ZERO = /^0x0{40}$/i;\n\nexport function isZeroAddress(address: string | undefined | null): boolean {\n if (!address) return true;\n return ZERO.test(address.trim());\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction pickForgeBaseUrl(source?: ForgePayToSource): string | null {\n const fromEnv = process.env.FORGE_URL?.trim();\n if (fromEnv) return normalizeBaseUrl(fromEnv);\n\n const fromOpts = source?.baseUrl?.trim();\n if (!fromOpts) return null;\n\n const normalized = normalizeBaseUrl(fromOpts);\n // Never call a laptop Forge from a deployed merchant app.\n if (\n /localhost|127\\.0\\.0\\.1/i.test(normalized) &&\n process.env.NODE_ENV === \"production\"\n ) {\n return null;\n }\n return normalized;\n}\n\n/**\n * Resolve merchant payTo with minimal host config:\n * 1. `PAY_TO` env (optional override)\n * 2. Live address from Forge public API (when reachable)\n * 3. Baked-in fallback from the Forge PR\n */\nexport async function resolvePayTo(opts: {\n /** Baked-in address from Forge codegen (required fallback). */\n fallback: string;\n /** Optional explicit override (usually process.env.PAY_TO). */\n payTo?: string;\n forge?: ForgePayToSource;\n /** Fetch timeout ms. Defaults to 2500. */\n timeoutMs?: number;\n}): Promise<string> {\n const envOverride = process.env.PAY_TO?.trim();\n if (envOverride && !isZeroAddress(envOverride)) {\n return envOverride;\n }\n\n const explicit = opts.payTo?.trim();\n if (explicit && !isZeroAddress(explicit) && !opts.forge) {\n return explicit;\n }\n\n const baseUrl = pickForgeBaseUrl(opts.forge);\n if (baseUrl && opts.forge) {\n const url = `${baseUrl}/api/public/pay-to/${encodeURIComponent(opts.forge.owner)}/${encodeURIComponent(opts.forge.repo)}`;\n try {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n opts.timeoutMs ?? 2500,\n );\n const res = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n clearTimeout(timer);\n if (res.ok) {\n const json = (await res.json()) as { address?: string };\n if (json.address && !isZeroAddress(json.address)) {\n return json.address.trim();\n }\n }\n } catch (err) {\n console.warn(\"[forge] live payTo fetch failed, using fallback:\", err);\n }\n }\n\n if (explicit && !isZeroAddress(explicit)) return explicit;\n if (opts.fallback && !isZeroAddress(opts.fallback)) return opts.fallback;\n\n throw new Error(\n \"resolvePayTo: no usable address (set Forge wallet / PAY_TO, or re-run Forge PR)\",\n );\n}\n","import type { DefinedAgentService, SkillMeta } from \"./types.js\";\n\n/**\n * Generate a SKILL.md document for agents from defined Forge services.\n */\nexport function generateSkillMarkdown(\n services: DefinedAgentService[],\n meta: SkillMeta,\n): string {\n const base = meta.baseUrl.replace(/\\/$/, \"\");\n const lines: string[] = [\n `---`,\n `name: ${meta.name}`,\n `description: ${meta.description}`,\n `---`,\n ``,\n `# ${meta.name}`,\n ``,\n meta.description,\n ``,\n ];\n\n if (meta.homepage) {\n lines.push(`Homepage: ${meta.homepage}`, ``);\n }\n\n lines.push(\n `## How to use`,\n ``,\n `1. Read this skill.`,\n `2. Call the agent endpoint with a JSON body matching the input schema.`,\n `3. If you receive HTTP 402, pay via x402 (USDC) and retry with the payment header.`,\n `4. Use the returned output fields.`,\n ``,\n `## Services`,\n ``,\n );\n\n for (const service of services) {\n const path = `${base}/agent/${service.path}`;\n const price = service.payment.amount.startsWith(\"$\")\n ? service.payment.amount\n : `$${service.payment.amount}`;\n\n lines.push(\n `### \\`${service.name}\\``,\n ``,\n service.description,\n ``,\n `- **Method:** \\`POST\\``,\n `- **URL:** \\`${path}\\``,\n `- **Price:** ${price} ${service.payment.currency} via ${service.payment.protocols.join(\", \")}`,\n ``,\n `**Input**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Output**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.output).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Example**`,\n ``,\n \"```bash\",\n `curl -X POST ${path} \\\\`,\n ` -H \"Content-Type: application/json\" \\\\`,\n ` -d '${JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [\n k,\n t === \"url\" ? \"https://example.com/image.png\" : `example-${k}`,\n ]),\n ),\n )}'`,\n \"```\",\n ``,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["import_express","import_server","createRouter"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/define.ts","../src/mount.ts","../src/gateway.ts","../src/gateway-auth.ts","../src/payto.ts","../src/skill.ts"],"sourcesContent":["export { defineAgentService, waitUntil, createContext, toKebabCase } from \"./define.js\";\nexport { mountAgentServices, createForgeRouter } from \"./mount.js\";\nexport { generateSkillMarkdown } from \"./skill.js\";\nexport { resolvePayTo, isZeroAddress } from \"./payto.js\";\nexport { heartbeatGateway } from \"./gateway.js\";\nexport {\n isForgeGatewayRequest,\n requireForgeGateway,\n getForgeGatewaySecret,\n forgeGatewayLoopbackHeaders,\n FORGE_GATEWAY_USER_ID,\n} from \"./gateway-auth.js\";\nexport type {\n SchemaFieldType,\n InputSchema,\n OutputSchema,\n PaymentConfig,\n WaitUntilOptions,\n AgentServiceContext,\n AgentServiceConfig,\n DefinedAgentService,\n MountOptions,\n ForgePayToSource,\n SkillMeta,\n} from \"./types.js\";\n","import type {\n AgentServiceConfig,\n AgentServiceContext,\n DefinedAgentService,\n WaitUntilOptions,\n} from \"./types.js\";\n\nexport async function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 60_000;\n const started = Date.now();\n\n for (;;) {\n const value = await options.run();\n if (options.completedWhen(value)) {\n return value;\n }\n if (Date.now() - started >= timeoutMs) {\n throw new Error(`waitUntil timed out after ${timeoutMs}ms`);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function createContext(): AgentServiceContext {\n return {\n waitUntil,\n };\n}\n\n/** Convert snake_case or camelCase service name to kebab-case path segment. */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\nexport function defineAgentService<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TOutput extends Record<string, unknown> = Record<string, unknown>,\n>(\n config: AgentServiceConfig<TInput, TOutput>,\n): DefinedAgentService<TInput, TOutput> {\n if (!config.name?.trim()) {\n throw new Error(\"defineAgentService: name is required\");\n }\n if (!config.execute) {\n throw new Error(\"defineAgentService: execute is required\");\n }\n\n return {\n ...config,\n __forge: true,\n path: toKebabCase(config.name),\n };\n}\n","import type { Application, Request, Response, NextFunction, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { paymentMiddleware, x402ResourceServer } from \"@x402/express\";\nimport type { Network } from \"@x402/express\";\nimport { HTTPFacilitatorClient } from \"@x402/core/server\";\nimport type { RoutesConfig } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\nimport { createContext } from \"./define.js\";\nimport { heartbeatGateway } from \"./gateway.js\";\nimport {\n getForgeGatewaySecret,\n requireForgeGateway,\n} from \"./gateway-auth.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport type {\n DefinedAgentService,\n InputSchema,\n MountOptions,\n SchemaFieldType,\n} from \"./types.js\";\n\n/** Base mainnet — settled via the public XPay facilitator (no API keys). */\nconst DEFAULT_NETWORK = \"eip155:8453\" as const satisfies Network;\n/** Open facilitator: Base mainnet + Sepolia, no API keys. https://github.com/xpaysh/xpay-x402 */\nconst DEFAULT_FACILITATOR_URL = \"https://facilitator.xpay.sh\";\n\nfunction asNetwork(value: string | undefined): Network {\n return (value ?? DEFAULT_NETWORK) as Network;\n}\n\nfunction createFacilitatorClient(options: MountOptions): HTTPFacilitatorClient {\n return new HTTPFacilitatorClient({\n url: options.facilitatorUrl ?? DEFAULT_FACILITATOR_URL,\n createAuthHeaders: options.createAuthHeaders,\n });\n}\n\nfunction validateInput(\n body: unknown,\n schema: InputSchema,\n): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) {\n return { ok: false, error: \"Request body must be a JSON object\" };\n }\n\n const input = body as Record<string, unknown>;\n const value: Record<string, unknown> = {};\n\n for (const [key, type] of Object.entries(schema)) {\n if (!(key in input) || input[key] === undefined || input[key] === null) {\n return { ok: false, error: `Missing required field: ${key}` };\n }\n const err = checkType(key, input[key], type);\n if (err) return { ok: false, error: err };\n value[key] = input[key];\n }\n\n return { ok: true, value };\n}\n\nfunction checkType(key: string, value: unknown, type: SchemaFieldType): string | null {\n switch (type) {\n case \"string\":\n case \"url\":\n if (typeof value !== \"string\" || !value.trim()) {\n return `Field ${key} must be a non-empty string`;\n }\n if (type === \"url\") {\n try {\n new URL(value);\n } catch {\n return `Field ${key} must be a valid URL`;\n }\n }\n return null;\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return `Field ${key} must be a number`;\n }\n return null;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return `Field ${key} must be a boolean`;\n }\n return null;\n default:\n return null;\n }\n}\n\nfunction formatPrice(amount: string): string {\n const normalized = amount.startsWith(\"$\") ? amount.slice(1) : amount;\n return `$${normalized}`;\n}\n\nfunction mountFulfillmentRouter(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): void {\n const secret = getForgeGatewaySecret(options.gateway?.registerSecret);\n if (!secret) {\n console.warn(\n \"[forge] /forge/fulfill not mounted — set GATEWAY_REGISTER_SECRET (min 16 chars) so the gateway can fulfill after payment\",\n );\n return;\n }\n\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const router = createRouter();\n const context = createContext();\n const gate = requireForgeGateway(secret);\n\n for (const service of services) {\n router.post(\n `/${service.path}`,\n gate,\n async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] fulfill ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n },\n );\n }\n\n app.use(fulfillPath, router);\n console.log(\n `[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`,\n );\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * - POST {basePath}/{kebab-name} — optional public x402 agent surface\n * - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment\n *\n * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.\n */\nexport async function mountAgentServices(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): Promise<Router> {\n let payTo = \"\";\n if (!options.skipPayment) {\n const fallback =\n options.payToFallback?.trim() || options.payTo?.trim() || \"\";\n payTo = await resolvePayTo({\n fallback,\n payTo: options.payTo,\n forge: options.forge,\n });\n if (isZeroAddress(payTo)) {\n throw new Error(\n \"mountAgentServices: resolved payTo is the zero address. Re-run Forge to provision a wallet.\",\n );\n }\n }\n\n const basePath = (options.basePath ?? \"/agent\").replace(/\\/$/, \"\") || \"/agent\";\n const router = createRouter();\n const context = createContext();\n\n const routes: RoutesConfig = {};\n\n for (const service of services) {\n const routePath = `/${service.path}`;\n const fullPath = `${basePath}${routePath}`;\n const network = asNetwork(service.payment.network);\n\n if (!options.skipPayment) {\n routes[`POST ${fullPath}`] = {\n accepts: {\n scheme: \"exact\",\n price: formatPrice(service.payment.amount),\n network,\n payTo,\n },\n description: service.description,\n mimeType: \"application/json\",\n };\n }\n\n router.post(routePath, async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n });\n }\n\n if (!options.skipPayment && Object.keys(routes).length > 0) {\n const facilitator = createFacilitatorClient(options);\n const networks = new Set(\n services.map((s) => asNetwork(s.payment.network)),\n );\n const resourceServer = new x402ResourceServer(facilitator);\n for (const network of networks) {\n resourceServer.register(network, new ExactEvmScheme());\n }\n app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, true));\n }\n\n app.use(basePath, router);\n mountFulfillmentRouter(app, services, options);\n\n const gatewayUrl =\n options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();\n const handle =\n options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();\n const registerSecret =\n options.gateway?.registerSecret?.trim() ||\n process.env.GATEWAY_REGISTER_SECRET?.trim();\n const publicBaseUrl =\n options.gateway?.publicBaseUrl?.trim() ||\n process.env.PUBLIC_BASE_URL?.trim() ||\n (process.env.RAILWAY_PUBLIC_DOMAIN\n ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}`\n : \"\");\n\n if (gatewayUrl && handle && registerSecret && publicBaseUrl) {\n void heartbeatGateway({\n gatewayUrl,\n handle,\n baseUrl: publicBaseUrl,\n registerSecret,\n }).catch((err) => {\n console.warn(\"[forge] gateway heartbeat skipped:\", err);\n });\n }\n\n return router;\n}\n\nexport function createForgeRouter(\n services: DefinedAgentService[],\n options: MountOptions,\n): (app: Application) => Promise<Router> {\n return (app) => mountAgentServices(app, services, options);\n}\n\nexport function forgeErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction,\n): void {\n const message = err instanceof Error ? err.message : \"Internal error\";\n res.status(500).json({ error: message });\n}\n","/**\n * Notify ClawCash gateway of this merchant's live public base URL.\n * Full action registration still happens from Forge \"Go live\".\n */\nexport async function heartbeatGateway(opts: {\n gatewayUrl: string;\n handle: string;\n baseUrl: string;\n registerSecret: string;\n}): Promise<void> {\n const gateway = opts.gatewayUrl.replace(/\\/$/, \"\");\n const res = await fetch(`${gateway}/register/heartbeat`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${opts.registerSecret}`,\n },\n body: JSON.stringify({\n handle: opts.handle.trim().toLowerCase(),\n baseUrl: opts.baseUrl.replace(/\\/$/, \"\"),\n }),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(\n `gateway heartbeat failed (${res.status}): ${text.slice(0, 240)}`,\n );\n }\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Shared secret auth for ClawCash gateway → merchant fulfillment.\n * Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).\n */\nexport function getForgeGatewaySecret(\n explicit?: string | null,\n): string | null {\n const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();\n if (!value || value.length < 16) return null;\n return value;\n}\n\nexport function isForgeGatewayRequest(\n req: Request,\n secret?: string | null,\n): boolean {\n const expected = getForgeGatewaySecret(secret);\n if (!expected) return false;\n const header = req.header(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt = req.header(\"x-clawcash-gateway\")?.trim() ?? \"\";\n return bearer === expected || alt === expected;\n}\n\n/** Synthetic user id for DB rows created by gateway-driven fulfillment. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\n\nexport function requireForgeGateway(\n secret?: string | null,\n): (req: Request, res: Response, next: NextFunction) => void {\n return (req, res, next) => {\n if (!isForgeGatewayRequest(req, secret)) {\n res.status(401).json({\n error:\n \"Unauthorized — ClawCash gateway secret required (GATEWAY_REGISTER_SECRET)\",\n });\n return;\n }\n next();\n };\n}\n\n/** Headers merchant execute() should send on loopback calls during fulfillment. */\nexport function forgeGatewayLoopbackHeaders(\n secret?: string | null,\n): Record<string, string> {\n const expected = getForgeGatewaySecret(secret);\n if (!expected) return {};\n return {\n Authorization: `Bearer ${expected}`,\n \"X-ClawCash-Gateway\": expected,\n };\n}\n","import type { ForgePayToSource } from \"./types.js\";\n\nexport type { ForgePayToSource };\n\nconst ZERO = /^0x0{40}$/i;\n\nexport function isZeroAddress(address: string | undefined | null): boolean {\n if (!address) return true;\n return ZERO.test(address.trim());\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction pickForgeBaseUrl(source?: ForgePayToSource): string | null {\n const fromEnv = process.env.FORGE_URL?.trim();\n if (fromEnv) return normalizeBaseUrl(fromEnv);\n\n const fromOpts = source?.baseUrl?.trim();\n if (!fromOpts) return null;\n\n const normalized = normalizeBaseUrl(fromOpts);\n // Never call a laptop Forge from a deployed merchant app.\n if (\n /localhost|127\\.0\\.0\\.1/i.test(normalized) &&\n process.env.NODE_ENV === \"production\"\n ) {\n return null;\n }\n return normalized;\n}\n\n/**\n * Resolve merchant payTo with minimal host config:\n * 1. `PAY_TO` env (optional override)\n * 2. Live address from Forge public API (when reachable)\n * 3. Baked-in fallback from the Forge PR\n */\nexport async function resolvePayTo(opts: {\n /** Baked-in address from Forge codegen (required fallback). */\n fallback: string;\n /** Optional explicit override (usually process.env.PAY_TO). */\n payTo?: string;\n forge?: ForgePayToSource;\n /** Fetch timeout ms. Defaults to 2500. */\n timeoutMs?: number;\n}): Promise<string> {\n const envOverride = process.env.PAY_TO?.trim();\n if (envOverride && !isZeroAddress(envOverride)) {\n return envOverride;\n }\n\n const explicit = opts.payTo?.trim();\n if (explicit && !isZeroAddress(explicit) && !opts.forge) {\n return explicit;\n }\n\n const baseUrl = pickForgeBaseUrl(opts.forge);\n if (baseUrl && opts.forge) {\n const url = `${baseUrl}/api/public/pay-to/${encodeURIComponent(opts.forge.owner)}/${encodeURIComponent(opts.forge.repo)}`;\n try {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n opts.timeoutMs ?? 2500,\n );\n const res = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n clearTimeout(timer);\n if (res.ok) {\n const json = (await res.json()) as { address?: string };\n if (json.address && !isZeroAddress(json.address)) {\n return json.address.trim();\n }\n }\n } catch (err) {\n console.warn(\"[forge] live payTo fetch failed, using fallback:\", err);\n }\n }\n\n if (explicit && !isZeroAddress(explicit)) return explicit;\n if (opts.fallback && !isZeroAddress(opts.fallback)) return opts.fallback;\n\n throw new Error(\n \"resolvePayTo: no usable address (set Forge wallet / PAY_TO, or re-run Forge PR)\",\n );\n}\n","import type { DefinedAgentService, SkillMeta } from \"./types.js\";\n\n/**\n * Generate a SKILL.md document for agents from defined Forge services.\n */\nexport function generateSkillMarkdown(\n services: DefinedAgentService[],\n meta: SkillMeta,\n): string {\n const base = meta.baseUrl.replace(/\\/$/, \"\");\n const lines: string[] = [\n `---`,\n `name: ${meta.name}`,\n `description: ${meta.description}`,\n `---`,\n ``,\n `# ${meta.name}`,\n ``,\n meta.description,\n ``,\n ];\n\n if (meta.homepage) {\n lines.push(`Homepage: ${meta.homepage}`, ``);\n }\n\n lines.push(\n `## How to use`,\n ``,\n `1. Read this skill.`,\n `2. Call the agent endpoint with a JSON body matching the input schema.`,\n `3. If you receive HTTP 402, pay via x402 (USDC) and retry with the payment header.`,\n `4. Use the returned output fields.`,\n ``,\n `## Services`,\n ``,\n );\n\n for (const service of services) {\n const path = `${base}/agent/${service.path}`;\n const price = service.payment.amount.startsWith(\"$\")\n ? service.payment.amount\n : `$${service.payment.amount}`;\n\n lines.push(\n `### \\`${service.name}\\``,\n ``,\n service.description,\n ``,\n `- **Method:** \\`POST\\``,\n `- **URL:** \\`${path}\\``,\n `- **Price:** ${price} ${service.payment.currency} via ${service.payment.protocols.join(\", \")}`,\n ``,\n `**Input**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Output**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.output).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Example**`,\n ``,\n \"```bash\",\n `curl -X POST ${path} \\\\`,\n ` -H \"Content-Type: application/json\" \\\\`,\n ` -d '${JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [\n k,\n t === \"url\" ? \"https://example.com/image.png\" : `example-${k}`,\n ]),\n ),\n )}'`,\n \"```\",\n ``,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["import_express","import_server","createRouter"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Application, Router } from 'express';
1
+ import { Application, Router, Request, Response, NextFunction } from 'express';
2
2
 
3
3
  type SchemaFieldType = "string" | "url" | "number" | "boolean";
4
4
  type InputSchema = Record<string, SchemaFieldType>;
@@ -71,6 +71,22 @@ interface MountOptions {
71
71
  basePath?: string;
72
72
  /** Skip x402 (local dev only). Defaults to false. */
73
73
  skipPayment?: boolean;
74
+ /**
75
+ * Optional gateway heartbeat after mount.
76
+ * Prefer env: GATEWAY_URL, FORGE_HANDLE, GATEWAY_REGISTER_SECRET, PUBLIC_BASE_URL.
77
+ */
78
+ gateway?: {
79
+ url?: string;
80
+ handle?: string;
81
+ registerSecret?: string;
82
+ /** Merchant public base URL for fulfillment. */
83
+ publicBaseUrl?: string;
84
+ };
85
+ /**
86
+ * Path prefix for private fulfillment (gateway → merchant after x402).
87
+ * Defaults to /forge/fulfill. Not end-user / x402 routes.
88
+ */
89
+ fulfillPath?: string;
74
90
  }
75
91
  interface SkillMeta {
76
92
  name: string;
@@ -87,13 +103,12 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
87
103
 
88
104
  /**
89
105
  * Mount Forge agent services on an Express app.
90
- * Each service is exposed as POST {basePath}/{kebab-name} with optional x402.
106
+ * - POST {basePath}/{kebab-name} optional public x402 agent surface
107
+ * - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
91
108
  *
92
109
  * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.
93
- * Merchants should not need to configure PAY_TO on Railway for the happy path.
94
110
  */
95
111
  declare function mountAgentServices(app: Application, services: DefinedAgentService[], options: MountOptions): Promise<Router>;
96
- /** Express-compatible middleware alias used in docs. */
97
112
  declare function createForgeRouter(services: DefinedAgentService[], options: MountOptions): (app: Application) => Promise<Router>;
98
113
 
99
114
  /**
@@ -118,4 +133,27 @@ declare function resolvePayTo(opts: {
118
133
  timeoutMs?: number;
119
134
  }): Promise<string>;
120
135
 
121
- export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, type ForgePayToSource, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, createContext, createForgeRouter, defineAgentService, generateSkillMarkdown, isZeroAddress, mountAgentServices, resolvePayTo, toKebabCase, waitUntil };
136
+ /**
137
+ * Notify ClawCash gateway of this merchant's live public base URL.
138
+ * Full action registration still happens from Forge "Go live".
139
+ */
140
+ declare function heartbeatGateway(opts: {
141
+ gatewayUrl: string;
142
+ handle: string;
143
+ baseUrl: string;
144
+ registerSecret: string;
145
+ }): Promise<void>;
146
+
147
+ /**
148
+ * Shared secret auth for ClawCash gateway → merchant fulfillment.
149
+ * Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).
150
+ */
151
+ declare function getForgeGatewaySecret(explicit?: string | null): string | null;
152
+ declare function isForgeGatewayRequest(req: Request, secret?: string | null): boolean;
153
+ /** Synthetic user id for DB rows created by gateway-driven fulfillment. */
154
+ declare const FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
155
+ declare function requireForgeGateway(secret?: string | null): (req: Request, res: Response, next: NextFunction) => void;
156
+ /** Headers merchant execute() should send on loopback calls during fulfillment. */
157
+ declare function forgeGatewayLoopbackHeaders(secret?: string | null): Record<string, string>;
158
+
159
+ export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type ForgePayToSource, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, createContext, createForgeRouter, defineAgentService, forgeGatewayLoopbackHeaders, generateSkillMarkdown, getForgeGatewaySecret, heartbeatGateway, isForgeGatewayRequest, isZeroAddress, mountAgentServices, requireForgeGateway, resolvePayTo, toKebabCase, waitUntil };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Application, Router } from 'express';
1
+ import { Application, Router, Request, Response, NextFunction } from 'express';
2
2
 
3
3
  type SchemaFieldType = "string" | "url" | "number" | "boolean";
4
4
  type InputSchema = Record<string, SchemaFieldType>;
@@ -71,6 +71,22 @@ interface MountOptions {
71
71
  basePath?: string;
72
72
  /** Skip x402 (local dev only). Defaults to false. */
73
73
  skipPayment?: boolean;
74
+ /**
75
+ * Optional gateway heartbeat after mount.
76
+ * Prefer env: GATEWAY_URL, FORGE_HANDLE, GATEWAY_REGISTER_SECRET, PUBLIC_BASE_URL.
77
+ */
78
+ gateway?: {
79
+ url?: string;
80
+ handle?: string;
81
+ registerSecret?: string;
82
+ /** Merchant public base URL for fulfillment. */
83
+ publicBaseUrl?: string;
84
+ };
85
+ /**
86
+ * Path prefix for private fulfillment (gateway → merchant after x402).
87
+ * Defaults to /forge/fulfill. Not end-user / x402 routes.
88
+ */
89
+ fulfillPath?: string;
74
90
  }
75
91
  interface SkillMeta {
76
92
  name: string;
@@ -87,13 +103,12 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
87
103
 
88
104
  /**
89
105
  * Mount Forge agent services on an Express app.
90
- * Each service is exposed as POST {basePath}/{kebab-name} with optional x402.
106
+ * - POST {basePath}/{kebab-name} optional public x402 agent surface
107
+ * - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
91
108
  *
92
109
  * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.
93
- * Merchants should not need to configure PAY_TO on Railway for the happy path.
94
110
  */
95
111
  declare function mountAgentServices(app: Application, services: DefinedAgentService[], options: MountOptions): Promise<Router>;
96
- /** Express-compatible middleware alias used in docs. */
97
112
  declare function createForgeRouter(services: DefinedAgentService[], options: MountOptions): (app: Application) => Promise<Router>;
98
113
 
99
114
  /**
@@ -118,4 +133,27 @@ declare function resolvePayTo(opts: {
118
133
  timeoutMs?: number;
119
134
  }): Promise<string>;
120
135
 
121
- export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, type ForgePayToSource, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, createContext, createForgeRouter, defineAgentService, generateSkillMarkdown, isZeroAddress, mountAgentServices, resolvePayTo, toKebabCase, waitUntil };
136
+ /**
137
+ * Notify ClawCash gateway of this merchant's live public base URL.
138
+ * Full action registration still happens from Forge "Go live".
139
+ */
140
+ declare function heartbeatGateway(opts: {
141
+ gatewayUrl: string;
142
+ handle: string;
143
+ baseUrl: string;
144
+ registerSecret: string;
145
+ }): Promise<void>;
146
+
147
+ /**
148
+ * Shared secret auth for ClawCash gateway → merchant fulfillment.
149
+ * Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).
150
+ */
151
+ declare function getForgeGatewaySecret(explicit?: string | null): string | null;
152
+ declare function isForgeGatewayRequest(req: Request, secret?: string | null): boolean;
153
+ /** Synthetic user id for DB rows created by gateway-driven fulfillment. */
154
+ declare const FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
155
+ declare function requireForgeGateway(secret?: string | null): (req: Request, res: Response, next: NextFunction) => void;
156
+ /** Headers merchant execute() should send on loopback calls during fulfillment. */
157
+ declare function forgeGatewayLoopbackHeaders(secret?: string | null): Record<string, string>;
158
+
159
+ export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type ForgePayToSource, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, createContext, createForgeRouter, defineAgentService, forgeGatewayLoopbackHeaders, generateSkillMarkdown, getForgeGatewaySecret, heartbeatGateway, isForgeGatewayRequest, isZeroAddress, mountAgentServices, requireForgeGateway, resolvePayTo, toKebabCase, waitUntil };
package/dist/index.js CHANGED
@@ -42,6 +42,63 @@ import { paymentMiddleware, x402ResourceServer } from "@x402/express";
42
42
  import { HTTPFacilitatorClient } from "@x402/core/server";
43
43
  import { ExactEvmScheme } from "@x402/evm/exact/server";
44
44
 
45
+ // src/gateway.ts
46
+ async function heartbeatGateway(opts) {
47
+ const gateway = opts.gatewayUrl.replace(/\/$/, "");
48
+ const res = await fetch(`${gateway}/register/heartbeat`, {
49
+ method: "POST",
50
+ headers: {
51
+ "content-type": "application/json",
52
+ authorization: `Bearer ${opts.registerSecret}`
53
+ },
54
+ body: JSON.stringify({
55
+ handle: opts.handle.trim().toLowerCase(),
56
+ baseUrl: opts.baseUrl.replace(/\/$/, "")
57
+ })
58
+ });
59
+ if (!res.ok) {
60
+ const text = await res.text();
61
+ throw new Error(
62
+ `gateway heartbeat failed (${res.status}): ${text.slice(0, 240)}`
63
+ );
64
+ }
65
+ }
66
+
67
+ // src/gateway-auth.ts
68
+ function getForgeGatewaySecret(explicit) {
69
+ const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
70
+ if (!value || value.length < 16) return null;
71
+ return value;
72
+ }
73
+ function isForgeGatewayRequest(req, secret) {
74
+ const expected = getForgeGatewaySecret(secret);
75
+ if (!expected) return false;
76
+ const header = req.header("authorization") ?? "";
77
+ const bearer = header.toLowerCase().startsWith("bearer ") ? header.slice(7).trim() : "";
78
+ const alt = req.header("x-clawcash-gateway")?.trim() ?? "";
79
+ return bearer === expected || alt === expected;
80
+ }
81
+ var FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
82
+ function requireForgeGateway(secret) {
83
+ return (req, res, next) => {
84
+ if (!isForgeGatewayRequest(req, secret)) {
85
+ res.status(401).json({
86
+ error: "Unauthorized \u2014 ClawCash gateway secret required (GATEWAY_REGISTER_SECRET)"
87
+ });
88
+ return;
89
+ }
90
+ next();
91
+ };
92
+ }
93
+ function forgeGatewayLoopbackHeaders(secret) {
94
+ const expected = getForgeGatewaySecret(secret);
95
+ if (!expected) return {};
96
+ return {
97
+ Authorization: `Bearer ${expected}`,
98
+ "X-ClawCash-Gateway": expected
99
+ };
100
+ }
101
+
45
102
  // src/payto.ts
46
103
  var ZERO = /^0x0{40}$/i;
47
104
  function isZeroAddress(address) {
@@ -163,6 +220,44 @@ function formatPrice(amount) {
163
220
  const normalized = amount.startsWith("$") ? amount.slice(1) : amount;
164
221
  return `$${normalized}`;
165
222
  }
223
+ function mountFulfillmentRouter(app, services, options) {
224
+ const secret = getForgeGatewaySecret(options.gateway?.registerSecret);
225
+ if (!secret) {
226
+ console.warn(
227
+ "[forge] /forge/fulfill not mounted \u2014 set GATEWAY_REGISTER_SECRET (min 16 chars) so the gateway can fulfill after payment"
228
+ );
229
+ return;
230
+ }
231
+ const fulfillPath = (options.fulfillPath ?? "/forge/fulfill").replace(/\/$/, "") || "/forge/fulfill";
232
+ const router = createRouter();
233
+ const context = createContext();
234
+ const gate = requireForgeGateway(secret);
235
+ for (const service of services) {
236
+ router.post(
237
+ `/${service.path}`,
238
+ gate,
239
+ async (req, res) => {
240
+ try {
241
+ const validated = validateInput(req.body, service.input);
242
+ if (!validated.ok) {
243
+ res.status(400).json({ error: validated.error });
244
+ return;
245
+ }
246
+ const output = await service.execute(validated.value, context);
247
+ res.json(output);
248
+ } catch (err) {
249
+ const message = err instanceof Error ? err.message : "Internal error";
250
+ console.error(`[forge] fulfill ${service.name} failed:`, err);
251
+ res.status(500).json({ error: message });
252
+ }
253
+ }
254
+ );
255
+ }
256
+ app.use(fulfillPath, router);
257
+ console.log(
258
+ `[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`
259
+ );
260
+ }
166
261
  async function mountAgentServices(app, services, options) {
167
262
  let payTo = "";
168
263
  if (!options.skipPayment) {
@@ -226,6 +321,21 @@ async function mountAgentServices(app, services, options) {
226
321
  app.use(paymentMiddleware(routes, resourceServer, void 0, void 0, true));
227
322
  }
228
323
  app.use(basePath, router);
324
+ mountFulfillmentRouter(app, services, options);
325
+ const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();
326
+ const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();
327
+ const registerSecret = options.gateway?.registerSecret?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
328
+ const publicBaseUrl = options.gateway?.publicBaseUrl?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.RAILWAY_PUBLIC_DOMAIN ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}` : "");
329
+ if (gatewayUrl && handle && registerSecret && publicBaseUrl) {
330
+ void heartbeatGateway({
331
+ gatewayUrl,
332
+ handle,
333
+ baseUrl: publicBaseUrl,
334
+ registerSecret
335
+ }).catch((err) => {
336
+ console.warn("[forge] gateway heartbeat skipped:", err);
337
+ });
338
+ }
229
339
  return router;
230
340
  }
231
341
  function createForgeRouter(services, options) {
@@ -316,12 +426,18 @@ function generateSkillMarkdown(services, meta) {
316
426
  return lines.join("\n");
317
427
  }
318
428
  export {
429
+ FORGE_GATEWAY_USER_ID,
319
430
  createContext,
320
431
  createForgeRouter,
321
432
  defineAgentService,
433
+ forgeGatewayLoopbackHeaders,
322
434
  generateSkillMarkdown,
435
+ getForgeGatewaySecret,
436
+ heartbeatGateway,
437
+ isForgeGatewayRequest,
323
438
  isZeroAddress,
324
439
  mountAgentServices,
440
+ requireForgeGateway,
325
441
  resolvePayTo,
326
442
  toKebabCase,
327
443
  waitUntil
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define.ts","../src/mount.ts","../src/payto.ts","../src/skill.ts"],"sourcesContent":["import type {\n AgentServiceConfig,\n AgentServiceContext,\n DefinedAgentService,\n WaitUntilOptions,\n} from \"./types.js\";\n\nexport async function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 60_000;\n const started = Date.now();\n\n for (;;) {\n const value = await options.run();\n if (options.completedWhen(value)) {\n return value;\n }\n if (Date.now() - started >= timeoutMs) {\n throw new Error(`waitUntil timed out after ${timeoutMs}ms`);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function createContext(): AgentServiceContext {\n return {\n waitUntil,\n };\n}\n\n/** Convert snake_case or camelCase service name to kebab-case path segment. */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\nexport function defineAgentService<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TOutput extends Record<string, unknown> = Record<string, unknown>,\n>(\n config: AgentServiceConfig<TInput, TOutput>,\n): DefinedAgentService<TInput, TOutput> {\n if (!config.name?.trim()) {\n throw new Error(\"defineAgentService: name is required\");\n }\n if (!config.execute) {\n throw new Error(\"defineAgentService: execute is required\");\n }\n\n return {\n ...config,\n __forge: true,\n path: toKebabCase(config.name),\n };\n}\n","import type { Application, Request, Response, NextFunction, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { paymentMiddleware, x402ResourceServer } from \"@x402/express\";\nimport type { Network } from \"@x402/express\";\nimport { HTTPFacilitatorClient } from \"@x402/core/server\";\nimport type { RoutesConfig } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\nimport { createContext } from \"./define.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport type {\n DefinedAgentService,\n InputSchema,\n MountOptions,\n SchemaFieldType,\n} from \"./types.js\";\n\n/** Base mainnet — settled via the public XPay facilitator (no API keys). */\nconst DEFAULT_NETWORK = \"eip155:8453\" as const satisfies Network;\n/** Open facilitator: Base mainnet + Sepolia, no API keys. https://github.com/xpaysh/xpay-x402 */\nconst DEFAULT_FACILITATOR_URL = \"https://facilitator.xpay.sh\";\n\nfunction asNetwork(value: string | undefined): Network {\n return (value ?? DEFAULT_NETWORK) as Network;\n}\n\nfunction createFacilitatorClient(options: MountOptions): HTTPFacilitatorClient {\n return new HTTPFacilitatorClient({\n url: options.facilitatorUrl ?? DEFAULT_FACILITATOR_URL,\n createAuthHeaders: options.createAuthHeaders,\n });\n}\n\nfunction validateInput(\n body: unknown,\n schema: InputSchema,\n): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) {\n return { ok: false, error: \"Request body must be a JSON object\" };\n }\n\n const input = body as Record<string, unknown>;\n const value: Record<string, unknown> = {};\n\n for (const [key, type] of Object.entries(schema)) {\n if (!(key in input) || input[key] === undefined || input[key] === null) {\n return { ok: false, error: `Missing required field: ${key}` };\n }\n const err = checkType(key, input[key], type);\n if (err) return { ok: false, error: err };\n value[key] = input[key];\n }\n\n return { ok: true, value };\n}\n\nfunction checkType(key: string, value: unknown, type: SchemaFieldType): string | null {\n switch (type) {\n case \"string\":\n case \"url\":\n if (typeof value !== \"string\" || !value.trim()) {\n return `Field ${key} must be a non-empty string`;\n }\n if (type === \"url\") {\n try {\n new URL(value);\n } catch {\n return `Field ${key} must be a valid URL`;\n }\n }\n return null;\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return `Field ${key} must be a number`;\n }\n return null;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return `Field ${key} must be a boolean`;\n }\n return null;\n default:\n return null;\n }\n}\n\nfunction formatPrice(amount: string): string {\n const normalized = amount.startsWith(\"$\") ? amount.slice(1) : amount;\n return `$${normalized}`;\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * Each service is exposed as POST {basePath}/{kebab-name} with optional x402.\n *\n * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.\n * Merchants should not need to configure PAY_TO on Railway for the happy path.\n */\nexport async function mountAgentServices(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): Promise<Router> {\n let payTo = \"\";\n if (!options.skipPayment) {\n const fallback =\n options.payToFallback?.trim() || options.payTo?.trim() || \"\";\n payTo = await resolvePayTo({\n fallback,\n payTo: options.payTo,\n forge: options.forge,\n });\n if (isZeroAddress(payTo)) {\n throw new Error(\n \"mountAgentServices: resolved payTo is the zero address. Re-run Forge to provision a wallet.\",\n );\n }\n }\n\n const basePath = (options.basePath ?? \"/agent\").replace(/\\/$/, \"\") || \"/agent\";\n const router = createRouter();\n const context = createContext();\n\n const routes: RoutesConfig = {};\n\n for (const service of services) {\n const routePath = `/${service.path}`;\n const fullPath = `${basePath}${routePath}`;\n const network = asNetwork(service.payment.network);\n\n if (!options.skipPayment) {\n routes[`POST ${fullPath}`] = {\n accepts: {\n scheme: \"exact\",\n price: formatPrice(service.payment.amount),\n network,\n payTo,\n },\n description: service.description,\n mimeType: \"application/json\",\n };\n }\n\n router.post(routePath, async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n });\n }\n\n if (!options.skipPayment && Object.keys(routes).length > 0) {\n const facilitator = createFacilitatorClient(options);\n const networks = new Set(\n services.map((s) => asNetwork(s.payment.network)),\n );\n const resourceServer = new x402ResourceServer(facilitator);\n for (const network of networks) {\n resourceServer.register(network, new ExactEvmScheme());\n }\n app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, true));\n }\n\n app.use(basePath, router);\n return router;\n}\n\n/** Express-compatible middleware alias used in docs. */\nexport function createForgeRouter(\n services: DefinedAgentService[],\n options: MountOptions,\n): (app: Application) => Promise<Router> {\n return (app) => mountAgentServices(app, services, options);\n}\n\n/** Optional no-op next for typing convenience in tests. */\nexport function forgeErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction,\n): void {\n const message = err instanceof Error ? err.message : \"Internal error\";\n res.status(500).json({ error: message });\n}\n","import type { ForgePayToSource } from \"./types.js\";\n\nexport type { ForgePayToSource };\n\nconst ZERO = /^0x0{40}$/i;\n\nexport function isZeroAddress(address: string | undefined | null): boolean {\n if (!address) return true;\n return ZERO.test(address.trim());\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction pickForgeBaseUrl(source?: ForgePayToSource): string | null {\n const fromEnv = process.env.FORGE_URL?.trim();\n if (fromEnv) return normalizeBaseUrl(fromEnv);\n\n const fromOpts = source?.baseUrl?.trim();\n if (!fromOpts) return null;\n\n const normalized = normalizeBaseUrl(fromOpts);\n // Never call a laptop Forge from a deployed merchant app.\n if (\n /localhost|127\\.0\\.0\\.1/i.test(normalized) &&\n process.env.NODE_ENV === \"production\"\n ) {\n return null;\n }\n return normalized;\n}\n\n/**\n * Resolve merchant payTo with minimal host config:\n * 1. `PAY_TO` env (optional override)\n * 2. Live address from Forge public API (when reachable)\n * 3. Baked-in fallback from the Forge PR\n */\nexport async function resolvePayTo(opts: {\n /** Baked-in address from Forge codegen (required fallback). */\n fallback: string;\n /** Optional explicit override (usually process.env.PAY_TO). */\n payTo?: string;\n forge?: ForgePayToSource;\n /** Fetch timeout ms. Defaults to 2500. */\n timeoutMs?: number;\n}): Promise<string> {\n const envOverride = process.env.PAY_TO?.trim();\n if (envOverride && !isZeroAddress(envOverride)) {\n return envOverride;\n }\n\n const explicit = opts.payTo?.trim();\n if (explicit && !isZeroAddress(explicit) && !opts.forge) {\n return explicit;\n }\n\n const baseUrl = pickForgeBaseUrl(opts.forge);\n if (baseUrl && opts.forge) {\n const url = `${baseUrl}/api/public/pay-to/${encodeURIComponent(opts.forge.owner)}/${encodeURIComponent(opts.forge.repo)}`;\n try {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n opts.timeoutMs ?? 2500,\n );\n const res = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n clearTimeout(timer);\n if (res.ok) {\n const json = (await res.json()) as { address?: string };\n if (json.address && !isZeroAddress(json.address)) {\n return json.address.trim();\n }\n }\n } catch (err) {\n console.warn(\"[forge] live payTo fetch failed, using fallback:\", err);\n }\n }\n\n if (explicit && !isZeroAddress(explicit)) return explicit;\n if (opts.fallback && !isZeroAddress(opts.fallback)) return opts.fallback;\n\n throw new Error(\n \"resolvePayTo: no usable address (set Forge wallet / PAY_TO, or re-run Forge PR)\",\n );\n}\n","import type { DefinedAgentService, SkillMeta } from \"./types.js\";\n\n/**\n * Generate a SKILL.md document for agents from defined Forge services.\n */\nexport function generateSkillMarkdown(\n services: DefinedAgentService[],\n meta: SkillMeta,\n): string {\n const base = meta.baseUrl.replace(/\\/$/, \"\");\n const lines: string[] = [\n `---`,\n `name: ${meta.name}`,\n `description: ${meta.description}`,\n `---`,\n ``,\n `# ${meta.name}`,\n ``,\n meta.description,\n ``,\n ];\n\n if (meta.homepage) {\n lines.push(`Homepage: ${meta.homepage}`, ``);\n }\n\n lines.push(\n `## How to use`,\n ``,\n `1. Read this skill.`,\n `2. Call the agent endpoint with a JSON body matching the input schema.`,\n `3. If you receive HTTP 402, pay via x402 (USDC) and retry with the payment header.`,\n `4. Use the returned output fields.`,\n ``,\n `## Services`,\n ``,\n );\n\n for (const service of services) {\n const path = `${base}/agent/${service.path}`;\n const price = service.payment.amount.startsWith(\"$\")\n ? service.payment.amount\n : `$${service.payment.amount}`;\n\n lines.push(\n `### \\`${service.name}\\``,\n ``,\n service.description,\n ``,\n `- **Method:** \\`POST\\``,\n `- **URL:** \\`${path}\\``,\n `- **Price:** ${price} ${service.payment.currency} via ${service.payment.protocols.join(\", \")}`,\n ``,\n `**Input**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Output**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.output).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Example**`,\n ``,\n \"```bash\",\n `curl -X POST ${path} \\\\`,\n ` -H \"Content-Type: application/json\" \\\\`,\n ` -d '${JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [\n k,\n t === \"url\" ? \"https://example.com/image.png\" : `example-${k}`,\n ]),\n ),\n )}'`,\n \"```\",\n ``,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/define.ts","../src/mount.ts","../src/gateway.ts","../src/gateway-auth.ts","../src/payto.ts","../src/skill.ts"],"sourcesContent":["import type {\n AgentServiceConfig,\n AgentServiceContext,\n DefinedAgentService,\n WaitUntilOptions,\n} from \"./types.js\";\n\nexport async function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 60_000;\n const started = Date.now();\n\n for (;;) {\n const value = await options.run();\n if (options.completedWhen(value)) {\n return value;\n }\n if (Date.now() - started >= timeoutMs) {\n throw new Error(`waitUntil timed out after ${timeoutMs}ms`);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function createContext(): AgentServiceContext {\n return {\n waitUntil,\n };\n}\n\n/** Convert snake_case or camelCase service name to kebab-case path segment. */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\nexport function defineAgentService<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TOutput extends Record<string, unknown> = Record<string, unknown>,\n>(\n config: AgentServiceConfig<TInput, TOutput>,\n): DefinedAgentService<TInput, TOutput> {\n if (!config.name?.trim()) {\n throw new Error(\"defineAgentService: name is required\");\n }\n if (!config.execute) {\n throw new Error(\"defineAgentService: execute is required\");\n }\n\n return {\n ...config,\n __forge: true,\n path: toKebabCase(config.name),\n };\n}\n","import type { Application, Request, Response, NextFunction, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { paymentMiddleware, x402ResourceServer } from \"@x402/express\";\nimport type { Network } from \"@x402/express\";\nimport { HTTPFacilitatorClient } from \"@x402/core/server\";\nimport type { RoutesConfig } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\nimport { createContext } from \"./define.js\";\nimport { heartbeatGateway } from \"./gateway.js\";\nimport {\n getForgeGatewaySecret,\n requireForgeGateway,\n} from \"./gateway-auth.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport type {\n DefinedAgentService,\n InputSchema,\n MountOptions,\n SchemaFieldType,\n} from \"./types.js\";\n\n/** Base mainnet — settled via the public XPay facilitator (no API keys). */\nconst DEFAULT_NETWORK = \"eip155:8453\" as const satisfies Network;\n/** Open facilitator: Base mainnet + Sepolia, no API keys. https://github.com/xpaysh/xpay-x402 */\nconst DEFAULT_FACILITATOR_URL = \"https://facilitator.xpay.sh\";\n\nfunction asNetwork(value: string | undefined): Network {\n return (value ?? DEFAULT_NETWORK) as Network;\n}\n\nfunction createFacilitatorClient(options: MountOptions): HTTPFacilitatorClient {\n return new HTTPFacilitatorClient({\n url: options.facilitatorUrl ?? DEFAULT_FACILITATOR_URL,\n createAuthHeaders: options.createAuthHeaders,\n });\n}\n\nfunction validateInput(\n body: unknown,\n schema: InputSchema,\n): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) {\n return { ok: false, error: \"Request body must be a JSON object\" };\n }\n\n const input = body as Record<string, unknown>;\n const value: Record<string, unknown> = {};\n\n for (const [key, type] of Object.entries(schema)) {\n if (!(key in input) || input[key] === undefined || input[key] === null) {\n return { ok: false, error: `Missing required field: ${key}` };\n }\n const err = checkType(key, input[key], type);\n if (err) return { ok: false, error: err };\n value[key] = input[key];\n }\n\n return { ok: true, value };\n}\n\nfunction checkType(key: string, value: unknown, type: SchemaFieldType): string | null {\n switch (type) {\n case \"string\":\n case \"url\":\n if (typeof value !== \"string\" || !value.trim()) {\n return `Field ${key} must be a non-empty string`;\n }\n if (type === \"url\") {\n try {\n new URL(value);\n } catch {\n return `Field ${key} must be a valid URL`;\n }\n }\n return null;\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return `Field ${key} must be a number`;\n }\n return null;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return `Field ${key} must be a boolean`;\n }\n return null;\n default:\n return null;\n }\n}\n\nfunction formatPrice(amount: string): string {\n const normalized = amount.startsWith(\"$\") ? amount.slice(1) : amount;\n return `$${normalized}`;\n}\n\nfunction mountFulfillmentRouter(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): void {\n const secret = getForgeGatewaySecret(options.gateway?.registerSecret);\n if (!secret) {\n console.warn(\n \"[forge] /forge/fulfill not mounted — set GATEWAY_REGISTER_SECRET (min 16 chars) so the gateway can fulfill after payment\",\n );\n return;\n }\n\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const router = createRouter();\n const context = createContext();\n const gate = requireForgeGateway(secret);\n\n for (const service of services) {\n router.post(\n `/${service.path}`,\n gate,\n async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] fulfill ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n },\n );\n }\n\n app.use(fulfillPath, router);\n console.log(\n `[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`,\n );\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * - POST {basePath}/{kebab-name} — optional public x402 agent surface\n * - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment\n *\n * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.\n */\nexport async function mountAgentServices(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): Promise<Router> {\n let payTo = \"\";\n if (!options.skipPayment) {\n const fallback =\n options.payToFallback?.trim() || options.payTo?.trim() || \"\";\n payTo = await resolvePayTo({\n fallback,\n payTo: options.payTo,\n forge: options.forge,\n });\n if (isZeroAddress(payTo)) {\n throw new Error(\n \"mountAgentServices: resolved payTo is the zero address. Re-run Forge to provision a wallet.\",\n );\n }\n }\n\n const basePath = (options.basePath ?? \"/agent\").replace(/\\/$/, \"\") || \"/agent\";\n const router = createRouter();\n const context = createContext();\n\n const routes: RoutesConfig = {};\n\n for (const service of services) {\n const routePath = `/${service.path}`;\n const fullPath = `${basePath}${routePath}`;\n const network = asNetwork(service.payment.network);\n\n if (!options.skipPayment) {\n routes[`POST ${fullPath}`] = {\n accepts: {\n scheme: \"exact\",\n price: formatPrice(service.payment.amount),\n network,\n payTo,\n },\n description: service.description,\n mimeType: \"application/json\",\n };\n }\n\n router.post(routePath, async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n });\n }\n\n if (!options.skipPayment && Object.keys(routes).length > 0) {\n const facilitator = createFacilitatorClient(options);\n const networks = new Set(\n services.map((s) => asNetwork(s.payment.network)),\n );\n const resourceServer = new x402ResourceServer(facilitator);\n for (const network of networks) {\n resourceServer.register(network, new ExactEvmScheme());\n }\n app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, true));\n }\n\n app.use(basePath, router);\n mountFulfillmentRouter(app, services, options);\n\n const gatewayUrl =\n options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();\n const handle =\n options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();\n const registerSecret =\n options.gateway?.registerSecret?.trim() ||\n process.env.GATEWAY_REGISTER_SECRET?.trim();\n const publicBaseUrl =\n options.gateway?.publicBaseUrl?.trim() ||\n process.env.PUBLIC_BASE_URL?.trim() ||\n (process.env.RAILWAY_PUBLIC_DOMAIN\n ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}`\n : \"\");\n\n if (gatewayUrl && handle && registerSecret && publicBaseUrl) {\n void heartbeatGateway({\n gatewayUrl,\n handle,\n baseUrl: publicBaseUrl,\n registerSecret,\n }).catch((err) => {\n console.warn(\"[forge] gateway heartbeat skipped:\", err);\n });\n }\n\n return router;\n}\n\nexport function createForgeRouter(\n services: DefinedAgentService[],\n options: MountOptions,\n): (app: Application) => Promise<Router> {\n return (app) => mountAgentServices(app, services, options);\n}\n\nexport function forgeErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction,\n): void {\n const message = err instanceof Error ? err.message : \"Internal error\";\n res.status(500).json({ error: message });\n}\n","/**\n * Notify ClawCash gateway of this merchant's live public base URL.\n * Full action registration still happens from Forge \"Go live\".\n */\nexport async function heartbeatGateway(opts: {\n gatewayUrl: string;\n handle: string;\n baseUrl: string;\n registerSecret: string;\n}): Promise<void> {\n const gateway = opts.gatewayUrl.replace(/\\/$/, \"\");\n const res = await fetch(`${gateway}/register/heartbeat`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${opts.registerSecret}`,\n },\n body: JSON.stringify({\n handle: opts.handle.trim().toLowerCase(),\n baseUrl: opts.baseUrl.replace(/\\/$/, \"\"),\n }),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(\n `gateway heartbeat failed (${res.status}): ${text.slice(0, 240)}`,\n );\n }\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Shared secret auth for ClawCash gateway → merchant fulfillment.\n * Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).\n */\nexport function getForgeGatewaySecret(\n explicit?: string | null,\n): string | null {\n const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();\n if (!value || value.length < 16) return null;\n return value;\n}\n\nexport function isForgeGatewayRequest(\n req: Request,\n secret?: string | null,\n): boolean {\n const expected = getForgeGatewaySecret(secret);\n if (!expected) return false;\n const header = req.header(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt = req.header(\"x-clawcash-gateway\")?.trim() ?? \"\";\n return bearer === expected || alt === expected;\n}\n\n/** Synthetic user id for DB rows created by gateway-driven fulfillment. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\n\nexport function requireForgeGateway(\n secret?: string | null,\n): (req: Request, res: Response, next: NextFunction) => void {\n return (req, res, next) => {\n if (!isForgeGatewayRequest(req, secret)) {\n res.status(401).json({\n error:\n \"Unauthorized — ClawCash gateway secret required (GATEWAY_REGISTER_SECRET)\",\n });\n return;\n }\n next();\n };\n}\n\n/** Headers merchant execute() should send on loopback calls during fulfillment. */\nexport function forgeGatewayLoopbackHeaders(\n secret?: string | null,\n): Record<string, string> {\n const expected = getForgeGatewaySecret(secret);\n if (!expected) return {};\n return {\n Authorization: `Bearer ${expected}`,\n \"X-ClawCash-Gateway\": expected,\n };\n}\n","import type { ForgePayToSource } from \"./types.js\";\n\nexport type { ForgePayToSource };\n\nconst ZERO = /^0x0{40}$/i;\n\nexport function isZeroAddress(address: string | undefined | null): boolean {\n if (!address) return true;\n return ZERO.test(address.trim());\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction pickForgeBaseUrl(source?: ForgePayToSource): string | null {\n const fromEnv = process.env.FORGE_URL?.trim();\n if (fromEnv) return normalizeBaseUrl(fromEnv);\n\n const fromOpts = source?.baseUrl?.trim();\n if (!fromOpts) return null;\n\n const normalized = normalizeBaseUrl(fromOpts);\n // Never call a laptop Forge from a deployed merchant app.\n if (\n /localhost|127\\.0\\.0\\.1/i.test(normalized) &&\n process.env.NODE_ENV === \"production\"\n ) {\n return null;\n }\n return normalized;\n}\n\n/**\n * Resolve merchant payTo with minimal host config:\n * 1. `PAY_TO` env (optional override)\n * 2. Live address from Forge public API (when reachable)\n * 3. Baked-in fallback from the Forge PR\n */\nexport async function resolvePayTo(opts: {\n /** Baked-in address from Forge codegen (required fallback). */\n fallback: string;\n /** Optional explicit override (usually process.env.PAY_TO). */\n payTo?: string;\n forge?: ForgePayToSource;\n /** Fetch timeout ms. Defaults to 2500. */\n timeoutMs?: number;\n}): Promise<string> {\n const envOverride = process.env.PAY_TO?.trim();\n if (envOverride && !isZeroAddress(envOverride)) {\n return envOverride;\n }\n\n const explicit = opts.payTo?.trim();\n if (explicit && !isZeroAddress(explicit) && !opts.forge) {\n return explicit;\n }\n\n const baseUrl = pickForgeBaseUrl(opts.forge);\n if (baseUrl && opts.forge) {\n const url = `${baseUrl}/api/public/pay-to/${encodeURIComponent(opts.forge.owner)}/${encodeURIComponent(opts.forge.repo)}`;\n try {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n opts.timeoutMs ?? 2500,\n );\n const res = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n clearTimeout(timer);\n if (res.ok) {\n const json = (await res.json()) as { address?: string };\n if (json.address && !isZeroAddress(json.address)) {\n return json.address.trim();\n }\n }\n } catch (err) {\n console.warn(\"[forge] live payTo fetch failed, using fallback:\", err);\n }\n }\n\n if (explicit && !isZeroAddress(explicit)) return explicit;\n if (opts.fallback && !isZeroAddress(opts.fallback)) return opts.fallback;\n\n throw new Error(\n \"resolvePayTo: no usable address (set Forge wallet / PAY_TO, or re-run Forge PR)\",\n );\n}\n","import type { DefinedAgentService, SkillMeta } from \"./types.js\";\n\n/**\n * Generate a SKILL.md document for agents from defined Forge services.\n */\nexport function generateSkillMarkdown(\n services: DefinedAgentService[],\n meta: SkillMeta,\n): string {\n const base = meta.baseUrl.replace(/\\/$/, \"\");\n const lines: string[] = [\n `---`,\n `name: ${meta.name}`,\n `description: ${meta.description}`,\n `---`,\n ``,\n `# ${meta.name}`,\n ``,\n meta.description,\n ``,\n ];\n\n if (meta.homepage) {\n lines.push(`Homepage: ${meta.homepage}`, ``);\n }\n\n lines.push(\n `## How to use`,\n ``,\n `1. Read this skill.`,\n `2. Call the agent endpoint with a JSON body matching the input schema.`,\n `3. If you receive HTTP 402, pay via x402 (USDC) and retry with the payment header.`,\n `4. Use the returned output fields.`,\n ``,\n `## Services`,\n ``,\n );\n\n for (const service of services) {\n const path = `${base}/agent/${service.path}`;\n const price = service.payment.amount.startsWith(\"$\")\n ? service.payment.amount\n : `$${service.payment.amount}`;\n\n lines.push(\n `### \\`${service.name}\\``,\n ``,\n service.description,\n ``,\n `- **Method:** \\`POST\\``,\n `- **URL:** \\`${path}\\``,\n `- **Price:** ${price} ${service.payment.currency} via ${service.payment.protocols.join(\", \")}`,\n ``,\n `**Input**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Output**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.output).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Example**`,\n ``,\n \"```bash\",\n `curl -X POST ${path} \\\\`,\n ` -H \"Content-Type: application/json\" \\\\`,\n ` -d '${JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [\n k,\n t === \"url\" ? \"https://example.com/image.png\" : `example-${k}`,\n ]),\n ),\n )}'`,\n \"```\",\n ``,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@clawcash/forge",
3
- "version": "0.1.3",
4
- "description": "ClawCash Forge — define agent-native services with x402 payments",
3
+ "version": "0.1.5",
4
+ "description": "ClawCash Forge — make existing SaaS agent-native (define, mount, x402, gateway fulfill)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",