@clawcash/forge 0.1.5 → 0.1.7
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 +8 -1
- package/dist/index.cjs +141 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -4
- package/dist/index.d.ts +79 -4
- package/dist/index.js +135 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,6 +55,12 @@ Resolution order: `PAY_TO` env (optional override) → live Forge API → `payTo
|
|
|
55
55
|
|
|
56
56
|
Agents call `POST /agent/remove-background` and pay via x402 when challenged with HTTP 402.
|
|
57
57
|
|
|
58
|
+
After mount, the merchant also exposes liveliness probes:
|
|
59
|
+
|
|
60
|
+
- `GET /forge/health` — process up + Forge mounted
|
|
61
|
+
- `GET /forge/status` — readiness (`ready` needs fulfill secret + public URL + actions)
|
|
62
|
+
- `GET /.well-known/clawcash.json` — discovery
|
|
63
|
+
|
|
58
64
|
Default payment network is **Base mainnet** (`eip155:8453`) via the public **XPay** facilitator (`https://facilitator.xpay.sh`) — no API keys.
|
|
59
65
|
|
|
60
66
|
## API
|
|
@@ -62,10 +68,11 @@ Default payment network is **Base mainnet** (`eip155:8453`) via the public **XPa
|
|
|
62
68
|
| Export | Purpose |
|
|
63
69
|
|--------|---------|
|
|
64
70
|
| `defineAgentService` | Declare name, schemas, payment, and `execute` |
|
|
65
|
-
| `mountAgentServices` | Async mount
|
|
71
|
+
| `mountAgentServices` | Async mount agent + fulfill + health/status routes |
|
|
66
72
|
| `resolvePayTo` | Resolve merchant wallet (env → Forge → fallback) |
|
|
67
73
|
| `generateSkillMarkdown` | Build a `SKILL.md` for agents |
|
|
68
74
|
| `waitUntil` / `context.waitUntil` | Poll until a job completes |
|
|
75
|
+
| `heartbeatGateway` | Push live base URL to the ClawCash gateway |
|
|
69
76
|
|
|
70
77
|
## Build
|
|
71
78
|
|
package/dist/index.cjs
CHANGED
|
@@ -21,16 +21,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
FORGE_GATEWAY_USER_ID: () => FORGE_GATEWAY_USER_ID,
|
|
24
|
+
buildForgeSurfaceStatus: () => buildForgeSurfaceStatus,
|
|
25
|
+
buildWellKnownDocument: () => buildWellKnownDocument,
|
|
24
26
|
createContext: () => createContext,
|
|
25
27
|
createForgeRouter: () => createForgeRouter,
|
|
26
28
|
defineAgentService: () => defineAgentService,
|
|
27
29
|
forgeGatewayLoopbackHeaders: () => forgeGatewayLoopbackHeaders,
|
|
30
|
+
forgeSdkVersion: () => forgeSdkVersion,
|
|
28
31
|
generateSkillMarkdown: () => generateSkillMarkdown,
|
|
32
|
+
getForgeApiKey: () => getForgeApiKey,
|
|
29
33
|
getForgeGatewaySecret: () => getForgeGatewaySecret,
|
|
34
|
+
getLastHeartbeat: () => getLastHeartbeat,
|
|
30
35
|
heartbeatGateway: () => heartbeatGateway,
|
|
31
36
|
isForgeGatewayRequest: () => isForgeGatewayRequest,
|
|
32
37
|
isZeroAddress: () => isZeroAddress,
|
|
33
38
|
mountAgentServices: () => mountAgentServices,
|
|
39
|
+
recordHeartbeatResult: () => recordHeartbeatResult,
|
|
34
40
|
requireForgeGateway: () => requireForgeGateway,
|
|
35
41
|
resolvePayTo: () => resolvePayTo,
|
|
36
42
|
toKebabCase: () => toKebabCase,
|
|
@@ -105,17 +111,20 @@ async function heartbeatGateway(opts) {
|
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
// src/gateway-auth.ts
|
|
108
|
-
function
|
|
109
|
-
const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
|
|
114
|
+
function getForgeApiKey(explicit) {
|
|
115
|
+
const value = (explicit ?? process.env.FORGE_API_KEY ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
|
|
110
116
|
if (!value || value.length < 16) return null;
|
|
111
117
|
return value;
|
|
112
118
|
}
|
|
119
|
+
function getForgeGatewaySecret(explicit) {
|
|
120
|
+
return getForgeApiKey(explicit);
|
|
121
|
+
}
|
|
113
122
|
function isForgeGatewayRequest(req, secret) {
|
|
114
|
-
const expected =
|
|
123
|
+
const expected = getForgeApiKey(secret);
|
|
115
124
|
if (!expected) return false;
|
|
116
125
|
const header = req.header("authorization") ?? "";
|
|
117
126
|
const bearer = header.toLowerCase().startsWith("bearer ") ? header.slice(7).trim() : "";
|
|
118
|
-
const alt = req.header("x-clawcash-gateway")?.trim()
|
|
127
|
+
const alt = req.header("x-forge-api-key")?.trim() || req.header("x-clawcash-gateway")?.trim() || "";
|
|
119
128
|
return bearer === expected || alt === expected;
|
|
120
129
|
}
|
|
121
130
|
var FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
|
|
@@ -123,7 +132,7 @@ function requireForgeGateway(secret) {
|
|
|
123
132
|
return (req, res, next) => {
|
|
124
133
|
if (!isForgeGatewayRequest(req, secret)) {
|
|
125
134
|
res.status(401).json({
|
|
126
|
-
error: "Unauthorized \u2014
|
|
135
|
+
error: "Unauthorized \u2014 Forge API key required (FORGE_API_KEY from Forge dashboard)"
|
|
127
136
|
});
|
|
128
137
|
return;
|
|
129
138
|
}
|
|
@@ -131,11 +140,11 @@ function requireForgeGateway(secret) {
|
|
|
131
140
|
};
|
|
132
141
|
}
|
|
133
142
|
function forgeGatewayLoopbackHeaders(secret) {
|
|
134
|
-
const expected =
|
|
143
|
+
const expected = getForgeApiKey(secret);
|
|
135
144
|
if (!expected) return {};
|
|
136
145
|
return {
|
|
137
146
|
Authorization: `Bearer ${expected}`,
|
|
138
|
-
"X-
|
|
147
|
+
"X-Forge-Api-Key": expected
|
|
139
148
|
};
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -199,6 +208,66 @@ async function resolvePayTo(opts) {
|
|
|
199
208
|
);
|
|
200
209
|
}
|
|
201
210
|
|
|
211
|
+
// src/status.ts
|
|
212
|
+
var PACKAGE_VERSION = "0.1.7";
|
|
213
|
+
var lastHeartbeat = {
|
|
214
|
+
attempted: false,
|
|
215
|
+
ok: null,
|
|
216
|
+
at: null,
|
|
217
|
+
error: null
|
|
218
|
+
};
|
|
219
|
+
function recordHeartbeatResult(result) {
|
|
220
|
+
lastHeartbeat = {
|
|
221
|
+
attempted: true,
|
|
222
|
+
ok: result.ok,
|
|
223
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
224
|
+
error: result.ok ? null : result.error
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function getLastHeartbeat() {
|
|
228
|
+
return { ...lastHeartbeat };
|
|
229
|
+
}
|
|
230
|
+
function buildForgeSurfaceStatus(opts) {
|
|
231
|
+
const fulfillPath = opts.fulfillPath.replace(/\/$/, "") || "/forge/fulfill";
|
|
232
|
+
const agentBasePath = opts.agentBasePath.replace(/\/$/, "") || "/agent";
|
|
233
|
+
const ready = opts.fulfillMounted && Boolean(opts.publicBaseUrl) && opts.actions.length > 0;
|
|
234
|
+
return {
|
|
235
|
+
ok: true,
|
|
236
|
+
forge: true,
|
|
237
|
+
version: PACKAGE_VERSION,
|
|
238
|
+
handle: opts.handle,
|
|
239
|
+
publicBaseUrl: opts.publicBaseUrl,
|
|
240
|
+
gatewayUrl: opts.gatewayUrl,
|
|
241
|
+
fulfillMounted: opts.fulfillMounted,
|
|
242
|
+
fulfillPath,
|
|
243
|
+
agentBasePath,
|
|
244
|
+
skipPayment: opts.skipPayment,
|
|
245
|
+
actions: opts.actions,
|
|
246
|
+
ready,
|
|
247
|
+
heartbeat: getLastHeartbeat(),
|
|
248
|
+
endpoints: {
|
|
249
|
+
health: "/forge/health",
|
|
250
|
+
status: "/forge/status",
|
|
251
|
+
wellKnown: "/.well-known/clawcash.json",
|
|
252
|
+
fulfill: fulfillPath,
|
|
253
|
+
agent: agentBasePath
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function buildWellKnownDocument(status) {
|
|
258
|
+
return {
|
|
259
|
+
forge: true,
|
|
260
|
+
version: status.version,
|
|
261
|
+
health: status.endpoints.health,
|
|
262
|
+
status: status.endpoints.status,
|
|
263
|
+
handle: status.handle,
|
|
264
|
+
ready: status.ready
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function forgeSdkVersion() {
|
|
268
|
+
return PACKAGE_VERSION;
|
|
269
|
+
}
|
|
270
|
+
|
|
202
271
|
// src/mount.ts
|
|
203
272
|
var DEFAULT_NETWORK = "eip155:8453";
|
|
204
273
|
var DEFAULT_FACILITATOR_URL = "https://facilitator.xpay.sh";
|
|
@@ -261,10 +330,10 @@ function formatPrice(amount) {
|
|
|
261
330
|
return `$${normalized}`;
|
|
262
331
|
}
|
|
263
332
|
function mountFulfillmentRouter(app, services, options) {
|
|
264
|
-
const secret =
|
|
333
|
+
const secret = getForgeApiKey(options.gateway?.registerSecret);
|
|
265
334
|
if (!secret) {
|
|
266
335
|
console.warn(
|
|
267
|
-
"[forge] /forge/fulfill not mounted \u2014 set
|
|
336
|
+
"[forge] /forge/fulfill not mounted \u2014 set FORGE_API_KEY (from Forge dashboard, min 16 chars) so the gateway can fulfill after payment"
|
|
268
337
|
);
|
|
269
338
|
return;
|
|
270
339
|
}
|
|
@@ -298,6 +367,43 @@ function mountFulfillmentRouter(app, services, options) {
|
|
|
298
367
|
`[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`
|
|
299
368
|
);
|
|
300
369
|
}
|
|
370
|
+
function mountForgeStatusRoutes(app, services, options, opts) {
|
|
371
|
+
const getStatus = () => buildForgeSurfaceStatus({
|
|
372
|
+
handle: opts.handle,
|
|
373
|
+
publicBaseUrl: opts.publicBaseUrl,
|
|
374
|
+
gatewayUrl: opts.gatewayUrl,
|
|
375
|
+
fulfillMounted: opts.fulfillMounted,
|
|
376
|
+
fulfillPath: opts.fulfillPath,
|
|
377
|
+
agentBasePath: opts.agentBasePath,
|
|
378
|
+
skipPayment: Boolean(options.skipPayment),
|
|
379
|
+
actions: services.map((s) => ({
|
|
380
|
+
name: s.name,
|
|
381
|
+
path: s.path,
|
|
382
|
+
price: s.payment.amount
|
|
383
|
+
}))
|
|
384
|
+
});
|
|
385
|
+
app.get("/forge/health", (_req, res) => {
|
|
386
|
+
const status = getStatus();
|
|
387
|
+
res.json({
|
|
388
|
+
ok: true,
|
|
389
|
+
forge: true,
|
|
390
|
+
ready: status.ready,
|
|
391
|
+
handle: status.handle,
|
|
392
|
+
version: status.version,
|
|
393
|
+
actions: status.actions.map((a) => a.name)
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
app.get("/forge/status", (_req, res) => {
|
|
397
|
+
const status = getStatus();
|
|
398
|
+
res.status(status.ready ? 200 : 503).json(status);
|
|
399
|
+
});
|
|
400
|
+
app.get("/.well-known/clawcash.json", (_req, res) => {
|
|
401
|
+
res.json(buildWellKnownDocument(getStatus()));
|
|
402
|
+
});
|
|
403
|
+
console.log(
|
|
404
|
+
"[forge] liveliness: GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json"
|
|
405
|
+
);
|
|
406
|
+
}
|
|
301
407
|
async function mountAgentServices(app, services, options) {
|
|
302
408
|
let payTo = "";
|
|
303
409
|
if (!options.skipPayment) {
|
|
@@ -314,6 +420,7 @@ async function mountAgentServices(app, services, options) {
|
|
|
314
420
|
}
|
|
315
421
|
}
|
|
316
422
|
const basePath = (options.basePath ?? "/agent").replace(/\/$/, "") || "/agent";
|
|
423
|
+
const fulfillPath = (options.fulfillPath ?? "/forge/fulfill").replace(/\/$/, "") || "/forge/fulfill";
|
|
317
424
|
const router = (0, import_express.Router)();
|
|
318
425
|
const context = createContext();
|
|
319
426
|
const routes = {};
|
|
@@ -361,18 +468,33 @@ async function mountAgentServices(app, services, options) {
|
|
|
361
468
|
app.use((0, import_express2.paymentMiddleware)(routes, resourceServer, void 0, void 0, true));
|
|
362
469
|
}
|
|
363
470
|
app.use(basePath, router);
|
|
471
|
+
const fulfillMounted = Boolean(
|
|
472
|
+
getForgeApiKey(options.gateway?.registerSecret)
|
|
473
|
+
);
|
|
364
474
|
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}` : "");
|
|
475
|
+
const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim() || null;
|
|
476
|
+
const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim() || null;
|
|
477
|
+
const registerSecret = options.gateway?.registerSecret?.trim() || process.env.FORGE_API_KEY?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
|
|
478
|
+
const publicBaseUrl = options.gateway?.publicBaseUrl?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.RAILWAY_PUBLIC_DOMAIN ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}` : "") || null;
|
|
479
|
+
mountForgeStatusRoutes(app, services, options, {
|
|
480
|
+
fulfillMounted,
|
|
481
|
+
fulfillPath,
|
|
482
|
+
agentBasePath: basePath,
|
|
483
|
+
handle,
|
|
484
|
+
publicBaseUrl,
|
|
485
|
+
gatewayUrl
|
|
486
|
+
});
|
|
369
487
|
if (gatewayUrl && handle && registerSecret && publicBaseUrl) {
|
|
370
488
|
void heartbeatGateway({
|
|
371
489
|
gatewayUrl,
|
|
372
490
|
handle,
|
|
373
491
|
baseUrl: publicBaseUrl,
|
|
374
492
|
registerSecret
|
|
493
|
+
}).then(() => {
|
|
494
|
+
recordHeartbeatResult({ ok: true });
|
|
375
495
|
}).catch((err) => {
|
|
496
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
497
|
+
recordHeartbeatResult({ ok: false, error: message });
|
|
376
498
|
console.warn("[forge] gateway heartbeat skipped:", err);
|
|
377
499
|
});
|
|
378
500
|
}
|
|
@@ -468,16 +590,22 @@ function generateSkillMarkdown(services, meta) {
|
|
|
468
590
|
// Annotate the CommonJS export names for ESM import in node:
|
|
469
591
|
0 && (module.exports = {
|
|
470
592
|
FORGE_GATEWAY_USER_ID,
|
|
593
|
+
buildForgeSurfaceStatus,
|
|
594
|
+
buildWellKnownDocument,
|
|
471
595
|
createContext,
|
|
472
596
|
createForgeRouter,
|
|
473
597
|
defineAgentService,
|
|
474
598
|
forgeGatewayLoopbackHeaders,
|
|
599
|
+
forgeSdkVersion,
|
|
475
600
|
generateSkillMarkdown,
|
|
601
|
+
getForgeApiKey,
|
|
476
602
|
getForgeGatewaySecret,
|
|
603
|
+
getLastHeartbeat,
|
|
477
604
|
heartbeatGateway,
|
|
478
605
|
isForgeGatewayRequest,
|
|
479
606
|
isZeroAddress,
|
|
480
607
|
mountAgentServices,
|
|
608
|
+
recordHeartbeatResult,
|
|
481
609
|
requireForgeGateway,
|
|
482
610
|
resolvePayTo,
|
|
483
611
|
toKebabCase,
|
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/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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/define.ts","../src/mount.ts","../src/gateway.ts","../src/gateway-auth.ts","../src/payto.ts","../src/status.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 buildForgeSurfaceStatus,\n buildWellKnownDocument,\n forgeSdkVersion,\n getLastHeartbeat,\n recordHeartbeatResult,\n} from \"./status.js\";\nexport type { ForgeHeartbeatState, ForgeSurfaceStatus } from \"./status.js\";\nexport {\n isForgeGatewayRequest,\n requireForgeGateway,\n getForgeApiKey,\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 getForgeApiKey,\n requireForgeGateway,\n} from \"./gateway-auth.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n recordHeartbeatResult,\n} from \"./status.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 = getForgeApiKey(options.gateway?.registerSecret);\n if (!secret) {\n console.warn(\n \"[forge] /forge/fulfill not mounted — set FORGE_API_KEY (from Forge dashboard, 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\nfunction mountForgeStatusRoutes(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n opts: {\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n },\n): void {\n const getStatus = () =>\n buildForgeSurfaceStatus({\n handle: opts.handle,\n publicBaseUrl: opts.publicBaseUrl,\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath: opts.fulfillPath,\n agentBasePath: opts.agentBasePath,\n skipPayment: Boolean(options.skipPayment),\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n\n app.get(\"/forge/health\", (_req: Request, res: Response) => {\n const status = getStatus();\n res.json({\n ok: true,\n forge: true as const,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n });\n\n app.get(\"/forge/status\", (_req: Request, res: Response) => {\n const status = getStatus();\n res.status(status.ready ? 200 : 503).json(status);\n });\n\n app.get(\"/.well-known/clawcash.json\", (_req: Request, res: Response) => {\n res.json(buildWellKnownDocument(getStatus()));\n });\n\n console.log(\n \"[forge] liveliness: GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json\",\n );\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * - GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json — liveliness\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 fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\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 const fulfillMounted = Boolean(\n getForgeApiKey(options.gateway?.registerSecret),\n );\n mountFulfillmentRouter(app, services, options);\n\n const gatewayUrl =\n options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim() || null;\n const handle =\n options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim() || null;\n const registerSecret =\n options.gateway?.registerSecret?.trim() ||\n process.env.FORGE_API_KEY?.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 null;\n\n mountForgeStatusRoutes(app, services, options, {\n fulfillMounted,\n fulfillPath,\n agentBasePath: basePath,\n handle,\n publicBaseUrl,\n gatewayUrl,\n });\n\n if (gatewayUrl && handle && registerSecret && publicBaseUrl) {\n void heartbeatGateway({\n gatewayUrl,\n handle,\n baseUrl: publicBaseUrl,\n registerSecret,\n })\n .then(() => {\n recordHeartbeatResult({ ok: true });\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n recordHeartbeatResult({ ok: false, error: message });\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 * Per-project Forge API key for gateway → merchant fulfillment.\n * Merchants set FORGE_API_KEY from the Forge dashboard (never the shared\n * GATEWAY_REGISTER_SECRET — that is Forge↔gateway platform auth only).\n *\n * GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.\n */\nexport function getForgeApiKey(explicit?: string | null): string | null {\n const value =\n (explicit ?? process.env.FORGE_API_KEY ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();\n if (!value || value.length < 16) return null;\n return value;\n}\n\n/** @deprecated Use getForgeApiKey */\nexport function getForgeGatewaySecret(\n explicit?: string | null,\n): string | null {\n return getForgeApiKey(explicit);\n}\n\nexport function isForgeGatewayRequest(\n req: Request,\n secret?: string | null,\n): boolean {\n const expected = getForgeApiKey(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 =\n req.header(\"x-forge-api-key\")?.trim() ||\n req.header(\"x-clawcash-gateway\")?.trim() ||\n \"\";\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 — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\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 = getForgeApiKey(secret);\n if (!expected) return {};\n return {\n Authorization: `Bearer ${expected}`,\n \"X-Forge-Api-Key\": 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live checks.\n */\n\nexport type ForgeHeartbeatState = {\n attempted: boolean;\n ok: boolean | null;\n at: string | null;\n error: string | null;\n};\n\nexport type ForgeSurfaceStatus = {\n ok: boolean;\n forge: true;\n version: string;\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n /** True when FORGE_API_KEY is set so /forge/fulfill is mounted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Env + fulfill ready — merchant can accept gateway fulfillment. */\n ready: boolean;\n heartbeat: ForgeHeartbeatState;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n agent: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.1.7\";\n\nlet lastHeartbeat: ForgeHeartbeatState = {\n attempted: false,\n ok: null,\n at: null,\n error: null,\n};\n\nexport function recordHeartbeatResult(\n result: { ok: true } | { ok: false; error: string },\n): void {\n lastHeartbeat = {\n attempted: true,\n ok: result.ok,\n at: new Date().toISOString(),\n error: result.ok ? null : result.error,\n };\n}\n\nexport function getLastHeartbeat(): ForgeHeartbeatState {\n return { ...lastHeartbeat };\n}\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const agentBasePath = opts.agentBasePath.replace(/\\/$/, \"\") || \"/agent\";\n const ready =\n opts.fulfillMounted &&\n Boolean(opts.publicBaseUrl) &&\n opts.actions.length > 0;\n\n return {\n ok: true,\n forge: true,\n version: PACKAGE_VERSION,\n handle: opts.handle,\n publicBaseUrl: opts.publicBaseUrl,\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath,\n agentBasePath,\n skipPayment: opts.skipPayment,\n actions: opts.actions,\n ready,\n heartbeat: getLastHeartbeat(),\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\n agent: agentBasePath,\n },\n };\n}\n\nexport function buildWellKnownDocument(status: ForgeSurfaceStatus): {\n forge: true;\n version: string;\n health: string;\n status: string;\n handle: string | null;\n ready: boolean;\n} {\n return {\n forge: true,\n version: status.version,\n health: status.endpoints.health,\n status: status.endpoints.status,\n handle: status.handle,\n ready: status.ready,\n };\n}\n\nexport function forgeSdkVersion(): string {\n return PACKAGE_VERSION;\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
|
@@ -73,11 +73,12 @@ interface MountOptions {
|
|
|
73
73
|
skipPayment?: boolean;
|
|
74
74
|
/**
|
|
75
75
|
* Optional gateway heartbeat after mount.
|
|
76
|
-
* Prefer env: GATEWAY_URL, FORGE_HANDLE,
|
|
76
|
+
* Prefer env: GATEWAY_URL, FORGE_HANDLE, FORGE_API_KEY, PUBLIC_BASE_URL.
|
|
77
77
|
*/
|
|
78
78
|
gateway?: {
|
|
79
79
|
url?: string;
|
|
80
80
|
handle?: string;
|
|
81
|
+
/** @deprecated Prefer FORGE_API_KEY env — per-project key from Forge dashboard. */
|
|
81
82
|
registerSecret?: string;
|
|
82
83
|
/** Merchant public base URL for fulfillment. */
|
|
83
84
|
publicBaseUrl?: string;
|
|
@@ -103,6 +104,7 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
|
|
|
103
104
|
|
|
104
105
|
/**
|
|
105
106
|
* Mount Forge agent services on an Express app.
|
|
107
|
+
* - GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json — liveliness
|
|
106
108
|
* - POST {basePath}/{kebab-name} — optional public x402 agent surface
|
|
107
109
|
* - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
|
|
108
110
|
*
|
|
@@ -145,9 +147,82 @@ declare function heartbeatGateway(opts: {
|
|
|
145
147
|
}): Promise<void>;
|
|
146
148
|
|
|
147
149
|
/**
|
|
148
|
-
*
|
|
149
|
-
* Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).
|
|
150
|
+
* Merchant-side Forge surface status — public probes for deploy / go-live checks.
|
|
150
151
|
*/
|
|
152
|
+
type ForgeHeartbeatState = {
|
|
153
|
+
attempted: boolean;
|
|
154
|
+
ok: boolean | null;
|
|
155
|
+
at: string | null;
|
|
156
|
+
error: string | null;
|
|
157
|
+
};
|
|
158
|
+
type ForgeSurfaceStatus = {
|
|
159
|
+
ok: boolean;
|
|
160
|
+
forge: true;
|
|
161
|
+
version: string;
|
|
162
|
+
handle: string | null;
|
|
163
|
+
publicBaseUrl: string | null;
|
|
164
|
+
gatewayUrl: string | null;
|
|
165
|
+
/** True when FORGE_API_KEY is set so /forge/fulfill is mounted. */
|
|
166
|
+
fulfillMounted: boolean;
|
|
167
|
+
fulfillPath: string;
|
|
168
|
+
agentBasePath: string;
|
|
169
|
+
skipPayment: boolean;
|
|
170
|
+
actions: Array<{
|
|
171
|
+
name: string;
|
|
172
|
+
path: string;
|
|
173
|
+
price: string;
|
|
174
|
+
}>;
|
|
175
|
+
/** Env + fulfill ready — merchant can accept gateway fulfillment. */
|
|
176
|
+
ready: boolean;
|
|
177
|
+
heartbeat: ForgeHeartbeatState;
|
|
178
|
+
endpoints: {
|
|
179
|
+
health: string;
|
|
180
|
+
status: string;
|
|
181
|
+
wellKnown: string;
|
|
182
|
+
fulfill: string;
|
|
183
|
+
agent: string;
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
declare function recordHeartbeatResult(result: {
|
|
187
|
+
ok: true;
|
|
188
|
+
} | {
|
|
189
|
+
ok: false;
|
|
190
|
+
error: string;
|
|
191
|
+
}): void;
|
|
192
|
+
declare function getLastHeartbeat(): ForgeHeartbeatState;
|
|
193
|
+
declare function buildForgeSurfaceStatus(opts: {
|
|
194
|
+
handle: string | null;
|
|
195
|
+
publicBaseUrl: string | null;
|
|
196
|
+
gatewayUrl: string | null;
|
|
197
|
+
fulfillMounted: boolean;
|
|
198
|
+
fulfillPath: string;
|
|
199
|
+
agentBasePath: string;
|
|
200
|
+
skipPayment: boolean;
|
|
201
|
+
actions: Array<{
|
|
202
|
+
name: string;
|
|
203
|
+
path: string;
|
|
204
|
+
price: string;
|
|
205
|
+
}>;
|
|
206
|
+
}): ForgeSurfaceStatus;
|
|
207
|
+
declare function buildWellKnownDocument(status: ForgeSurfaceStatus): {
|
|
208
|
+
forge: true;
|
|
209
|
+
version: string;
|
|
210
|
+
health: string;
|
|
211
|
+
status: string;
|
|
212
|
+
handle: string | null;
|
|
213
|
+
ready: boolean;
|
|
214
|
+
};
|
|
215
|
+
declare function forgeSdkVersion(): string;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Per-project Forge API key for gateway → merchant fulfillment.
|
|
219
|
+
* Merchants set FORGE_API_KEY from the Forge dashboard (never the shared
|
|
220
|
+
* GATEWAY_REGISTER_SECRET — that is Forge↔gateway platform auth only).
|
|
221
|
+
*
|
|
222
|
+
* GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.
|
|
223
|
+
*/
|
|
224
|
+
declare function getForgeApiKey(explicit?: string | null): string | null;
|
|
225
|
+
/** @deprecated Use getForgeApiKey */
|
|
151
226
|
declare function getForgeGatewaySecret(explicit?: string | null): string | null;
|
|
152
227
|
declare function isForgeGatewayRequest(req: Request, secret?: string | null): boolean;
|
|
153
228
|
/** Synthetic user id for DB rows created by gateway-driven fulfillment. */
|
|
@@ -156,4 +231,4 @@ declare function requireForgeGateway(secret?: string | null): (req: Request, res
|
|
|
156
231
|
/** Headers merchant execute() should send on loopback calls during fulfillment. */
|
|
157
232
|
declare function forgeGatewayLoopbackHeaders(secret?: string | null): Record<string, string>;
|
|
158
233
|
|
|
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 };
|
|
234
|
+
export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type ForgeHeartbeatState, type ForgePayToSource, type ForgeSurfaceStatus, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, buildForgeSurfaceStatus, buildWellKnownDocument, createContext, createForgeRouter, defineAgentService, forgeGatewayLoopbackHeaders, forgeSdkVersion, generateSkillMarkdown, getForgeApiKey, getForgeGatewaySecret, getLastHeartbeat, heartbeatGateway, isForgeGatewayRequest, isZeroAddress, mountAgentServices, recordHeartbeatResult, requireForgeGateway, resolvePayTo, toKebabCase, waitUntil };
|
package/dist/index.d.ts
CHANGED
|
@@ -73,11 +73,12 @@ interface MountOptions {
|
|
|
73
73
|
skipPayment?: boolean;
|
|
74
74
|
/**
|
|
75
75
|
* Optional gateway heartbeat after mount.
|
|
76
|
-
* Prefer env: GATEWAY_URL, FORGE_HANDLE,
|
|
76
|
+
* Prefer env: GATEWAY_URL, FORGE_HANDLE, FORGE_API_KEY, PUBLIC_BASE_URL.
|
|
77
77
|
*/
|
|
78
78
|
gateway?: {
|
|
79
79
|
url?: string;
|
|
80
80
|
handle?: string;
|
|
81
|
+
/** @deprecated Prefer FORGE_API_KEY env — per-project key from Forge dashboard. */
|
|
81
82
|
registerSecret?: string;
|
|
82
83
|
/** Merchant public base URL for fulfillment. */
|
|
83
84
|
publicBaseUrl?: string;
|
|
@@ -103,6 +104,7 @@ declare function defineAgentService<TInput extends Record<string, unknown> = Rec
|
|
|
103
104
|
|
|
104
105
|
/**
|
|
105
106
|
* Mount Forge agent services on an Express app.
|
|
107
|
+
* - GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json — liveliness
|
|
106
108
|
* - POST {basePath}/{kebab-name} — optional public x402 agent surface
|
|
107
109
|
* - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment
|
|
108
110
|
*
|
|
@@ -145,9 +147,82 @@ declare function heartbeatGateway(opts: {
|
|
|
145
147
|
}): Promise<void>;
|
|
146
148
|
|
|
147
149
|
/**
|
|
148
|
-
*
|
|
149
|
-
* Same secret as GATEWAY_REGISTER_SECRET (Forge go-live / heartbeat).
|
|
150
|
+
* Merchant-side Forge surface status — public probes for deploy / go-live checks.
|
|
150
151
|
*/
|
|
152
|
+
type ForgeHeartbeatState = {
|
|
153
|
+
attempted: boolean;
|
|
154
|
+
ok: boolean | null;
|
|
155
|
+
at: string | null;
|
|
156
|
+
error: string | null;
|
|
157
|
+
};
|
|
158
|
+
type ForgeSurfaceStatus = {
|
|
159
|
+
ok: boolean;
|
|
160
|
+
forge: true;
|
|
161
|
+
version: string;
|
|
162
|
+
handle: string | null;
|
|
163
|
+
publicBaseUrl: string | null;
|
|
164
|
+
gatewayUrl: string | null;
|
|
165
|
+
/** True when FORGE_API_KEY is set so /forge/fulfill is mounted. */
|
|
166
|
+
fulfillMounted: boolean;
|
|
167
|
+
fulfillPath: string;
|
|
168
|
+
agentBasePath: string;
|
|
169
|
+
skipPayment: boolean;
|
|
170
|
+
actions: Array<{
|
|
171
|
+
name: string;
|
|
172
|
+
path: string;
|
|
173
|
+
price: string;
|
|
174
|
+
}>;
|
|
175
|
+
/** Env + fulfill ready — merchant can accept gateway fulfillment. */
|
|
176
|
+
ready: boolean;
|
|
177
|
+
heartbeat: ForgeHeartbeatState;
|
|
178
|
+
endpoints: {
|
|
179
|
+
health: string;
|
|
180
|
+
status: string;
|
|
181
|
+
wellKnown: string;
|
|
182
|
+
fulfill: string;
|
|
183
|
+
agent: string;
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
declare function recordHeartbeatResult(result: {
|
|
187
|
+
ok: true;
|
|
188
|
+
} | {
|
|
189
|
+
ok: false;
|
|
190
|
+
error: string;
|
|
191
|
+
}): void;
|
|
192
|
+
declare function getLastHeartbeat(): ForgeHeartbeatState;
|
|
193
|
+
declare function buildForgeSurfaceStatus(opts: {
|
|
194
|
+
handle: string | null;
|
|
195
|
+
publicBaseUrl: string | null;
|
|
196
|
+
gatewayUrl: string | null;
|
|
197
|
+
fulfillMounted: boolean;
|
|
198
|
+
fulfillPath: string;
|
|
199
|
+
agentBasePath: string;
|
|
200
|
+
skipPayment: boolean;
|
|
201
|
+
actions: Array<{
|
|
202
|
+
name: string;
|
|
203
|
+
path: string;
|
|
204
|
+
price: string;
|
|
205
|
+
}>;
|
|
206
|
+
}): ForgeSurfaceStatus;
|
|
207
|
+
declare function buildWellKnownDocument(status: ForgeSurfaceStatus): {
|
|
208
|
+
forge: true;
|
|
209
|
+
version: string;
|
|
210
|
+
health: string;
|
|
211
|
+
status: string;
|
|
212
|
+
handle: string | null;
|
|
213
|
+
ready: boolean;
|
|
214
|
+
};
|
|
215
|
+
declare function forgeSdkVersion(): string;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Per-project Forge API key for gateway → merchant fulfillment.
|
|
219
|
+
* Merchants set FORGE_API_KEY from the Forge dashboard (never the shared
|
|
220
|
+
* GATEWAY_REGISTER_SECRET — that is Forge↔gateway platform auth only).
|
|
221
|
+
*
|
|
222
|
+
* GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.
|
|
223
|
+
*/
|
|
224
|
+
declare function getForgeApiKey(explicit?: string | null): string | null;
|
|
225
|
+
/** @deprecated Use getForgeApiKey */
|
|
151
226
|
declare function getForgeGatewaySecret(explicit?: string | null): string | null;
|
|
152
227
|
declare function isForgeGatewayRequest(req: Request, secret?: string | null): boolean;
|
|
153
228
|
/** Synthetic user id for DB rows created by gateway-driven fulfillment. */
|
|
@@ -156,4 +231,4 @@ declare function requireForgeGateway(secret?: string | null): (req: Request, res
|
|
|
156
231
|
/** Headers merchant execute() should send on loopback calls during fulfillment. */
|
|
157
232
|
declare function forgeGatewayLoopbackHeaders(secret?: string | null): Record<string, string>;
|
|
158
233
|
|
|
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 };
|
|
234
|
+
export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type ForgeHeartbeatState, type ForgePayToSource, type ForgeSurfaceStatus, type InputSchema, type MountOptions, type OutputSchema, type PaymentConfig, type SchemaFieldType, type SkillMeta, type WaitUntilOptions, buildForgeSurfaceStatus, buildWellKnownDocument, createContext, createForgeRouter, defineAgentService, forgeGatewayLoopbackHeaders, forgeSdkVersion, generateSkillMarkdown, getForgeApiKey, getForgeGatewaySecret, getLastHeartbeat, heartbeatGateway, isForgeGatewayRequest, isZeroAddress, mountAgentServices, recordHeartbeatResult, requireForgeGateway, resolvePayTo, toKebabCase, waitUntil };
|
package/dist/index.js
CHANGED
|
@@ -65,17 +65,20 @@ async function heartbeatGateway(opts) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// src/gateway-auth.ts
|
|
68
|
-
function
|
|
69
|
-
const value = (explicit ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
|
|
68
|
+
function getForgeApiKey(explicit) {
|
|
69
|
+
const value = (explicit ?? process.env.FORGE_API_KEY ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();
|
|
70
70
|
if (!value || value.length < 16) return null;
|
|
71
71
|
return value;
|
|
72
72
|
}
|
|
73
|
+
function getForgeGatewaySecret(explicit) {
|
|
74
|
+
return getForgeApiKey(explicit);
|
|
75
|
+
}
|
|
73
76
|
function isForgeGatewayRequest(req, secret) {
|
|
74
|
-
const expected =
|
|
77
|
+
const expected = getForgeApiKey(secret);
|
|
75
78
|
if (!expected) return false;
|
|
76
79
|
const header = req.header("authorization") ?? "";
|
|
77
80
|
const bearer = header.toLowerCase().startsWith("bearer ") ? header.slice(7).trim() : "";
|
|
78
|
-
const alt = req.header("x-clawcash-gateway")?.trim()
|
|
81
|
+
const alt = req.header("x-forge-api-key")?.trim() || req.header("x-clawcash-gateway")?.trim() || "";
|
|
79
82
|
return bearer === expected || alt === expected;
|
|
80
83
|
}
|
|
81
84
|
var FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
|
|
@@ -83,7 +86,7 @@ function requireForgeGateway(secret) {
|
|
|
83
86
|
return (req, res, next) => {
|
|
84
87
|
if (!isForgeGatewayRequest(req, secret)) {
|
|
85
88
|
res.status(401).json({
|
|
86
|
-
error: "Unauthorized \u2014
|
|
89
|
+
error: "Unauthorized \u2014 Forge API key required (FORGE_API_KEY from Forge dashboard)"
|
|
87
90
|
});
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
@@ -91,11 +94,11 @@ function requireForgeGateway(secret) {
|
|
|
91
94
|
};
|
|
92
95
|
}
|
|
93
96
|
function forgeGatewayLoopbackHeaders(secret) {
|
|
94
|
-
const expected =
|
|
97
|
+
const expected = getForgeApiKey(secret);
|
|
95
98
|
if (!expected) return {};
|
|
96
99
|
return {
|
|
97
100
|
Authorization: `Bearer ${expected}`,
|
|
98
|
-
"X-
|
|
101
|
+
"X-Forge-Api-Key": expected
|
|
99
102
|
};
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -159,6 +162,66 @@ async function resolvePayTo(opts) {
|
|
|
159
162
|
);
|
|
160
163
|
}
|
|
161
164
|
|
|
165
|
+
// src/status.ts
|
|
166
|
+
var PACKAGE_VERSION = "0.1.7";
|
|
167
|
+
var lastHeartbeat = {
|
|
168
|
+
attempted: false,
|
|
169
|
+
ok: null,
|
|
170
|
+
at: null,
|
|
171
|
+
error: null
|
|
172
|
+
};
|
|
173
|
+
function recordHeartbeatResult(result) {
|
|
174
|
+
lastHeartbeat = {
|
|
175
|
+
attempted: true,
|
|
176
|
+
ok: result.ok,
|
|
177
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
178
|
+
error: result.ok ? null : result.error
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function getLastHeartbeat() {
|
|
182
|
+
return { ...lastHeartbeat };
|
|
183
|
+
}
|
|
184
|
+
function buildForgeSurfaceStatus(opts) {
|
|
185
|
+
const fulfillPath = opts.fulfillPath.replace(/\/$/, "") || "/forge/fulfill";
|
|
186
|
+
const agentBasePath = opts.agentBasePath.replace(/\/$/, "") || "/agent";
|
|
187
|
+
const ready = opts.fulfillMounted && Boolean(opts.publicBaseUrl) && opts.actions.length > 0;
|
|
188
|
+
return {
|
|
189
|
+
ok: true,
|
|
190
|
+
forge: true,
|
|
191
|
+
version: PACKAGE_VERSION,
|
|
192
|
+
handle: opts.handle,
|
|
193
|
+
publicBaseUrl: opts.publicBaseUrl,
|
|
194
|
+
gatewayUrl: opts.gatewayUrl,
|
|
195
|
+
fulfillMounted: opts.fulfillMounted,
|
|
196
|
+
fulfillPath,
|
|
197
|
+
agentBasePath,
|
|
198
|
+
skipPayment: opts.skipPayment,
|
|
199
|
+
actions: opts.actions,
|
|
200
|
+
ready,
|
|
201
|
+
heartbeat: getLastHeartbeat(),
|
|
202
|
+
endpoints: {
|
|
203
|
+
health: "/forge/health",
|
|
204
|
+
status: "/forge/status",
|
|
205
|
+
wellKnown: "/.well-known/clawcash.json",
|
|
206
|
+
fulfill: fulfillPath,
|
|
207
|
+
agent: agentBasePath
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function buildWellKnownDocument(status) {
|
|
212
|
+
return {
|
|
213
|
+
forge: true,
|
|
214
|
+
version: status.version,
|
|
215
|
+
health: status.endpoints.health,
|
|
216
|
+
status: status.endpoints.status,
|
|
217
|
+
handle: status.handle,
|
|
218
|
+
ready: status.ready
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function forgeSdkVersion() {
|
|
222
|
+
return PACKAGE_VERSION;
|
|
223
|
+
}
|
|
224
|
+
|
|
162
225
|
// src/mount.ts
|
|
163
226
|
var DEFAULT_NETWORK = "eip155:8453";
|
|
164
227
|
var DEFAULT_FACILITATOR_URL = "https://facilitator.xpay.sh";
|
|
@@ -221,10 +284,10 @@ function formatPrice(amount) {
|
|
|
221
284
|
return `$${normalized}`;
|
|
222
285
|
}
|
|
223
286
|
function mountFulfillmentRouter(app, services, options) {
|
|
224
|
-
const secret =
|
|
287
|
+
const secret = getForgeApiKey(options.gateway?.registerSecret);
|
|
225
288
|
if (!secret) {
|
|
226
289
|
console.warn(
|
|
227
|
-
"[forge] /forge/fulfill not mounted \u2014 set
|
|
290
|
+
"[forge] /forge/fulfill not mounted \u2014 set FORGE_API_KEY (from Forge dashboard, min 16 chars) so the gateway can fulfill after payment"
|
|
228
291
|
);
|
|
229
292
|
return;
|
|
230
293
|
}
|
|
@@ -258,6 +321,43 @@ function mountFulfillmentRouter(app, services, options) {
|
|
|
258
321
|
`[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`
|
|
259
322
|
);
|
|
260
323
|
}
|
|
324
|
+
function mountForgeStatusRoutes(app, services, options, opts) {
|
|
325
|
+
const getStatus = () => buildForgeSurfaceStatus({
|
|
326
|
+
handle: opts.handle,
|
|
327
|
+
publicBaseUrl: opts.publicBaseUrl,
|
|
328
|
+
gatewayUrl: opts.gatewayUrl,
|
|
329
|
+
fulfillMounted: opts.fulfillMounted,
|
|
330
|
+
fulfillPath: opts.fulfillPath,
|
|
331
|
+
agentBasePath: opts.agentBasePath,
|
|
332
|
+
skipPayment: Boolean(options.skipPayment),
|
|
333
|
+
actions: services.map((s) => ({
|
|
334
|
+
name: s.name,
|
|
335
|
+
path: s.path,
|
|
336
|
+
price: s.payment.amount
|
|
337
|
+
}))
|
|
338
|
+
});
|
|
339
|
+
app.get("/forge/health", (_req, res) => {
|
|
340
|
+
const status = getStatus();
|
|
341
|
+
res.json({
|
|
342
|
+
ok: true,
|
|
343
|
+
forge: true,
|
|
344
|
+
ready: status.ready,
|
|
345
|
+
handle: status.handle,
|
|
346
|
+
version: status.version,
|
|
347
|
+
actions: status.actions.map((a) => a.name)
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
app.get("/forge/status", (_req, res) => {
|
|
351
|
+
const status = getStatus();
|
|
352
|
+
res.status(status.ready ? 200 : 503).json(status);
|
|
353
|
+
});
|
|
354
|
+
app.get("/.well-known/clawcash.json", (_req, res) => {
|
|
355
|
+
res.json(buildWellKnownDocument(getStatus()));
|
|
356
|
+
});
|
|
357
|
+
console.log(
|
|
358
|
+
"[forge] liveliness: GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json"
|
|
359
|
+
);
|
|
360
|
+
}
|
|
261
361
|
async function mountAgentServices(app, services, options) {
|
|
262
362
|
let payTo = "";
|
|
263
363
|
if (!options.skipPayment) {
|
|
@@ -274,6 +374,7 @@ async function mountAgentServices(app, services, options) {
|
|
|
274
374
|
}
|
|
275
375
|
}
|
|
276
376
|
const basePath = (options.basePath ?? "/agent").replace(/\/$/, "") || "/agent";
|
|
377
|
+
const fulfillPath = (options.fulfillPath ?? "/forge/fulfill").replace(/\/$/, "") || "/forge/fulfill";
|
|
277
378
|
const router = createRouter();
|
|
278
379
|
const context = createContext();
|
|
279
380
|
const routes = {};
|
|
@@ -321,18 +422,33 @@ async function mountAgentServices(app, services, options) {
|
|
|
321
422
|
app.use(paymentMiddleware(routes, resourceServer, void 0, void 0, true));
|
|
322
423
|
}
|
|
323
424
|
app.use(basePath, router);
|
|
425
|
+
const fulfillMounted = Boolean(
|
|
426
|
+
getForgeApiKey(options.gateway?.registerSecret)
|
|
427
|
+
);
|
|
324
428
|
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}` : "");
|
|
429
|
+
const gatewayUrl = options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim() || null;
|
|
430
|
+
const handle = options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim() || null;
|
|
431
|
+
const registerSecret = options.gateway?.registerSecret?.trim() || process.env.FORGE_API_KEY?.trim() || process.env.GATEWAY_REGISTER_SECRET?.trim();
|
|
432
|
+
const publicBaseUrl = options.gateway?.publicBaseUrl?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.RAILWAY_PUBLIC_DOMAIN ? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}` : "") || null;
|
|
433
|
+
mountForgeStatusRoutes(app, services, options, {
|
|
434
|
+
fulfillMounted,
|
|
435
|
+
fulfillPath,
|
|
436
|
+
agentBasePath: basePath,
|
|
437
|
+
handle,
|
|
438
|
+
publicBaseUrl,
|
|
439
|
+
gatewayUrl
|
|
440
|
+
});
|
|
329
441
|
if (gatewayUrl && handle && registerSecret && publicBaseUrl) {
|
|
330
442
|
void heartbeatGateway({
|
|
331
443
|
gatewayUrl,
|
|
332
444
|
handle,
|
|
333
445
|
baseUrl: publicBaseUrl,
|
|
334
446
|
registerSecret
|
|
447
|
+
}).then(() => {
|
|
448
|
+
recordHeartbeatResult({ ok: true });
|
|
335
449
|
}).catch((err) => {
|
|
450
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
451
|
+
recordHeartbeatResult({ ok: false, error: message });
|
|
336
452
|
console.warn("[forge] gateway heartbeat skipped:", err);
|
|
337
453
|
});
|
|
338
454
|
}
|
|
@@ -427,16 +543,22 @@ function generateSkillMarkdown(services, meta) {
|
|
|
427
543
|
}
|
|
428
544
|
export {
|
|
429
545
|
FORGE_GATEWAY_USER_ID,
|
|
546
|
+
buildForgeSurfaceStatus,
|
|
547
|
+
buildWellKnownDocument,
|
|
430
548
|
createContext,
|
|
431
549
|
createForgeRouter,
|
|
432
550
|
defineAgentService,
|
|
433
551
|
forgeGatewayLoopbackHeaders,
|
|
552
|
+
forgeSdkVersion,
|
|
434
553
|
generateSkillMarkdown,
|
|
554
|
+
getForgeApiKey,
|
|
435
555
|
getForgeGatewaySecret,
|
|
556
|
+
getLastHeartbeat,
|
|
436
557
|
heartbeatGateway,
|
|
437
558
|
isForgeGatewayRequest,
|
|
438
559
|
isZeroAddress,
|
|
439
560
|
mountAgentServices,
|
|
561
|
+
recordHeartbeatResult,
|
|
440
562
|
requireForgeGateway,
|
|
441
563
|
resolvePayTo,
|
|
442
564
|
toKebabCase,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/define.ts","../src/mount.ts","../src/gateway.ts","../src/gateway-auth.ts","../src/payto.ts","../src/status.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 getForgeApiKey,\n requireForgeGateway,\n} from \"./gateway-auth.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n recordHeartbeatResult,\n} from \"./status.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 = getForgeApiKey(options.gateway?.registerSecret);\n if (!secret) {\n console.warn(\n \"[forge] /forge/fulfill not mounted — set FORGE_API_KEY (from Forge dashboard, 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\nfunction mountForgeStatusRoutes(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n opts: {\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n },\n): void {\n const getStatus = () =>\n buildForgeSurfaceStatus({\n handle: opts.handle,\n publicBaseUrl: opts.publicBaseUrl,\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath: opts.fulfillPath,\n agentBasePath: opts.agentBasePath,\n skipPayment: Boolean(options.skipPayment),\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n\n app.get(\"/forge/health\", (_req: Request, res: Response) => {\n const status = getStatus();\n res.json({\n ok: true,\n forge: true as const,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n });\n\n app.get(\"/forge/status\", (_req: Request, res: Response) => {\n const status = getStatus();\n res.status(status.ready ? 200 : 503).json(status);\n });\n\n app.get(\"/.well-known/clawcash.json\", (_req: Request, res: Response) => {\n res.json(buildWellKnownDocument(getStatus()));\n });\n\n console.log(\n \"[forge] liveliness: GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json\",\n );\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * - GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json — liveliness\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 fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\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 const fulfillMounted = Boolean(\n getForgeApiKey(options.gateway?.registerSecret),\n );\n mountFulfillmentRouter(app, services, options);\n\n const gatewayUrl =\n options.gateway?.url?.trim() || process.env.GATEWAY_URL?.trim() || null;\n const handle =\n options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim() || null;\n const registerSecret =\n options.gateway?.registerSecret?.trim() ||\n process.env.FORGE_API_KEY?.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 null;\n\n mountForgeStatusRoutes(app, services, options, {\n fulfillMounted,\n fulfillPath,\n agentBasePath: basePath,\n handle,\n publicBaseUrl,\n gatewayUrl,\n });\n\n if (gatewayUrl && handle && registerSecret && publicBaseUrl) {\n void heartbeatGateway({\n gatewayUrl,\n handle,\n baseUrl: publicBaseUrl,\n registerSecret,\n })\n .then(() => {\n recordHeartbeatResult({ ok: true });\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n recordHeartbeatResult({ ok: false, error: message });\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 * Per-project Forge API key for gateway → merchant fulfillment.\n * Merchants set FORGE_API_KEY from the Forge dashboard (never the shared\n * GATEWAY_REGISTER_SECRET — that is Forge↔gateway platform auth only).\n *\n * GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.\n */\nexport function getForgeApiKey(explicit?: string | null): string | null {\n const value =\n (explicit ?? process.env.FORGE_API_KEY ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();\n if (!value || value.length < 16) return null;\n return value;\n}\n\n/** @deprecated Use getForgeApiKey */\nexport function getForgeGatewaySecret(\n explicit?: string | null,\n): string | null {\n return getForgeApiKey(explicit);\n}\n\nexport function isForgeGatewayRequest(\n req: Request,\n secret?: string | null,\n): boolean {\n const expected = getForgeApiKey(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 =\n req.header(\"x-forge-api-key\")?.trim() ||\n req.header(\"x-clawcash-gateway\")?.trim() ||\n \"\";\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 — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\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 = getForgeApiKey(secret);\n if (!expected) return {};\n return {\n Authorization: `Bearer ${expected}`,\n \"X-Forge-Api-Key\": 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live checks.\n */\n\nexport type ForgeHeartbeatState = {\n attempted: boolean;\n ok: boolean | null;\n at: string | null;\n error: string | null;\n};\n\nexport type ForgeSurfaceStatus = {\n ok: boolean;\n forge: true;\n version: string;\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n /** True when FORGE_API_KEY is set so /forge/fulfill is mounted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Env + fulfill ready — merchant can accept gateway fulfillment. */\n ready: boolean;\n heartbeat: ForgeHeartbeatState;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n agent: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.1.7\";\n\nlet lastHeartbeat: ForgeHeartbeatState = {\n attempted: false,\n ok: null,\n at: null,\n error: null,\n};\n\nexport function recordHeartbeatResult(\n result: { ok: true } | { ok: false; error: string },\n): void {\n lastHeartbeat = {\n attempted: true,\n ok: result.ok,\n at: new Date().toISOString(),\n error: result.ok ? null : result.error,\n };\n}\n\nexport function getLastHeartbeat(): ForgeHeartbeatState {\n return { ...lastHeartbeat };\n}\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const agentBasePath = opts.agentBasePath.replace(/\\/$/, \"\") || \"/agent\";\n const ready =\n opts.fulfillMounted &&\n Boolean(opts.publicBaseUrl) &&\n opts.actions.length > 0;\n\n return {\n ok: true,\n forge: true,\n version: PACKAGE_VERSION,\n handle: opts.handle,\n publicBaseUrl: opts.publicBaseUrl,\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath,\n agentBasePath,\n skipPayment: opts.skipPayment,\n actions: opts.actions,\n ready,\n heartbeat: getLastHeartbeat(),\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\n agent: agentBasePath,\n },\n };\n}\n\nexport function buildWellKnownDocument(status: ForgeSurfaceStatus): {\n forge: true;\n version: string;\n health: string;\n status: string;\n handle: string | null;\n ready: boolean;\n} {\n return {\n forge: true,\n version: status.version,\n health: status.endpoints.health,\n status: status.endpoints.status,\n handle: status.handle,\n ready: status.ready,\n };\n}\n\nexport function forgeSdkVersion(): string {\n return PACKAGE_VERSION;\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 — make existing SaaS agent-native (define, mount, x402, gateway fulfill)",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "ClawCash Forge — make existing SaaS agent-native (define, mount, x402, gateway fulfill, health)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|