@clawcash/forge 0.1.4 → 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 +5 -1
- package/dist/index.cjs +84 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# @clawcash/forge
|
|
2
2
|
|
|
3
|
-
|
|
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,13 +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,
|
|
27
30
|
heartbeatGateway: () => heartbeatGateway,
|
|
31
|
+
isForgeGatewayRequest: () => isForgeGatewayRequest,
|
|
28
32
|
isZeroAddress: () => isZeroAddress,
|
|
29
33
|
mountAgentServices: () => mountAgentServices,
|
|
34
|
+
requireForgeGateway: () => requireForgeGateway,
|
|
30
35
|
resolvePayTo: () => resolvePayTo,
|
|
31
36
|
toKebabCase: () => toKebabCase,
|
|
32
37
|
waitUntil: () => waitUntil
|
|
@@ -99,6 +104,41 @@ async function heartbeatGateway(opts) {
|
|
|
99
104
|
}
|
|
100
105
|
}
|
|
101
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
|
+
|
|
102
142
|
// src/payto.ts
|
|
103
143
|
var ZERO = /^0x0{40}$/i;
|
|
104
144
|
function isZeroAddress(address) {
|
|
@@ -220,6 +260,44 @@ function formatPrice(amount) {
|
|
|
220
260
|
const normalized = amount.startsWith("$") ? amount.slice(1) : amount;
|
|
221
261
|
return `$${normalized}`;
|
|
222
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
|
+
}
|
|
223
301
|
async function mountAgentServices(app, services, options) {
|
|
224
302
|
let payTo = "";
|
|
225
303
|
if (!options.skipPayment) {
|
|
@@ -283,6 +361,7 @@ async function mountAgentServices(app, services, options) {
|
|
|
283
361
|
app.use((0, import_express2.paymentMiddleware)(routes, resourceServer, void 0, void 0, true));
|
|
284
362
|
}
|
|
285
363
|
app.use(basePath, router);
|
|
364
|
+
mountFulfillmentRouter(app, services, options);
|
|
286
365
|
const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();
|
|
287
366
|
const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();
|
|
288
367
|
const registerSecret = options.gateway?.registerSecret?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
|
|
@@ -388,13 +467,18 @@ function generateSkillMarkdown(services, meta) {
|
|
|
388
467
|
}
|
|
389
468
|
// Annotate the CommonJS export names for ESM import in node:
|
|
390
469
|
0 && (module.exports = {
|
|
470
|
+
FORGE_GATEWAY_USER_ID,
|
|
391
471
|
createContext,
|
|
392
472
|
createForgeRouter,
|
|
393
473
|
defineAgentService,
|
|
474
|
+
forgeGatewayLoopbackHeaders,
|
|
394
475
|
generateSkillMarkdown,
|
|
476
|
+
getForgeGatewaySecret,
|
|
395
477
|
heartbeatGateway,
|
|
478
|
+
isForgeGatewayRequest,
|
|
396
479
|
isZeroAddress,
|
|
397
480
|
mountAgentServices,
|
|
481
|
+
requireForgeGateway,
|
|
398
482
|
resolvePayTo,
|
|
399
483
|
toKebabCase,
|
|
400
484
|
waitUntil
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/define.ts","../src/mount.ts","../src/gateway.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 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 { 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\n // Fire-and-forget baseUrl heartbeat so gateway fulfillment stays current.\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\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","/**\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 { 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>;
|
|
@@ -82,6 +82,11 @@ interface MountOptions {
|
|
|
82
82
|
/** Merchant public base URL for fulfillment. */
|
|
83
83
|
publicBaseUrl?: string;
|
|
84
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;
|
|
85
90
|
}
|
|
86
91
|
interface SkillMeta {
|
|
87
92
|
name: string;
|
|
@@ -98,13 +103,12 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
|
|
|
98
103
|
|
|
99
104
|
/**
|
|
100
105
|
* Mount Forge agent services on an Express app.
|
|
101
|
-
*
|
|
106
|
+
* - POST {basePath}/{kebab-name} — optional public x402 agent surface
|
|
107
|
+
* - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
|
|
102
108
|
*
|
|
103
109
|
* payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.
|
|
104
|
-
* Merchants should not need to configure PAY_TO on Railway for the happy path.
|
|
105
110
|
*/
|
|
106
111
|
declare function mountAgentServices(app: Application, services: DefinedAgentService[], options: MountOptions): Promise<Router>;
|
|
107
|
-
/** Express-compatible middleware alias used in docs. */
|
|
108
112
|
declare function createForgeRouter(services: DefinedAgentService[], options: MountOptions): (app: Application) => Promise<Router>;
|
|
109
113
|
|
|
110
114
|
/**
|
|
@@ -140,4 +144,16 @@ declare function heartbeatGateway(opts: {
|
|
|
140
144
|
registerSecret: string;
|
|
141
145
|
}): Promise<void>;
|
|
142
146
|
|
|
143
|
-
|
|
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>;
|
|
@@ -82,6 +82,11 @@ interface MountOptions {
|
|
|
82
82
|
/** Merchant public base URL for fulfillment. */
|
|
83
83
|
publicBaseUrl?: string;
|
|
84
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;
|
|
85
90
|
}
|
|
86
91
|
interface SkillMeta {
|
|
87
92
|
name: string;
|
|
@@ -98,13 +103,12 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
|
|
|
98
103
|
|
|
99
104
|
/**
|
|
100
105
|
* Mount Forge agent services on an Express app.
|
|
101
|
-
*
|
|
106
|
+
* - POST {basePath}/{kebab-name} — optional public x402 agent surface
|
|
107
|
+
* - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
|
|
102
108
|
*
|
|
103
109
|
* payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.
|
|
104
|
-
* Merchants should not need to configure PAY_TO on Railway for the happy path.
|
|
105
110
|
*/
|
|
106
111
|
declare function mountAgentServices(app: Application, services: DefinedAgentService[], options: MountOptions): Promise<Router>;
|
|
107
|
-
/** Express-compatible middleware alias used in docs. */
|
|
108
112
|
declare function createForgeRouter(services: DefinedAgentService[], options: MountOptions): (app: Application) => Promise<Router>;
|
|
109
113
|
|
|
110
114
|
/**
|
|
@@ -140,4 +144,16 @@ declare function heartbeatGateway(opts: {
|
|
|
140
144
|
registerSecret: string;
|
|
141
145
|
}): Promise<void>;
|
|
142
146
|
|
|
143
|
-
|
|
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
|
@@ -64,6 +64,41 @@ async function heartbeatGateway(opts) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
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
|
+
|
|
67
102
|
// src/payto.ts
|
|
68
103
|
var ZERO = /^0x0{40}$/i;
|
|
69
104
|
function isZeroAddress(address) {
|
|
@@ -185,6 +220,44 @@ function formatPrice(amount) {
|
|
|
185
220
|
const normalized = amount.startsWith("$") ? amount.slice(1) : amount;
|
|
186
221
|
return `$${normalized}`;
|
|
187
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
|
+
}
|
|
188
261
|
async function mountAgentServices(app, services, options) {
|
|
189
262
|
let payTo = "";
|
|
190
263
|
if (!options.skipPayment) {
|
|
@@ -248,6 +321,7 @@ async function mountAgentServices(app, services, options) {
|
|
|
248
321
|
app.use(paymentMiddleware(routes, resourceServer, void 0, void 0, true));
|
|
249
322
|
}
|
|
250
323
|
app.use(basePath, router);
|
|
324
|
+
mountFulfillmentRouter(app, services, options);
|
|
251
325
|
const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim();
|
|
252
326
|
const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim();
|
|
253
327
|
const registerSecret = options.gateway?.registerSecret?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
|
|
@@ -352,13 +426,18 @@ function generateSkillMarkdown(services, meta) {
|
|
|
352
426
|
return lines.join("\n");
|
|
353
427
|
}
|
|
354
428
|
export {
|
|
429
|
+
FORGE_GATEWAY_USER_ID,
|
|
355
430
|
createContext,
|
|
356
431
|
createForgeRouter,
|
|
357
432
|
defineAgentService,
|
|
433
|
+
forgeGatewayLoopbackHeaders,
|
|
358
434
|
generateSkillMarkdown,
|
|
435
|
+
getForgeGatewaySecret,
|
|
359
436
|
heartbeatGateway,
|
|
437
|
+
isForgeGatewayRequest,
|
|
360
438
|
isZeroAddress,
|
|
361
439
|
mountAgentServices,
|
|
440
|
+
requireForgeGateway,
|
|
362
441
|
resolvePayTo,
|
|
363
442
|
toKebabCase,
|
|
364
443
|
waitUntil
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/define.ts","../src/mount.ts","../src/gateway.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 { 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\n // Fire-and-forget baseUrl heartbeat so gateway fulfillment stays current.\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\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","/**\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 { 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.
|
|
4
|
-
"description": "ClawCash Forge —
|
|
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",
|