@clawcash/forge 0.2.0 → 0.2.1
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 +2 -0
- package/dist/index.cjs +12 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,8 @@ Env: `FORGE_API_KEY` (required, from the Forge dashboard), `FORGE_HANDLE` (if no
|
|
|
58
58
|
|
|
59
59
|
The worker makes outbound requests only. It runs behind NAT, on localhost, in a private subnet — anywhere with outbound HTTPS. Polling doubles as the merchant heartbeat.
|
|
60
60
|
|
|
61
|
+
If the handle is not registered yet (`claim` returns 404), the worker **keeps waiting** — deploy first, then press **Go live** on the Forge dashboard. Only a bad `FORGE_API_KEY` (401) stops the worker.
|
|
62
|
+
|
|
61
63
|
## Fetch-handler mode (inbound alternative)
|
|
62
64
|
|
|
63
65
|
For gateway-push deployments, `createForgeHandler(services)` returns a web-standard `(Request) => Promise<Response>` serving `/forge/health`, `/forge/status`, `/.well-known/clawcash.json`, and `POST /forge/fulfill/{action}` (gated by `FORGE_API_KEY`). Mounts natively in Next.js route handlers, Hono, Bun, Deno; `createForgeRequestMatcher` returns `null` on unmatched routes for fall-through mounting.
|
package/dist/index.cjs
CHANGED
|
@@ -284,11 +284,20 @@ function forgeWorker(options) {
|
|
|
284
284
|
{ handle, workerId, waitMs },
|
|
285
285
|
controller.signal
|
|
286
286
|
);
|
|
287
|
-
if (status === 401
|
|
287
|
+
if (status === 401) {
|
|
288
288
|
throw new Error(
|
|
289
|
-
`claim rejected (
|
|
289
|
+
`claim rejected (401): ${JSON.stringify(json2).slice(0, 300)} \u2014 check FORGE_API_KEY matches the Forge dashboard key`
|
|
290
290
|
);
|
|
291
291
|
}
|
|
292
|
+
if (status === 404 || status === 409) {
|
|
293
|
+
const why = status === 404 ? `handle "${handle}" is not registered yet \u2014 press Go live on the Forge dashboard` : `merchant is not in worker mode yet \u2014 press Go live again`;
|
|
294
|
+
console.warn(
|
|
295
|
+
`[forge-worker] waiting: ${why} (${JSON.stringify(json2).slice(0, 160)})`
|
|
296
|
+
);
|
|
297
|
+
throw Object.assign(new Error(`claim waiting (${status})`), {
|
|
298
|
+
transient: true
|
|
299
|
+
});
|
|
300
|
+
}
|
|
292
301
|
if (status >= 400) {
|
|
293
302
|
throw Object.assign(
|
|
294
303
|
new Error(`claim failed (${status})`),
|
|
@@ -305,7 +314,7 @@ function forgeWorker(options) {
|
|
|
305
314
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
306
315
|
if (error.name === "AbortError") break;
|
|
307
316
|
reportError(error);
|
|
308
|
-
const transient = error.transient || !/claim rejected/.test(error.message);
|
|
317
|
+
const transient = error.transient === true || !/claim rejected \(401\)/.test(error.message);
|
|
309
318
|
if (!transient) {
|
|
310
319
|
console.error(
|
|
311
320
|
"[forge-worker] fatal configuration error \u2014 stopping worker"
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/define.ts","../src/worker.ts","../src/gateway-auth.ts","../src/validate.ts","../src/status.ts","../src/handler.ts"],"sourcesContent":["// @clawcash/forge — merchant SDK for the ClawCash gateway.\n//\n// Default integration: forgeWorker() — outbound-only fulfillment, no inbound\n// routes, no framework coupling. createForgeHandler() is the inbound\n// alternative for gateway-push deployments. Payment (x402) lives entirely on\n// the gateway; this package has zero runtime dependencies.\n\nexport { defineAgentService, waitUntil, createContext, toKebabCase } from \"./define.js\";\nexport { forgeWorker } from \"./worker.js\";\nexport type { ForgeWorkerOptions, ForgeWorkerHandle } from \"./worker.js\";\nexport { createForgeHandler, createForgeRequestMatcher } from \"./handler.js\";\nexport type { ForgeHandlerOptions, ForgeFetchHandler } from \"./handler.js\";\nexport { validateInput, checkType } from \"./validate.js\";\nexport type { ValidationResult } from \"./validate.js\";\nexport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n forgeSdkVersion,\n} from \"./status.js\";\nexport type { ForgeSurfaceStatus } from \"./status.js\";\nexport {\n getForgeApiKey,\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} 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 os from \"node:os\";\nimport { createContext, toKebabCase } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Outbound worker mode — the default v2 integration.\n *\n * The worker makes only outbound HTTP calls to the ClawCash gateway:\n * it long-polls POST /jobs/claim for jobs agents have already paid for,\n * runs the matching service's execute(), and posts the result back.\n * No inbound routes, no public URL, no entry-point changes.\n *\n * Polling doubles as the merchant heartbeat — the gateway treats a recent\n * claim poll as \"worker online\".\n */\n\nconst DEFAULT_GATEWAY_URL = \"https://gateway.clawca.sh\";\nconst DEFAULT_WAIT_MS = 20_000;\nconst BACKOFF_STEPS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];\n\nexport interface ForgeWorkerOptions {\n services: DefinedAgentService[];\n /** Merchant handle. Defaults to FORGE_HANDLE env. */\n handle?: string;\n /** Per-project key from the Forge dashboard. Defaults to FORGE_API_KEY env. */\n apiKey?: string;\n /** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */\n gatewayUrl?: string;\n /** Shown in gateway job rows. Defaults to hostname-pid. */\n workerId?: string;\n /** Long-poll window per claim request. Defaults to 20s (gateway caps at 25s). */\n waitMs?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface ForgeWorkerHandle {\n stop: () => void;\n readonly running: boolean;\n}\n\ninterface ClaimedJob {\n id: string;\n action: string;\n input: Record<string, unknown>;\n leaseMs: number;\n attempt: number;\n}\n\nfunction findService(\n services: DefinedAgentService[],\n action: string,\n): DefinedAgentService | null {\n const key = toKebabCase(action.trim());\n return (\n services.find((s) => s.path === key || toKebabCase(s.name) === key) ?? null\n );\n}\n\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n signal: AbortSignal,\n): Promise<{ status: number; json: Record<string, unknown> }> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n const text = await res.text();\n let json: Record<string, unknown> = {};\n try {\n json = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n json = { raw: text };\n }\n return { status: res.status, json };\n}\n\nexport function forgeWorker(options: ForgeWorkerOptions): ForgeWorkerHandle {\n const handle = (options.handle ?? process.env.FORGE_HANDLE ?? \"\")\n .trim()\n .toLowerCase();\n if (!handle) {\n throw new Error(\n \"forgeWorker: handle is required (option or FORGE_HANDLE env)\",\n );\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!apiKey) {\n throw new Error(\n \"forgeWorker: FORGE_API_KEY is required (per-project key from the Forge dashboard, min 16 chars)\",\n );\n }\n if (!options.services?.length) {\n throw new Error(\"forgeWorker: services must be a non-empty array\");\n }\n\n const gatewayUrl = (\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n DEFAULT_GATEWAY_URL\n ).replace(/\\/$/, \"\");\n const workerId =\n options.workerId?.trim() || `${os.hostname()}-${process.pid}`;\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n\n let running = true;\n const controller = new AbortController();\n let wakeSleeper: (() => void) | null = null;\n\n const reportError = (err: Error) => {\n if (options.onError) options.onError(err);\n else console.warn(`[forge-worker] ${err.message}`);\n };\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n wakeSleeper = null;\n resolve();\n }, ms);\n wakeSleeper = () => {\n clearTimeout(timer);\n wakeSleeper = null;\n resolve();\n };\n });\n\n async function runJob(job: ClaimedJob): Promise<void> {\n const resultUrl = `${gatewayUrl}/jobs/${encodeURIComponent(job.id)}/result`;\n const service = findService(options.services, job.action);\n if (!service) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: `no service registered for action ${job.action}` },\n controller.signal,\n );\n reportError(new Error(`job ${job.id}: unknown action ${job.action}`));\n return;\n }\n\n try {\n const validated = validateInput(job.input, service.input);\n if (!validated.ok) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: validated.error },\n controller.signal,\n );\n return;\n }\n const output = await service.execute(validated.value, createContext());\n const res = await postJson(\n resultUrl,\n apiKey!,\n { ok: true, output },\n controller.signal,\n );\n if (res.status >= 400) {\n reportError(\n new Error(\n `job ${job.id}: result rejected (${res.status}): ${JSON.stringify(res.json).slice(0, 300)}`,\n ),\n );\n } else {\n console.log(`[forge-worker] completed job ${job.id} (${job.action})`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"execute failed\";\n try {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: message },\n controller.signal,\n );\n } catch {\n // gateway unreachable — lease expiry will re-queue the job\n }\n reportError(new Error(`job ${job.id} failed: ${message}`));\n }\n }\n\n async function loop(): Promise<void> {\n console.log(\n `[forge-worker] ${handle} polling ${gatewayUrl}/jobs/claim as ${workerId} (${options.services.length} action(s))`,\n );\n let backoffIndex = 0;\n while (running) {\n try {\n const { status, json } = await postJson(\n `${gatewayUrl}/jobs/claim`,\n apiKey!,\n { handle, workerId, waitMs },\n controller.signal,\n );\n if (status === 401 || status === 404 || status === 409) {\n // Not registered / wrong key / wrong mode — fatal config, not transient.\n throw new Error(\n `claim rejected (${status}): ${JSON.stringify(json).slice(0, 300)} — check FORGE_API_KEY, handle, and that the project is registered with fulfillment.mode=worker`,\n );\n }\n if (status >= 400) {\n throw Object.assign(\n new Error(`claim failed (${status})`),\n { transient: true },\n );\n }\n backoffIndex = 0;\n const job = (json as { job?: ClaimedJob | null }).job;\n if (job) {\n await runJob(job);\n }\n } catch (err) {\n if (!running) break;\n const error = err instanceof Error ? err : new Error(String(err));\n if (error.name === \"AbortError\") break;\n reportError(error);\n const transient =\n (error as { transient?: boolean }).transient ||\n !/claim rejected/.test(error.message);\n if (!transient) {\n console.error(\n \"[forge-worker] fatal configuration error — stopping worker\",\n );\n running = false;\n break;\n }\n const delay =\n BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)];\n backoffIndex += 1;\n await sleep(delay);\n }\n }\n console.log(\"[forge-worker] stopped\");\n }\n\n void loop();\n\n return {\n stop() {\n running = false;\n controller.abort();\n wakeSleeper?.();\n },\n get running() {\n return running;\n },\n };\n}\n","/**\n * Per-project Forge API key helpers — framework-free.\n *\n * Merchants set FORGE_API_KEY from the Forge dashboard. The worker uses it\n * to claim jobs from the gateway; the fetch handler uses it to gate\n * gateway-push fulfillment; execute() can use forgeGatewayLoopbackHeaders()\n * so the merchant's own API middleware can recognize fulfillment traffic.\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/** Synthetic user id for DB rows created by fulfillment traffic. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\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 { InputSchema, SchemaFieldType } from \"./types.js\";\n\n/**\n * Framework-free input validation shared by every execution surface\n * (Express mount, fetch handler, worker).\n */\n\nexport type ValidationResult =\n | { ok: true; value: Record<string, unknown> }\n | { ok: false; error: string };\n\nexport function validateInput(\n body: unknown,\n schema: InputSchema,\n): ValidationResult {\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\nexport function checkType(\n key: string,\n value: unknown,\n type: SchemaFieldType,\n): 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live\n * checks when running the fetch handler. Worker-mode merchants report\n * liveness by polling the gateway instead.\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 fulfillment is accepted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Key + actions present — merchant can accept gateway fulfillment. */\n ready: boolean;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.2.0\";\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const ready = opts.fulfillMounted && 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 actions: opts.actions,\n ready,\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\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 { createContext } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n} from \"./status.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Web-standard fetch handler — mount Forge fulfillment + liveliness on any\n * runtime that speaks (Request) => Response: Next.js route handlers, Hono,\n * Fastify (via adapter), Bun, Deno, plain node:http (via adapter).\n *\n * This surface is for gateway-push (fulfillment.mode=sdk) merchants and\n * deploy probes. Payment lives on the gateway; prefer forgeWorker() when the\n * merchant can run an outbound worker instead.\n *\n * Routes handled (anything else returns null from tryHandleForgeRequest, or\n * 404 from the plain handler):\n * - GET /forge/health\n * - GET /forge/status\n * - GET /.well-known/clawcash.json\n * - POST {fulfillPath}/{action-path} (requires FORGE_API_KEY auth)\n */\n\nexport interface ForgeHandlerOptions {\n /** Merchant handle — FORGE_HANDLE env when omitted. */\n handle?: string;\n /** Per-project key. FORGE_API_KEY env when omitted. */\n apiKey?: string;\n /** Defaults to /forge/fulfill. */\n fulfillPath?: string;\n /** Reported in /forge/status. Optional. */\n publicBaseUrl?: string;\n /** Reported in /forge/status. Defaults to GATEWAY_URL env or the ClawCash gateway. */\n gatewayUrl?: string;\n}\n\nexport type ForgeFetchHandler = (request: Request) => Promise<Response>;\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nfunction isAuthorized(request: Request, apiKey: string | null): boolean {\n if (!apiKey) return false;\n const header = request.headers.get(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt =\n request.headers.get(\"x-forge-api-key\")?.trim() ||\n request.headers.get(\"x-clawcash-gateway\")?.trim() ||\n \"\";\n return bearer === apiKey || alt === apiKey;\n}\n\n/**\n * Returns a Response when the request targets a Forge route, else null so\n * the host app can fall through to its own routing.\n */\nexport function createForgeRequestMatcher(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): (request: Request) => Promise<Response | null> {\n if (!services?.length) {\n throw new Error(\"createForgeHandler: services must be a non-empty array\");\n }\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const handle =\n options.handle?.trim().toLowerCase() ||\n process.env.FORGE_HANDLE?.trim().toLowerCase() ||\n null;\n const gatewayUrl =\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n \"https://gateway.clawca.sh\";\n const context = createContext();\n\n const getStatus = () => {\n const apiKey = getForgeApiKey(options.apiKey);\n return buildForgeSurfaceStatus({\n handle,\n publicBaseUrl: options.publicBaseUrl ?? null,\n gatewayUrl,\n fulfillMounted: Boolean(apiKey),\n fulfillPath,\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n };\n\n return async (request: Request): Promise<Response | null> => {\n const url = new URL(request.url);\n const pathname = url.pathname.replace(/\\/$/, \"\") || \"/\";\n\n if (request.method === \"GET\") {\n if (pathname === \"/forge/health\") {\n const status = getStatus();\n return json({\n ok: true,\n forge: true,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n }\n if (pathname === \"/forge/status\") {\n const status = getStatus();\n return json(status, status.ready ? 200 : 503);\n }\n if (pathname === \"/.well-known/clawcash.json\") {\n return json(buildWellKnownDocument(getStatus()));\n }\n return null;\n }\n\n if (request.method === \"POST\" && pathname.startsWith(`${fulfillPath}/`)) {\n const actionPath = pathname.slice(fulfillPath.length + 1);\n const service = services.find((s) => s.path === actionPath) ?? null;\n if (!service) {\n return json({ error: `Unknown action ${actionPath}` }, 404);\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!isAuthorized(request, apiKey)) {\n return json(\n {\n error:\n \"Unauthorized — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\n },\n 401,\n );\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json({ error: \"Request body must be JSON\" }, 400);\n }\n const validated = validateInput(body, service.input);\n if (!validated.ok) {\n return json({ error: validated.error }, 400);\n }\n try {\n const output = await service.execute(validated.value, context);\n return 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 return json({ error: message }, 500);\n }\n }\n\n return null;\n };\n}\n\n/** Like createForgeRequestMatcher, but 404s instead of returning null. */\nexport function createForgeHandler(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): ForgeFetchHandler {\n const match = createForgeRequestMatcher(services, options);\n return async (request) =>\n (await match(request)) ?? json({ error: \"Not found\" }, 404);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOA,eAAsB,UAAa,SAA0C;AAC3E,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,KAAK,IAAI;AAEzB,aAAS;AACP,UAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,QAAI,QAAQ,cAAc,KAAK,GAAG;AAChC,aAAO;AAAA,IACT;AACA,QAAI,KAAK,IAAI,IAAI,WAAW,WAAW;AACrC,YAAM,IAAI,MAAM,6BAA6B,SAAS,IAAI;AAAA,IAC5D;AACA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,EAChE;AACF;AAEO,SAAS,gBAAqC;AACnD,SAAO;AAAA,IACL;AAAA,EACF;AACF;AAGO,SAAS,YAAY,MAAsB;AAChD,SAAO,KACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,MAAM,GAAG,EACjB,YAAY;AACjB;AAEO,SAAS,mBAId,QACsC;AACtC,MAAI,CAAC,OAAO,MAAM,KAAK,GAAG;AACxB,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS;AAAA,IACT,MAAM,YAAY,OAAO,IAAI;AAAA,EAC/B;AACF;;;ACxDA,qBAAe;;;ACUR,SAAS,eAAe,UAAyC;AACtE,QAAM,SACH,YAAY,QAAQ,IAAI,iBAAiB,QAAQ,IAAI,0BAA0B,KAAK;AACvF,MAAI,CAAC,SAAS,MAAM,SAAS,GAAI,QAAO;AACxC,SAAO;AACT;AAGO,IAAM,wBAAwB;AAG9B,SAAS,4BACd,QACwB;AACxB,QAAM,WAAW,eAAe,MAAM;AACtC,MAAI,CAAC,SAAU,QAAO,CAAC;AACvB,SAAO;AAAA,IACL,eAAe,UAAU,QAAQ;AAAA,IACjC,mBAAmB;AAAA,EACrB;AACF;;;ACnBO,SAAS,cACd,MACA,QACkB;AAClB,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AAC5D,WAAO,EAAE,IAAI,OAAO,OAAO,qCAAqC;AAAA,EAClE;AAEA,QAAM,QAAQ;AACd,QAAM,QAAiC,CAAC;AAExC,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AAChD,QAAI,EAAE,OAAO,UAAU,MAAM,GAAG,MAAM,UAAa,MAAM,GAAG,MAAM,MAAM;AACtE,aAAO,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,GAAG;AAAA,IAC9D;AACA,UAAM,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,IAAI;AAC3C,QAAI,IAAK,QAAO,EAAE,IAAI,OAAO,OAAO,IAAI;AACxC,UAAM,GAAG,IAAI,MAAM,GAAG;AAAA,EACxB;AAEA,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAEO,SAAS,UACd,KACA,OACA,MACe;AACf,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,UAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,UAAI,SAAS,OAAO;AAClB,YAAI;AACF,cAAI,IAAI,KAAK;AAAA,QACf,QAAQ;AACN,iBAAO,SAAS,GAAG;AAAA,QACrB;AAAA,MACF;AACA,aAAO;AAAA,IACT,KAAK;AACH,UAAI,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,GAAG;AACpD,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,aAAO;AAAA,IACT,KAAK;AACH,UAAI,OAAO,UAAU,WAAW;AAC9B,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;AFhDA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AACxB,IAAM,mBAAmB,CAAC,KAAO,KAAO,KAAO,KAAQ,GAAM;AA8B7D,SAAS,YACP,UACA,QAC4B;AAC5B,QAAM,MAAM,YAAY,OAAO,KAAK,CAAC;AACrC,SACE,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,YAAY,EAAE,IAAI,MAAM,GAAG,KAAK;AAE3E;AAEA,eAAe,SACb,KACA,QACA,MACA,QAC4D;AAC5D,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,eAAe,UAAU,MAAM;AAAA,IACjC;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAIA,QAAgC,CAAC;AACrC,MAAI;AACF,IAAAA,QAAO,OAAQ,KAAK,MAAM,IAAI,IAAgC,CAAC;AAAA,EACjE,QAAQ;AACN,IAAAA,QAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AACA,SAAO,EAAE,QAAQ,IAAI,QAAQ,MAAAA,MAAK;AACpC;AAEO,SAAS,YAAY,SAAgD;AAC1E,QAAM,UAAU,QAAQ,UAAU,QAAQ,IAAI,gBAAgB,IAC3D,KAAK,EACL,YAAY;AACf,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,UAAU,QAAQ;AAC7B,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,QAAM,cACJ,QAAQ,YAAY,KAAK,KACzB,QAAQ,IAAI,aAAa,KAAK,KAC9B,qBACA,QAAQ,OAAO,EAAE;AACnB,QAAM,WACJ,QAAQ,UAAU,KAAK,KAAK,GAAG,eAAAC,QAAG,SAAS,CAAC,IAAI,QAAQ,GAAG;AAC7D,QAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,UAAU;AACd,QAAM,aAAa,IAAI,gBAAgB;AACvC,MAAI,cAAmC;AAEvC,QAAM,cAAc,CAAC,QAAe;AAClC,QAAI,QAAQ,QAAS,SAAQ,QAAQ,GAAG;AAAA,QACnC,SAAQ,KAAK,kBAAkB,IAAI,OAAO,EAAE;AAAA,EACnD;AAEA,QAAM,QAAQ,CAAC,OACb,IAAI,QAAc,CAAC,YAAY;AAC7B,UAAM,QAAQ,WAAW,MAAM;AAC7B,oBAAc;AACd,cAAQ;AAAA,IACV,GAAG,EAAE;AACL,kBAAc,MAAM;AAClB,mBAAa,KAAK;AAClB,oBAAc;AACd,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAEH,iBAAe,OAAO,KAAgC;AACpD,UAAM,YAAY,GAAG,UAAU,SAAS,mBAAmB,IAAI,EAAE,CAAC;AAClE,UAAM,UAAU,YAAY,QAAQ,UAAU,IAAI,MAAM;AACxD,QAAI,CAAC,SAAS;AACZ,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,EAAE,IAAI,OAAO,OAAO,oCAAoC,IAAI,MAAM,GAAG;AAAA,QACrE,WAAW;AAAA,MACb;AACA,kBAAY,IAAI,MAAM,OAAO,IAAI,EAAE,oBAAoB,IAAI,MAAM,EAAE,CAAC;AACpE;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,cAAc,IAAI,OAAO,QAAQ,KAAK;AACxD,UAAI,CAAC,UAAU,IAAI;AACjB,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,EAAE,IAAI,OAAO,OAAO,UAAU,MAAM;AAAA,UACpC,WAAW;AAAA,QACb;AACA;AAAA,MACF;AACA,YAAM,SAAS,MAAM,QAAQ,QAAQ,UAAU,OAAO,cAAc,CAAC;AACrE,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,MAAM,OAAO;AAAA,QACnB,WAAW;AAAA,MACb;AACA,UAAI,IAAI,UAAU,KAAK;AACrB;AAAA,UACE,IAAI;AAAA,YACF,OAAO,IAAI,EAAE,sBAAsB,IAAI,MAAM,MAAM,KAAK,UAAU,IAAI,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;AAAA,UAC3F;AAAA,QACF;AAAA,MACF,OAAO;AACL,gBAAQ,IAAI,gCAAgC,IAAI,EAAE,KAAK,IAAI,MAAM,GAAG;AAAA,MACtE;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,UAAI;AACF,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,EAAE,IAAI,OAAO,OAAO,QAAQ;AAAA,UAC5B,WAAW;AAAA,QACb;AAAA,MACF,QAAQ;AAAA,MAER;AACA,kBAAY,IAAI,MAAM,OAAO,IAAI,EAAE,YAAY,OAAO,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,iBAAe,OAAsB;AACnC,YAAQ;AAAA,MACN,kBAAkB,MAAM,YAAY,UAAU,kBAAkB,QAAQ,KAAK,QAAQ,SAAS,MAAM;AAAA,IACtG;AACA,QAAI,eAAe;AACnB,WAAO,SAAS;AACd,UAAI;AACF,cAAM,EAAE,QAAQ,MAAAD,MAAK,IAAI,MAAM;AAAA,UAC7B,GAAG,UAAU;AAAA,UACb;AAAA,UACA,EAAE,QAAQ,UAAU,OAAO;AAAA,UAC3B,WAAW;AAAA,QACb;AACA,YAAI,WAAW,OAAO,WAAW,OAAO,WAAW,KAAK;AAEtD,gBAAM,IAAI;AAAA,YACR,mBAAmB,MAAM,MAAM,KAAK,UAAUA,KAAI,EAAE,MAAM,GAAG,GAAG,CAAC;AAAA,UACnE;AAAA,QACF;AACA,YAAI,UAAU,KAAK;AACjB,gBAAM,OAAO;AAAA,YACX,IAAI,MAAM,iBAAiB,MAAM,GAAG;AAAA,YACpC,EAAE,WAAW,KAAK;AAAA,UACpB;AAAA,QACF;AACA,uBAAe;AACf,cAAM,MAAOA,MAAqC;AAClD,YAAI,KAAK;AACP,gBAAM,OAAO,GAAG;AAAA,QAClB;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,QAAS;AACd,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,YAAI,MAAM,SAAS,aAAc;AACjC,oBAAY,KAAK;AACjB,cAAM,YACH,MAAkC,aACnC,CAAC,iBAAiB,KAAK,MAAM,OAAO;AACtC,YAAI,CAAC,WAAW;AACd,kBAAQ;AAAA,YACN;AAAA,UACF;AACA,oBAAU;AACV;AAAA,QACF;AACA,cAAM,QACJ,iBAAiB,KAAK,IAAI,cAAc,iBAAiB,SAAS,CAAC,CAAC;AACtE,wBAAgB;AAChB,cAAM,MAAM,KAAK;AAAA,MACnB;AAAA,IACF;AACA,YAAQ,IAAI,wBAAwB;AAAA,EACtC;AAEA,OAAK,KAAK;AAEV,SAAO;AAAA,IACL,OAAO;AACL,gBAAU;AACV,iBAAW,MAAM;AACjB,oBAAc;AAAA,IAChB;AAAA,IACA,IAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AGxOA,IAAM,kBAAkB;AAEjB,SAAS,wBAAwB,MAOjB;AACrB,QAAM,cAAc,KAAK,YAAY,QAAQ,OAAO,EAAE,KAAK;AAC3D,QAAM,QAAQ,KAAK,kBAAkB,KAAK,QAAQ,SAAS;AAE3D,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,KAAK;AAAA,IACb,eAAe,KAAK;AAAA,IACpB,YAAY,KAAK;AAAA,IACjB,gBAAgB,KAAK;AAAA,IACrB;AAAA,IACA,SAAS,KAAK;AAAA,IACd;AAAA,IACA,WAAW;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEO,SAAS,uBAAuB,QAOrC;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,kBAA0B;AACxC,SAAO;AACT;;;ACvCA,SAAS,KAAK,MAAe,SAAS,KAAe;AACnD,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AAEA,SAAS,aAAa,SAAkB,QAAgC;AACtE,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,QAAQ,QAAQ,IAAI,eAAe,KAAK;AACvD,QAAM,SAAS,OAAO,YAAY,EAAE,WAAW,SAAS,IACpD,OAAO,MAAM,CAAC,EAAE,KAAK,IACrB;AACJ,QAAM,MACJ,QAAQ,QAAQ,IAAI,iBAAiB,GAAG,KAAK,KAC7C,QAAQ,QAAQ,IAAI,oBAAoB,GAAG,KAAK,KAChD;AACF,SAAO,WAAW,UAAU,QAAQ;AACtC;AAMO,SAAS,0BACd,UACA,UAA+B,CAAC,GACgB;AAChD,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AACA,QAAM,eACH,QAAQ,eAAe,kBAAkB,QAAQ,OAAO,EAAE,KAC3D;AACF,QAAM,SACJ,QAAQ,QAAQ,KAAK,EAAE,YAAY,KACnC,QAAQ,IAAI,cAAc,KAAK,EAAE,YAAY,KAC7C;AACF,QAAM,aACJ,QAAQ,YAAY,KAAK,KACzB,QAAQ,IAAI,aAAa,KAAK,KAC9B;AACF,QAAM,UAAU,cAAc;AAE9B,QAAM,YAAY,MAAM;AACtB,UAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,WAAO,wBAAwB;AAAA,MAC7B;AAAA,MACA,eAAe,QAAQ,iBAAiB;AAAA,MACxC;AAAA,MACA,gBAAgB,QAAQ,MAAM;AAAA,MAC9B;AAAA,MACA,SAAS,SAAS,IAAI,CAAC,OAAO;AAAA,QAC5B,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,OAAO,EAAE,QAAQ;AAAA,MACnB,EAAE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,SAAO,OAAO,YAA+C;AAC3D,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAM,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE,KAAK;AAEpD,QAAI,QAAQ,WAAW,OAAO;AAC5B,UAAI,aAAa,iBAAiB;AAChC,cAAM,SAAS,UAAU;AACzB,eAAO,KAAK;AAAA,UACV,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,OAAO,OAAO;AAAA,UACd,QAAQ,OAAO;AAAA,UACf,SAAS,OAAO;AAAA,UAChB,SAAS,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,QAC3C,CAAC;AAAA,MACH;AACA,UAAI,aAAa,iBAAiB;AAChC,cAAM,SAAS,UAAU;AACzB,eAAO,KAAK,QAAQ,OAAO,QAAQ,MAAM,GAAG;AAAA,MAC9C;AACA,UAAI,aAAa,8BAA8B;AAC7C,eAAO,KAAK,uBAAuB,UAAU,CAAC,CAAC;AAAA,MACjD;AACA,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,WAAW,UAAU,SAAS,WAAW,GAAG,WAAW,GAAG,GAAG;AACvE,YAAM,aAAa,SAAS,MAAM,YAAY,SAAS,CAAC;AACxD,YAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK;AAC/D,UAAI,CAAC,SAAS;AACZ,eAAO,KAAK,EAAE,OAAO,kBAAkB,UAAU,GAAG,GAAG,GAAG;AAAA,MAC5D;AACA,YAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,UAAI,CAAC,aAAa,SAAS,MAAM,GAAG;AAClC,eAAO;AAAA,UACL;AAAA,YACE,OACE;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AACN,eAAO,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AAAA,MACzD;AACA,YAAM,YAAY,cAAc,MAAM,QAAQ,KAAK;AACnD,UAAI,CAAC,UAAU,IAAI;AACjB,eAAO,KAAK,EAAE,OAAO,UAAU,MAAM,GAAG,GAAG;AAAA,MAC7C;AACA,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ,QAAQ,UAAU,OAAO,OAAO;AAC7D,eAAO,KAAK,MAAM;AAAA,MACpB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,gBAAQ,MAAM,mBAAmB,QAAQ,IAAI,YAAY,GAAG;AAC5D,eAAO,KAAK,EAAE,OAAO,QAAQ,GAAG,GAAG;AAAA,MACrC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAGO,SAAS,mBACd,UACA,UAA+B,CAAC,GACb;AACnB,QAAM,QAAQ,0BAA0B,UAAU,OAAO;AACzD,SAAO,OAAO,YACX,MAAM,MAAM,OAAO,KAAM,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;AAC9D;","names":["json","os"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/define.ts","../src/worker.ts","../src/gateway-auth.ts","../src/validate.ts","../src/status.ts","../src/handler.ts"],"sourcesContent":["// @clawcash/forge — merchant SDK for the ClawCash gateway.\n//\n// Default integration: forgeWorker() — outbound-only fulfillment, no inbound\n// routes, no framework coupling. createForgeHandler() is the inbound\n// alternative for gateway-push deployments. Payment (x402) lives entirely on\n// the gateway; this package has zero runtime dependencies.\n\nexport { defineAgentService, waitUntil, createContext, toKebabCase } from \"./define.js\";\nexport { forgeWorker } from \"./worker.js\";\nexport type { ForgeWorkerOptions, ForgeWorkerHandle } from \"./worker.js\";\nexport { createForgeHandler, createForgeRequestMatcher } from \"./handler.js\";\nexport type { ForgeHandlerOptions, ForgeFetchHandler } from \"./handler.js\";\nexport { validateInput, checkType } from \"./validate.js\";\nexport type { ValidationResult } from \"./validate.js\";\nexport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n forgeSdkVersion,\n} from \"./status.js\";\nexport type { ForgeSurfaceStatus } from \"./status.js\";\nexport {\n getForgeApiKey,\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} 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 os from \"node:os\";\nimport { createContext, toKebabCase } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Outbound worker mode — the default v2 integration.\n *\n * The worker makes only outbound HTTP calls to the ClawCash gateway:\n * it long-polls POST /jobs/claim for jobs agents have already paid for,\n * runs the matching service's execute(), and posts the result back.\n * No inbound routes, no public URL, no entry-point changes.\n *\n * Polling doubles as the merchant heartbeat — the gateway treats a recent\n * claim poll as \"worker online\".\n */\n\nconst DEFAULT_GATEWAY_URL = \"https://gateway.clawca.sh\";\nconst DEFAULT_WAIT_MS = 20_000;\nconst BACKOFF_STEPS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];\n\nexport interface ForgeWorkerOptions {\n services: DefinedAgentService[];\n /** Merchant handle. Defaults to FORGE_HANDLE env. */\n handle?: string;\n /** Per-project key from the Forge dashboard. Defaults to FORGE_API_KEY env. */\n apiKey?: string;\n /** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */\n gatewayUrl?: string;\n /** Shown in gateway job rows. Defaults to hostname-pid. */\n workerId?: string;\n /** Long-poll window per claim request. Defaults to 20s (gateway caps at 25s). */\n waitMs?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface ForgeWorkerHandle {\n stop: () => void;\n readonly running: boolean;\n}\n\ninterface ClaimedJob {\n id: string;\n action: string;\n input: Record<string, unknown>;\n leaseMs: number;\n attempt: number;\n}\n\nfunction findService(\n services: DefinedAgentService[],\n action: string,\n): DefinedAgentService | null {\n const key = toKebabCase(action.trim());\n return (\n services.find((s) => s.path === key || toKebabCase(s.name) === key) ?? null\n );\n}\n\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n signal: AbortSignal,\n): Promise<{ status: number; json: Record<string, unknown> }> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n const text = await res.text();\n let json: Record<string, unknown> = {};\n try {\n json = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n json = { raw: text };\n }\n return { status: res.status, json };\n}\n\nexport function forgeWorker(options: ForgeWorkerOptions): ForgeWorkerHandle {\n const handle = (options.handle ?? process.env.FORGE_HANDLE ?? \"\")\n .trim()\n .toLowerCase();\n if (!handle) {\n throw new Error(\n \"forgeWorker: handle is required (option or FORGE_HANDLE env)\",\n );\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!apiKey) {\n throw new Error(\n \"forgeWorker: FORGE_API_KEY is required (per-project key from the Forge dashboard, min 16 chars)\",\n );\n }\n if (!options.services?.length) {\n throw new Error(\"forgeWorker: services must be a non-empty array\");\n }\n\n const gatewayUrl = (\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n DEFAULT_GATEWAY_URL\n ).replace(/\\/$/, \"\");\n const workerId =\n options.workerId?.trim() || `${os.hostname()}-${process.pid}`;\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n\n let running = true;\n const controller = new AbortController();\n let wakeSleeper: (() => void) | null = null;\n\n const reportError = (err: Error) => {\n if (options.onError) options.onError(err);\n else console.warn(`[forge-worker] ${err.message}`);\n };\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n wakeSleeper = null;\n resolve();\n }, ms);\n wakeSleeper = () => {\n clearTimeout(timer);\n wakeSleeper = null;\n resolve();\n };\n });\n\n async function runJob(job: ClaimedJob): Promise<void> {\n const resultUrl = `${gatewayUrl}/jobs/${encodeURIComponent(job.id)}/result`;\n const service = findService(options.services, job.action);\n if (!service) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: `no service registered for action ${job.action}` },\n controller.signal,\n );\n reportError(new Error(`job ${job.id}: unknown action ${job.action}`));\n return;\n }\n\n try {\n const validated = validateInput(job.input, service.input);\n if (!validated.ok) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: validated.error },\n controller.signal,\n );\n return;\n }\n const output = await service.execute(validated.value, createContext());\n const res = await postJson(\n resultUrl,\n apiKey!,\n { ok: true, output },\n controller.signal,\n );\n if (res.status >= 400) {\n reportError(\n new Error(\n `job ${job.id}: result rejected (${res.status}): ${JSON.stringify(res.json).slice(0, 300)}`,\n ),\n );\n } else {\n console.log(`[forge-worker] completed job ${job.id} (${job.action})`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"execute failed\";\n try {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: message },\n controller.signal,\n );\n } catch {\n // gateway unreachable — lease expiry will re-queue the job\n }\n reportError(new Error(`job ${job.id} failed: ${message}`));\n }\n }\n\n async function loop(): Promise<void> {\n console.log(\n `[forge-worker] ${handle} polling ${gatewayUrl}/jobs/claim as ${workerId} (${options.services.length} action(s))`,\n );\n let backoffIndex = 0;\n while (running) {\n try {\n const { status, json } = await postJson(\n `${gatewayUrl}/jobs/claim`,\n apiKey!,\n { handle, workerId, waitMs },\n controller.signal,\n );\n // 401 = wrong key → fatal (operator must fix env).\n // 404/409 = not registered yet / wrong fulfillment mode → keep\n // polling. Deploy-before-Go-live is the happy path; the worker must\n // stay up until Forge registers the handle.\n if (status === 401) {\n throw new Error(\n `claim rejected (401): ${JSON.stringify(json).slice(0, 300)} — check FORGE_API_KEY matches the Forge dashboard key`,\n );\n }\n if (status === 404 || status === 409) {\n const why =\n status === 404\n ? `handle \"${handle}\" is not registered yet — press Go live on the Forge dashboard`\n : `merchant is not in worker mode yet — press Go live again`;\n console.warn(\n `[forge-worker] waiting: ${why} (${JSON.stringify(json).slice(0, 160)})`,\n );\n throw Object.assign(new Error(`claim waiting (${status})`), {\n transient: true,\n });\n }\n if (status >= 400) {\n throw Object.assign(\n new Error(`claim failed (${status})`),\n { transient: true },\n );\n }\n backoffIndex = 0;\n const job = (json as { job?: ClaimedJob | null }).job;\n if (job) {\n await runJob(job);\n }\n } catch (err) {\n if (!running) break;\n const error = err instanceof Error ? err : new Error(String(err));\n if (error.name === \"AbortError\") break;\n reportError(error);\n const transient =\n (error as { transient?: boolean }).transient === true ||\n !/claim rejected \\(401\\)/.test(error.message);\n if (!transient) {\n console.error(\n \"[forge-worker] fatal configuration error — stopping worker\",\n );\n running = false;\n break;\n }\n const delay =\n BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)];\n backoffIndex += 1;\n await sleep(delay);\n }\n }\n console.log(\"[forge-worker] stopped\");\n }\n\n void loop();\n\n return {\n stop() {\n running = false;\n controller.abort();\n wakeSleeper?.();\n },\n get running() {\n return running;\n },\n };\n}\n","/**\n * Per-project Forge API key helpers — framework-free.\n *\n * Merchants set FORGE_API_KEY from the Forge dashboard. The worker uses it\n * to claim jobs from the gateway; the fetch handler uses it to gate\n * gateway-push fulfillment; execute() can use forgeGatewayLoopbackHeaders()\n * so the merchant's own API middleware can recognize fulfillment traffic.\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/** Synthetic user id for DB rows created by fulfillment traffic. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\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 { InputSchema, SchemaFieldType } from \"./types.js\";\n\n/**\n * Framework-free input validation shared by every execution surface\n * (Express mount, fetch handler, worker).\n */\n\nexport type ValidationResult =\n | { ok: true; value: Record<string, unknown> }\n | { ok: false; error: string };\n\nexport function validateInput(\n body: unknown,\n schema: InputSchema,\n): ValidationResult {\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\nexport function checkType(\n key: string,\n value: unknown,\n type: SchemaFieldType,\n): 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live\n * checks when running the fetch handler. Worker-mode merchants report\n * liveness by polling the gateway instead.\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 fulfillment is accepted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Key + actions present — merchant can accept gateway fulfillment. */\n ready: boolean;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.2.0\";\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const ready = opts.fulfillMounted && 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 actions: opts.actions,\n ready,\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\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 { createContext } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n} from \"./status.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Web-standard fetch handler — mount Forge fulfillment + liveliness on any\n * runtime that speaks (Request) => Response: Next.js route handlers, Hono,\n * Fastify (via adapter), Bun, Deno, plain node:http (via adapter).\n *\n * This surface is for gateway-push (fulfillment.mode=sdk) merchants and\n * deploy probes. Payment lives on the gateway; prefer forgeWorker() when the\n * merchant can run an outbound worker instead.\n *\n * Routes handled (anything else returns null from tryHandleForgeRequest, or\n * 404 from the plain handler):\n * - GET /forge/health\n * - GET /forge/status\n * - GET /.well-known/clawcash.json\n * - POST {fulfillPath}/{action-path} (requires FORGE_API_KEY auth)\n */\n\nexport interface ForgeHandlerOptions {\n /** Merchant handle — FORGE_HANDLE env when omitted. */\n handle?: string;\n /** Per-project key. FORGE_API_KEY env when omitted. */\n apiKey?: string;\n /** Defaults to /forge/fulfill. */\n fulfillPath?: string;\n /** Reported in /forge/status. Optional. */\n publicBaseUrl?: string;\n /** Reported in /forge/status. Defaults to GATEWAY_URL env or the ClawCash gateway. */\n gatewayUrl?: string;\n}\n\nexport type ForgeFetchHandler = (request: Request) => Promise<Response>;\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nfunction isAuthorized(request: Request, apiKey: string | null): boolean {\n if (!apiKey) return false;\n const header = request.headers.get(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt =\n request.headers.get(\"x-forge-api-key\")?.trim() ||\n request.headers.get(\"x-clawcash-gateway\")?.trim() ||\n \"\";\n return bearer === apiKey || alt === apiKey;\n}\n\n/**\n * Returns a Response when the request targets a Forge route, else null so\n * the host app can fall through to its own routing.\n */\nexport function createForgeRequestMatcher(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): (request: Request) => Promise<Response | null> {\n if (!services?.length) {\n throw new Error(\"createForgeHandler: services must be a non-empty array\");\n }\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const handle =\n options.handle?.trim().toLowerCase() ||\n process.env.FORGE_HANDLE?.trim().toLowerCase() ||\n null;\n const gatewayUrl =\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n \"https://gateway.clawca.sh\";\n const context = createContext();\n\n const getStatus = () => {\n const apiKey = getForgeApiKey(options.apiKey);\n return buildForgeSurfaceStatus({\n handle,\n publicBaseUrl: options.publicBaseUrl ?? null,\n gatewayUrl,\n fulfillMounted: Boolean(apiKey),\n fulfillPath,\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n };\n\n return async (request: Request): Promise<Response | null> => {\n const url = new URL(request.url);\n const pathname = url.pathname.replace(/\\/$/, \"\") || \"/\";\n\n if (request.method === \"GET\") {\n if (pathname === \"/forge/health\") {\n const status = getStatus();\n return json({\n ok: true,\n forge: true,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n }\n if (pathname === \"/forge/status\") {\n const status = getStatus();\n return json(status, status.ready ? 200 : 503);\n }\n if (pathname === \"/.well-known/clawcash.json\") {\n return json(buildWellKnownDocument(getStatus()));\n }\n return null;\n }\n\n if (request.method === \"POST\" && pathname.startsWith(`${fulfillPath}/`)) {\n const actionPath = pathname.slice(fulfillPath.length + 1);\n const service = services.find((s) => s.path === actionPath) ?? null;\n if (!service) {\n return json({ error: `Unknown action ${actionPath}` }, 404);\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!isAuthorized(request, apiKey)) {\n return json(\n {\n error:\n \"Unauthorized — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\n },\n 401,\n );\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json({ error: \"Request body must be JSON\" }, 400);\n }\n const validated = validateInput(body, service.input);\n if (!validated.ok) {\n return json({ error: validated.error }, 400);\n }\n try {\n const output = await service.execute(validated.value, context);\n return 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 return json({ error: message }, 500);\n }\n }\n\n return null;\n };\n}\n\n/** Like createForgeRequestMatcher, but 404s instead of returning null. */\nexport function createForgeHandler(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): ForgeFetchHandler {\n const match = createForgeRequestMatcher(services, options);\n return async (request) =>\n (await match(request)) ?? json({ error: \"Not found\" }, 404);\n}\n"],"mappings":"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","names":["json","os"]}
|
package/dist/index.js
CHANGED
|
@@ -234,11 +234,20 @@ function forgeWorker(options) {
|
|
|
234
234
|
{ handle, workerId, waitMs },
|
|
235
235
|
controller.signal
|
|
236
236
|
);
|
|
237
|
-
if (status === 401
|
|
237
|
+
if (status === 401) {
|
|
238
238
|
throw new Error(
|
|
239
|
-
`claim rejected (
|
|
239
|
+
`claim rejected (401): ${JSON.stringify(json2).slice(0, 300)} \u2014 check FORGE_API_KEY matches the Forge dashboard key`
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
|
+
if (status === 404 || status === 409) {
|
|
243
|
+
const why = status === 404 ? `handle "${handle}" is not registered yet \u2014 press Go live on the Forge dashboard` : `merchant is not in worker mode yet \u2014 press Go live again`;
|
|
244
|
+
console.warn(
|
|
245
|
+
`[forge-worker] waiting: ${why} (${JSON.stringify(json2).slice(0, 160)})`
|
|
246
|
+
);
|
|
247
|
+
throw Object.assign(new Error(`claim waiting (${status})`), {
|
|
248
|
+
transient: true
|
|
249
|
+
});
|
|
250
|
+
}
|
|
242
251
|
if (status >= 400) {
|
|
243
252
|
throw Object.assign(
|
|
244
253
|
new Error(`claim failed (${status})`),
|
|
@@ -255,7 +264,7 @@ function forgeWorker(options) {
|
|
|
255
264
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
256
265
|
if (error.name === "AbortError") break;
|
|
257
266
|
reportError(error);
|
|
258
|
-
const transient = error.transient || !/claim rejected/.test(error.message);
|
|
267
|
+
const transient = error.transient === true || !/claim rejected \(401\)/.test(error.message);
|
|
259
268
|
if (!transient) {
|
|
260
269
|
console.error(
|
|
261
270
|
"[forge-worker] fatal configuration error \u2014 stopping worker"
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/define.ts","../src/worker.ts","../src/gateway-auth.ts","../src/validate.ts","../src/status.ts","../src/handler.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 os from \"node:os\";\nimport { createContext, toKebabCase } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Outbound worker mode — the default v2 integration.\n *\n * The worker makes only outbound HTTP calls to the ClawCash gateway:\n * it long-polls POST /jobs/claim for jobs agents have already paid for,\n * runs the matching service's execute(), and posts the result back.\n * No inbound routes, no public URL, no entry-point changes.\n *\n * Polling doubles as the merchant heartbeat — the gateway treats a recent\n * claim poll as \"worker online\".\n */\n\nconst DEFAULT_GATEWAY_URL = \"https://gateway.clawca.sh\";\nconst DEFAULT_WAIT_MS = 20_000;\nconst BACKOFF_STEPS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];\n\nexport interface ForgeWorkerOptions {\n services: DefinedAgentService[];\n /** Merchant handle. Defaults to FORGE_HANDLE env. */\n handle?: string;\n /** Per-project key from the Forge dashboard. Defaults to FORGE_API_KEY env. */\n apiKey?: string;\n /** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */\n gatewayUrl?: string;\n /** Shown in gateway job rows. Defaults to hostname-pid. */\n workerId?: string;\n /** Long-poll window per claim request. Defaults to 20s (gateway caps at 25s). */\n waitMs?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface ForgeWorkerHandle {\n stop: () => void;\n readonly running: boolean;\n}\n\ninterface ClaimedJob {\n id: string;\n action: string;\n input: Record<string, unknown>;\n leaseMs: number;\n attempt: number;\n}\n\nfunction findService(\n services: DefinedAgentService[],\n action: string,\n): DefinedAgentService | null {\n const key = toKebabCase(action.trim());\n return (\n services.find((s) => s.path === key || toKebabCase(s.name) === key) ?? null\n );\n}\n\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n signal: AbortSignal,\n): Promise<{ status: number; json: Record<string, unknown> }> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n const text = await res.text();\n let json: Record<string, unknown> = {};\n try {\n json = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n json = { raw: text };\n }\n return { status: res.status, json };\n}\n\nexport function forgeWorker(options: ForgeWorkerOptions): ForgeWorkerHandle {\n const handle = (options.handle ?? process.env.FORGE_HANDLE ?? \"\")\n .trim()\n .toLowerCase();\n if (!handle) {\n throw new Error(\n \"forgeWorker: handle is required (option or FORGE_HANDLE env)\",\n );\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!apiKey) {\n throw new Error(\n \"forgeWorker: FORGE_API_KEY is required (per-project key from the Forge dashboard, min 16 chars)\",\n );\n }\n if (!options.services?.length) {\n throw new Error(\"forgeWorker: services must be a non-empty array\");\n }\n\n const gatewayUrl = (\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n DEFAULT_GATEWAY_URL\n ).replace(/\\/$/, \"\");\n const workerId =\n options.workerId?.trim() || `${os.hostname()}-${process.pid}`;\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n\n let running = true;\n const controller = new AbortController();\n let wakeSleeper: (() => void) | null = null;\n\n const reportError = (err: Error) => {\n if (options.onError) options.onError(err);\n else console.warn(`[forge-worker] ${err.message}`);\n };\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n wakeSleeper = null;\n resolve();\n }, ms);\n wakeSleeper = () => {\n clearTimeout(timer);\n wakeSleeper = null;\n resolve();\n };\n });\n\n async function runJob(job: ClaimedJob): Promise<void> {\n const resultUrl = `${gatewayUrl}/jobs/${encodeURIComponent(job.id)}/result`;\n const service = findService(options.services, job.action);\n if (!service) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: `no service registered for action ${job.action}` },\n controller.signal,\n );\n reportError(new Error(`job ${job.id}: unknown action ${job.action}`));\n return;\n }\n\n try {\n const validated = validateInput(job.input, service.input);\n if (!validated.ok) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: validated.error },\n controller.signal,\n );\n return;\n }\n const output = await service.execute(validated.value, createContext());\n const res = await postJson(\n resultUrl,\n apiKey!,\n { ok: true, output },\n controller.signal,\n );\n if (res.status >= 400) {\n reportError(\n new Error(\n `job ${job.id}: result rejected (${res.status}): ${JSON.stringify(res.json).slice(0, 300)}`,\n ),\n );\n } else {\n console.log(`[forge-worker] completed job ${job.id} (${job.action})`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"execute failed\";\n try {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: message },\n controller.signal,\n );\n } catch {\n // gateway unreachable — lease expiry will re-queue the job\n }\n reportError(new Error(`job ${job.id} failed: ${message}`));\n }\n }\n\n async function loop(): Promise<void> {\n console.log(\n `[forge-worker] ${handle} polling ${gatewayUrl}/jobs/claim as ${workerId} (${options.services.length} action(s))`,\n );\n let backoffIndex = 0;\n while (running) {\n try {\n const { status, json } = await postJson(\n `${gatewayUrl}/jobs/claim`,\n apiKey!,\n { handle, workerId, waitMs },\n controller.signal,\n );\n if (status === 401 || status === 404 || status === 409) {\n // Not registered / wrong key / wrong mode — fatal config, not transient.\n throw new Error(\n `claim rejected (${status}): ${JSON.stringify(json).slice(0, 300)} — check FORGE_API_KEY, handle, and that the project is registered with fulfillment.mode=worker`,\n );\n }\n if (status >= 400) {\n throw Object.assign(\n new Error(`claim failed (${status})`),\n { transient: true },\n );\n }\n backoffIndex = 0;\n const job = (json as { job?: ClaimedJob | null }).job;\n if (job) {\n await runJob(job);\n }\n } catch (err) {\n if (!running) break;\n const error = err instanceof Error ? err : new Error(String(err));\n if (error.name === \"AbortError\") break;\n reportError(error);\n const transient =\n (error as { transient?: boolean }).transient ||\n !/claim rejected/.test(error.message);\n if (!transient) {\n console.error(\n \"[forge-worker] fatal configuration error — stopping worker\",\n );\n running = false;\n break;\n }\n const delay =\n BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)];\n backoffIndex += 1;\n await sleep(delay);\n }\n }\n console.log(\"[forge-worker] stopped\");\n }\n\n void loop();\n\n return {\n stop() {\n running = false;\n controller.abort();\n wakeSleeper?.();\n },\n get running() {\n return running;\n },\n };\n}\n","/**\n * Per-project Forge API key helpers — framework-free.\n *\n * Merchants set FORGE_API_KEY from the Forge dashboard. The worker uses it\n * to claim jobs from the gateway; the fetch handler uses it to gate\n * gateway-push fulfillment; execute() can use forgeGatewayLoopbackHeaders()\n * so the merchant's own API middleware can recognize fulfillment traffic.\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/** Synthetic user id for DB rows created by fulfillment traffic. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\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 { InputSchema, SchemaFieldType } from \"./types.js\";\n\n/**\n * Framework-free input validation shared by every execution surface\n * (Express mount, fetch handler, worker).\n */\n\nexport type ValidationResult =\n | { ok: true; value: Record<string, unknown> }\n | { ok: false; error: string };\n\nexport function validateInput(\n body: unknown,\n schema: InputSchema,\n): ValidationResult {\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\nexport function checkType(\n key: string,\n value: unknown,\n type: SchemaFieldType,\n): 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live\n * checks when running the fetch handler. Worker-mode merchants report\n * liveness by polling the gateway instead.\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 fulfillment is accepted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Key + actions present — merchant can accept gateway fulfillment. */\n ready: boolean;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.2.0\";\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const ready = opts.fulfillMounted && 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 actions: opts.actions,\n ready,\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\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 { createContext } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n} from \"./status.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Web-standard fetch handler — mount Forge fulfillment + liveliness on any\n * runtime that speaks (Request) => Response: Next.js route handlers, Hono,\n * Fastify (via adapter), Bun, Deno, plain node:http (via adapter).\n *\n * This surface is for gateway-push (fulfillment.mode=sdk) merchants and\n * deploy probes. Payment lives on the gateway; prefer forgeWorker() when the\n * merchant can run an outbound worker instead.\n *\n * Routes handled (anything else returns null from tryHandleForgeRequest, or\n * 404 from the plain handler):\n * - GET /forge/health\n * - GET /forge/status\n * - GET /.well-known/clawcash.json\n * - POST {fulfillPath}/{action-path} (requires FORGE_API_KEY auth)\n */\n\nexport interface ForgeHandlerOptions {\n /** Merchant handle — FORGE_HANDLE env when omitted. */\n handle?: string;\n /** Per-project key. FORGE_API_KEY env when omitted. */\n apiKey?: string;\n /** Defaults to /forge/fulfill. */\n fulfillPath?: string;\n /** Reported in /forge/status. Optional. */\n publicBaseUrl?: string;\n /** Reported in /forge/status. Defaults to GATEWAY_URL env or the ClawCash gateway. */\n gatewayUrl?: string;\n}\n\nexport type ForgeFetchHandler = (request: Request) => Promise<Response>;\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nfunction isAuthorized(request: Request, apiKey: string | null): boolean {\n if (!apiKey) return false;\n const header = request.headers.get(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt =\n request.headers.get(\"x-forge-api-key\")?.trim() ||\n request.headers.get(\"x-clawcash-gateway\")?.trim() ||\n \"\";\n return bearer === apiKey || alt === apiKey;\n}\n\n/**\n * Returns a Response when the request targets a Forge route, else null so\n * the host app can fall through to its own routing.\n */\nexport function createForgeRequestMatcher(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): (request: Request) => Promise<Response | null> {\n if (!services?.length) {\n throw new Error(\"createForgeHandler: services must be a non-empty array\");\n }\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const handle =\n options.handle?.trim().toLowerCase() ||\n process.env.FORGE_HANDLE?.trim().toLowerCase() ||\n null;\n const gatewayUrl =\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n \"https://gateway.clawca.sh\";\n const context = createContext();\n\n const getStatus = () => {\n const apiKey = getForgeApiKey(options.apiKey);\n return buildForgeSurfaceStatus({\n handle,\n publicBaseUrl: options.publicBaseUrl ?? null,\n gatewayUrl,\n fulfillMounted: Boolean(apiKey),\n fulfillPath,\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n };\n\n return async (request: Request): Promise<Response | null> => {\n const url = new URL(request.url);\n const pathname = url.pathname.replace(/\\/$/, \"\") || \"/\";\n\n if (request.method === \"GET\") {\n if (pathname === \"/forge/health\") {\n const status = getStatus();\n return json({\n ok: true,\n forge: true,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n }\n if (pathname === \"/forge/status\") {\n const status = getStatus();\n return json(status, status.ready ? 200 : 503);\n }\n if (pathname === \"/.well-known/clawcash.json\") {\n return json(buildWellKnownDocument(getStatus()));\n }\n return null;\n }\n\n if (request.method === \"POST\" && pathname.startsWith(`${fulfillPath}/`)) {\n const actionPath = pathname.slice(fulfillPath.length + 1);\n const service = services.find((s) => s.path === actionPath) ?? null;\n if (!service) {\n return json({ error: `Unknown action ${actionPath}` }, 404);\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!isAuthorized(request, apiKey)) {\n return json(\n {\n error:\n \"Unauthorized — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\n },\n 401,\n );\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json({ error: \"Request body must be JSON\" }, 400);\n }\n const validated = validateInput(body, service.input);\n if (!validated.ok) {\n return json({ error: validated.error }, 400);\n }\n try {\n const output = await service.execute(validated.value, context);\n return 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 return json({ error: message }, 500);\n }\n }\n\n return null;\n };\n}\n\n/** Like createForgeRequestMatcher, but 404s instead of returning null. */\nexport function createForgeHandler(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): ForgeFetchHandler {\n const match = createForgeRequestMatcher(services, options);\n return async (request) =>\n (await match(request)) ?? json({ error: \"Not found\" }, 404);\n}\n"],"mappings":";AAOA,eAAsB,UAAa,SAA0C;AAC3E,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,KAAK,IAAI;AAEzB,aAAS;AACP,UAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,QAAI,QAAQ,cAAc,KAAK,GAAG;AAChC,aAAO;AAAA,IACT;AACA,QAAI,KAAK,IAAI,IAAI,WAAW,WAAW;AACrC,YAAM,IAAI,MAAM,6BAA6B,SAAS,IAAI;AAAA,IAC5D;AACA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,EAChE;AACF;AAEO,SAAS,gBAAqC;AACnD,SAAO;AAAA,IACL;AAAA,EACF;AACF;AAGO,SAAS,YAAY,MAAsB;AAChD,SAAO,KACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,MAAM,GAAG,EACjB,YAAY;AACjB;AAEO,SAAS,mBAId,QACsC;AACtC,MAAI,CAAC,OAAO,MAAM,KAAK,GAAG;AACxB,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS;AAAA,IACT,MAAM,YAAY,OAAO,IAAI;AAAA,EAC/B;AACF;;;ACxDA,OAAO,QAAQ;;;ACUR,SAAS,eAAe,UAAyC;AACtE,QAAM,SACH,YAAY,QAAQ,IAAI,iBAAiB,QAAQ,IAAI,0BAA0B,KAAK;AACvF,MAAI,CAAC,SAAS,MAAM,SAAS,GAAI,QAAO;AACxC,SAAO;AACT;AAGO,IAAM,wBAAwB;AAG9B,SAAS,4BACd,QACwB;AACxB,QAAM,WAAW,eAAe,MAAM;AACtC,MAAI,CAAC,SAAU,QAAO,CAAC;AACvB,SAAO;AAAA,IACL,eAAe,UAAU,QAAQ;AAAA,IACjC,mBAAmB;AAAA,EACrB;AACF;;;ACnBO,SAAS,cACd,MACA,QACkB;AAClB,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AAC5D,WAAO,EAAE,IAAI,OAAO,OAAO,qCAAqC;AAAA,EAClE;AAEA,QAAM,QAAQ;AACd,QAAM,QAAiC,CAAC;AAExC,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AAChD,QAAI,EAAE,OAAO,UAAU,MAAM,GAAG,MAAM,UAAa,MAAM,GAAG,MAAM,MAAM;AACtE,aAAO,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,GAAG;AAAA,IAC9D;AACA,UAAM,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,IAAI;AAC3C,QAAI,IAAK,QAAO,EAAE,IAAI,OAAO,OAAO,IAAI;AACxC,UAAM,GAAG,IAAI,MAAM,GAAG;AAAA,EACxB;AAEA,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAEO,SAAS,UACd,KACA,OACA,MACe;AACf,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,UAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,UAAI,SAAS,OAAO;AAClB,YAAI;AACF,cAAI,IAAI,KAAK;AAAA,QACf,QAAQ;AACN,iBAAO,SAAS,GAAG;AAAA,QACrB;AAAA,MACF;AACA,aAAO;AAAA,IACT,KAAK;AACH,UAAI,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,GAAG;AACpD,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,aAAO;AAAA,IACT,KAAK;AACH,UAAI,OAAO,UAAU,WAAW;AAC9B,eAAO,SAAS,GAAG;AAAA,MACrB;AACA,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;AFhDA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AACxB,IAAM,mBAAmB,CAAC,KAAO,KAAO,KAAO,KAAQ,GAAM;AA8B7D,SAAS,YACP,UACA,QAC4B;AAC5B,QAAM,MAAM,YAAY,OAAO,KAAK,CAAC;AACrC,SACE,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,YAAY,EAAE,IAAI,MAAM,GAAG,KAAK;AAE3E;AAEA,eAAe,SACb,KACA,QACA,MACA,QAC4D;AAC5D,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,eAAe,UAAU,MAAM;AAAA,IACjC;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAIA,QAAgC,CAAC;AACrC,MAAI;AACF,IAAAA,QAAO,OAAQ,KAAK,MAAM,IAAI,IAAgC,CAAC;AAAA,EACjE,QAAQ;AACN,IAAAA,QAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AACA,SAAO,EAAE,QAAQ,IAAI,QAAQ,MAAAA,MAAK;AACpC;AAEO,SAAS,YAAY,SAAgD;AAC1E,QAAM,UAAU,QAAQ,UAAU,QAAQ,IAAI,gBAAgB,IAC3D,KAAK,EACL,YAAY;AACf,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,UAAU,QAAQ;AAC7B,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,QAAM,cACJ,QAAQ,YAAY,KAAK,KACzB,QAAQ,IAAI,aAAa,KAAK,KAC9B,qBACA,QAAQ,OAAO,EAAE;AACnB,QAAM,WACJ,QAAQ,UAAU,KAAK,KAAK,GAAG,GAAG,SAAS,CAAC,IAAI,QAAQ,GAAG;AAC7D,QAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,UAAU;AACd,QAAM,aAAa,IAAI,gBAAgB;AACvC,MAAI,cAAmC;AAEvC,QAAM,cAAc,CAAC,QAAe;AAClC,QAAI,QAAQ,QAAS,SAAQ,QAAQ,GAAG;AAAA,QACnC,SAAQ,KAAK,kBAAkB,IAAI,OAAO,EAAE;AAAA,EACnD;AAEA,QAAM,QAAQ,CAAC,OACb,IAAI,QAAc,CAAC,YAAY;AAC7B,UAAM,QAAQ,WAAW,MAAM;AAC7B,oBAAc;AACd,cAAQ;AAAA,IACV,GAAG,EAAE;AACL,kBAAc,MAAM;AAClB,mBAAa,KAAK;AAClB,oBAAc;AACd,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAEH,iBAAe,OAAO,KAAgC;AACpD,UAAM,YAAY,GAAG,UAAU,SAAS,mBAAmB,IAAI,EAAE,CAAC;AAClE,UAAM,UAAU,YAAY,QAAQ,UAAU,IAAI,MAAM;AACxD,QAAI,CAAC,SAAS;AACZ,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,EAAE,IAAI,OAAO,OAAO,oCAAoC,IAAI,MAAM,GAAG;AAAA,QACrE,WAAW;AAAA,MACb;AACA,kBAAY,IAAI,MAAM,OAAO,IAAI,EAAE,oBAAoB,IAAI,MAAM,EAAE,CAAC;AACpE;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,cAAc,IAAI,OAAO,QAAQ,KAAK;AACxD,UAAI,CAAC,UAAU,IAAI;AACjB,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,EAAE,IAAI,OAAO,OAAO,UAAU,MAAM;AAAA,UACpC,WAAW;AAAA,QACb;AACA;AAAA,MACF;AACA,YAAM,SAAS,MAAM,QAAQ,QAAQ,UAAU,OAAO,cAAc,CAAC;AACrE,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,MAAM,OAAO;AAAA,QACnB,WAAW;AAAA,MACb;AACA,UAAI,IAAI,UAAU,KAAK;AACrB;AAAA,UACE,IAAI;AAAA,YACF,OAAO,IAAI,EAAE,sBAAsB,IAAI,MAAM,MAAM,KAAK,UAAU,IAAI,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;AAAA,UAC3F;AAAA,QACF;AAAA,MACF,OAAO;AACL,gBAAQ,IAAI,gCAAgC,IAAI,EAAE,KAAK,IAAI,MAAM,GAAG;AAAA,MACtE;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,UAAI;AACF,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,EAAE,IAAI,OAAO,OAAO,QAAQ;AAAA,UAC5B,WAAW;AAAA,QACb;AAAA,MACF,QAAQ;AAAA,MAER;AACA,kBAAY,IAAI,MAAM,OAAO,IAAI,EAAE,YAAY,OAAO,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,iBAAe,OAAsB;AACnC,YAAQ;AAAA,MACN,kBAAkB,MAAM,YAAY,UAAU,kBAAkB,QAAQ,KAAK,QAAQ,SAAS,MAAM;AAAA,IACtG;AACA,QAAI,eAAe;AACnB,WAAO,SAAS;AACd,UAAI;AACF,cAAM,EAAE,QAAQ,MAAAA,MAAK,IAAI,MAAM;AAAA,UAC7B,GAAG,UAAU;AAAA,UACb;AAAA,UACA,EAAE,QAAQ,UAAU,OAAO;AAAA,UAC3B,WAAW;AAAA,QACb;AACA,YAAI,WAAW,OAAO,WAAW,OAAO,WAAW,KAAK;AAEtD,gBAAM,IAAI;AAAA,YACR,mBAAmB,MAAM,MAAM,KAAK,UAAUA,KAAI,EAAE,MAAM,GAAG,GAAG,CAAC;AAAA,UACnE;AAAA,QACF;AACA,YAAI,UAAU,KAAK;AACjB,gBAAM,OAAO;AAAA,YACX,IAAI,MAAM,iBAAiB,MAAM,GAAG;AAAA,YACpC,EAAE,WAAW,KAAK;AAAA,UACpB;AAAA,QACF;AACA,uBAAe;AACf,cAAM,MAAOA,MAAqC;AAClD,YAAI,KAAK;AACP,gBAAM,OAAO,GAAG;AAAA,QAClB;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,QAAS;AACd,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,YAAI,MAAM,SAAS,aAAc;AACjC,oBAAY,KAAK;AACjB,cAAM,YACH,MAAkC,aACnC,CAAC,iBAAiB,KAAK,MAAM,OAAO;AACtC,YAAI,CAAC,WAAW;AACd,kBAAQ;AAAA,YACN;AAAA,UACF;AACA,oBAAU;AACV;AAAA,QACF;AACA,cAAM,QACJ,iBAAiB,KAAK,IAAI,cAAc,iBAAiB,SAAS,CAAC,CAAC;AACtE,wBAAgB;AAChB,cAAM,MAAM,KAAK;AAAA,MACnB;AAAA,IACF;AACA,YAAQ,IAAI,wBAAwB;AAAA,EACtC;AAEA,OAAK,KAAK;AAEV,SAAO;AAAA,IACL,OAAO;AACL,gBAAU;AACV,iBAAW,MAAM;AACjB,oBAAc;AAAA,IAChB;AAAA,IACA,IAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AGxOA,IAAM,kBAAkB;AAEjB,SAAS,wBAAwB,MAOjB;AACrB,QAAM,cAAc,KAAK,YAAY,QAAQ,OAAO,EAAE,KAAK;AAC3D,QAAM,QAAQ,KAAK,kBAAkB,KAAK,QAAQ,SAAS;AAE3D,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,KAAK;AAAA,IACb,eAAe,KAAK;AAAA,IACpB,YAAY,KAAK;AAAA,IACjB,gBAAgB,KAAK;AAAA,IACrB;AAAA,IACA,SAAS,KAAK;AAAA,IACd;AAAA,IACA,WAAW;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEO,SAAS,uBAAuB,QAOrC;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,kBAA0B;AACxC,SAAO;AACT;;;ACvCA,SAAS,KAAK,MAAe,SAAS,KAAe;AACnD,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AAEA,SAAS,aAAa,SAAkB,QAAgC;AACtE,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,QAAQ,QAAQ,IAAI,eAAe,KAAK;AACvD,QAAM,SAAS,OAAO,YAAY,EAAE,WAAW,SAAS,IACpD,OAAO,MAAM,CAAC,EAAE,KAAK,IACrB;AACJ,QAAM,MACJ,QAAQ,QAAQ,IAAI,iBAAiB,GAAG,KAAK,KAC7C,QAAQ,QAAQ,IAAI,oBAAoB,GAAG,KAAK,KAChD;AACF,SAAO,WAAW,UAAU,QAAQ;AACtC;AAMO,SAAS,0BACd,UACA,UAA+B,CAAC,GACgB;AAChD,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AACA,QAAM,eACH,QAAQ,eAAe,kBAAkB,QAAQ,OAAO,EAAE,KAC3D;AACF,QAAM,SACJ,QAAQ,QAAQ,KAAK,EAAE,YAAY,KACnC,QAAQ,IAAI,cAAc,KAAK,EAAE,YAAY,KAC7C;AACF,QAAM,aACJ,QAAQ,YAAY,KAAK,KACzB,QAAQ,IAAI,aAAa,KAAK,KAC9B;AACF,QAAM,UAAU,cAAc;AAE9B,QAAM,YAAY,MAAM;AACtB,UAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,WAAO,wBAAwB;AAAA,MAC7B;AAAA,MACA,eAAe,QAAQ,iBAAiB;AAAA,MACxC;AAAA,MACA,gBAAgB,QAAQ,MAAM;AAAA,MAC9B;AAAA,MACA,SAAS,SAAS,IAAI,CAAC,OAAO;AAAA,QAC5B,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,OAAO,EAAE,QAAQ;AAAA,MACnB,EAAE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,SAAO,OAAO,YAA+C;AAC3D,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAM,WAAW,IAAI,SAAS,QAAQ,OAAO,EAAE,KAAK;AAEpD,QAAI,QAAQ,WAAW,OAAO;AAC5B,UAAI,aAAa,iBAAiB;AAChC,cAAM,SAAS,UAAU;AACzB,eAAO,KAAK;AAAA,UACV,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,OAAO,OAAO;AAAA,UACd,QAAQ,OAAO;AAAA,UACf,SAAS,OAAO;AAAA,UAChB,SAAS,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,QAC3C,CAAC;AAAA,MACH;AACA,UAAI,aAAa,iBAAiB;AAChC,cAAM,SAAS,UAAU;AACzB,eAAO,KAAK,QAAQ,OAAO,QAAQ,MAAM,GAAG;AAAA,MAC9C;AACA,UAAI,aAAa,8BAA8B;AAC7C,eAAO,KAAK,uBAAuB,UAAU,CAAC,CAAC;AAAA,MACjD;AACA,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,WAAW,UAAU,SAAS,WAAW,GAAG,WAAW,GAAG,GAAG;AACvE,YAAM,aAAa,SAAS,MAAM,YAAY,SAAS,CAAC;AACxD,YAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK;AAC/D,UAAI,CAAC,SAAS;AACZ,eAAO,KAAK,EAAE,OAAO,kBAAkB,UAAU,GAAG,GAAG,GAAG;AAAA,MAC5D;AACA,YAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,UAAI,CAAC,aAAa,SAAS,MAAM,GAAG;AAClC,eAAO;AAAA,UACL;AAAA,YACE,OACE;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AACN,eAAO,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AAAA,MACzD;AACA,YAAM,YAAY,cAAc,MAAM,QAAQ,KAAK;AACnD,UAAI,CAAC,UAAU,IAAI;AACjB,eAAO,KAAK,EAAE,OAAO,UAAU,MAAM,GAAG,GAAG;AAAA,MAC7C;AACA,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ,QAAQ,UAAU,OAAO,OAAO;AAC7D,eAAO,KAAK,MAAM;AAAA,MACpB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,gBAAQ,MAAM,mBAAmB,QAAQ,IAAI,YAAY,GAAG;AAC5D,eAAO,KAAK,EAAE,OAAO,QAAQ,GAAG,GAAG;AAAA,MACrC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAGO,SAAS,mBACd,UACA,UAA+B,CAAC,GACb;AACnB,QAAM,QAAQ,0BAA0B,UAAU,OAAO;AACzD,SAAO,OAAO,YACX,MAAM,MAAM,OAAO,KAAM,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;AAC9D;","names":["json"]}
|
|
1
|
+
{"version":3,"sources":["../src/define.ts","../src/worker.ts","../src/gateway-auth.ts","../src/validate.ts","../src/status.ts","../src/handler.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 os from \"node:os\";\nimport { createContext, toKebabCase } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Outbound worker mode — the default v2 integration.\n *\n * The worker makes only outbound HTTP calls to the ClawCash gateway:\n * it long-polls POST /jobs/claim for jobs agents have already paid for,\n * runs the matching service's execute(), and posts the result back.\n * No inbound routes, no public URL, no entry-point changes.\n *\n * Polling doubles as the merchant heartbeat — the gateway treats a recent\n * claim poll as \"worker online\".\n */\n\nconst DEFAULT_GATEWAY_URL = \"https://gateway.clawca.sh\";\nconst DEFAULT_WAIT_MS = 20_000;\nconst BACKOFF_STEPS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];\n\nexport interface ForgeWorkerOptions {\n services: DefinedAgentService[];\n /** Merchant handle. Defaults to FORGE_HANDLE env. */\n handle?: string;\n /** Per-project key from the Forge dashboard. Defaults to FORGE_API_KEY env. */\n apiKey?: string;\n /** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */\n gatewayUrl?: string;\n /** Shown in gateway job rows. Defaults to hostname-pid. */\n workerId?: string;\n /** Long-poll window per claim request. Defaults to 20s (gateway caps at 25s). */\n waitMs?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface ForgeWorkerHandle {\n stop: () => void;\n readonly running: boolean;\n}\n\ninterface ClaimedJob {\n id: string;\n action: string;\n input: Record<string, unknown>;\n leaseMs: number;\n attempt: number;\n}\n\nfunction findService(\n services: DefinedAgentService[],\n action: string,\n): DefinedAgentService | null {\n const key = toKebabCase(action.trim());\n return (\n services.find((s) => s.path === key || toKebabCase(s.name) === key) ?? null\n );\n}\n\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n signal: AbortSignal,\n): Promise<{ status: number; json: Record<string, unknown> }> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n const text = await res.text();\n let json: Record<string, unknown> = {};\n try {\n json = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n json = { raw: text };\n }\n return { status: res.status, json };\n}\n\nexport function forgeWorker(options: ForgeWorkerOptions): ForgeWorkerHandle {\n const handle = (options.handle ?? process.env.FORGE_HANDLE ?? \"\")\n .trim()\n .toLowerCase();\n if (!handle) {\n throw new Error(\n \"forgeWorker: handle is required (option or FORGE_HANDLE env)\",\n );\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!apiKey) {\n throw new Error(\n \"forgeWorker: FORGE_API_KEY is required (per-project key from the Forge dashboard, min 16 chars)\",\n );\n }\n if (!options.services?.length) {\n throw new Error(\"forgeWorker: services must be a non-empty array\");\n }\n\n const gatewayUrl = (\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n DEFAULT_GATEWAY_URL\n ).replace(/\\/$/, \"\");\n const workerId =\n options.workerId?.trim() || `${os.hostname()}-${process.pid}`;\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n\n let running = true;\n const controller = new AbortController();\n let wakeSleeper: (() => void) | null = null;\n\n const reportError = (err: Error) => {\n if (options.onError) options.onError(err);\n else console.warn(`[forge-worker] ${err.message}`);\n };\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n wakeSleeper = null;\n resolve();\n }, ms);\n wakeSleeper = () => {\n clearTimeout(timer);\n wakeSleeper = null;\n resolve();\n };\n });\n\n async function runJob(job: ClaimedJob): Promise<void> {\n const resultUrl = `${gatewayUrl}/jobs/${encodeURIComponent(job.id)}/result`;\n const service = findService(options.services, job.action);\n if (!service) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: `no service registered for action ${job.action}` },\n controller.signal,\n );\n reportError(new Error(`job ${job.id}: unknown action ${job.action}`));\n return;\n }\n\n try {\n const validated = validateInput(job.input, service.input);\n if (!validated.ok) {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: validated.error },\n controller.signal,\n );\n return;\n }\n const output = await service.execute(validated.value, createContext());\n const res = await postJson(\n resultUrl,\n apiKey!,\n { ok: true, output },\n controller.signal,\n );\n if (res.status >= 400) {\n reportError(\n new Error(\n `job ${job.id}: result rejected (${res.status}): ${JSON.stringify(res.json).slice(0, 300)}`,\n ),\n );\n } else {\n console.log(`[forge-worker] completed job ${job.id} (${job.action})`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"execute failed\";\n try {\n await postJson(\n resultUrl,\n apiKey!,\n { ok: false, error: message },\n controller.signal,\n );\n } catch {\n // gateway unreachable — lease expiry will re-queue the job\n }\n reportError(new Error(`job ${job.id} failed: ${message}`));\n }\n }\n\n async function loop(): Promise<void> {\n console.log(\n `[forge-worker] ${handle} polling ${gatewayUrl}/jobs/claim as ${workerId} (${options.services.length} action(s))`,\n );\n let backoffIndex = 0;\n while (running) {\n try {\n const { status, json } = await postJson(\n `${gatewayUrl}/jobs/claim`,\n apiKey!,\n { handle, workerId, waitMs },\n controller.signal,\n );\n // 401 = wrong key → fatal (operator must fix env).\n // 404/409 = not registered yet / wrong fulfillment mode → keep\n // polling. Deploy-before-Go-live is the happy path; the worker must\n // stay up until Forge registers the handle.\n if (status === 401) {\n throw new Error(\n `claim rejected (401): ${JSON.stringify(json).slice(0, 300)} — check FORGE_API_KEY matches the Forge dashboard key`,\n );\n }\n if (status === 404 || status === 409) {\n const why =\n status === 404\n ? `handle \"${handle}\" is not registered yet — press Go live on the Forge dashboard`\n : `merchant is not in worker mode yet — press Go live again`;\n console.warn(\n `[forge-worker] waiting: ${why} (${JSON.stringify(json).slice(0, 160)})`,\n );\n throw Object.assign(new Error(`claim waiting (${status})`), {\n transient: true,\n });\n }\n if (status >= 400) {\n throw Object.assign(\n new Error(`claim failed (${status})`),\n { transient: true },\n );\n }\n backoffIndex = 0;\n const job = (json as { job?: ClaimedJob | null }).job;\n if (job) {\n await runJob(job);\n }\n } catch (err) {\n if (!running) break;\n const error = err instanceof Error ? err : new Error(String(err));\n if (error.name === \"AbortError\") break;\n reportError(error);\n const transient =\n (error as { transient?: boolean }).transient === true ||\n !/claim rejected \\(401\\)/.test(error.message);\n if (!transient) {\n console.error(\n \"[forge-worker] fatal configuration error — stopping worker\",\n );\n running = false;\n break;\n }\n const delay =\n BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)];\n backoffIndex += 1;\n await sleep(delay);\n }\n }\n console.log(\"[forge-worker] stopped\");\n }\n\n void loop();\n\n return {\n stop() {\n running = false;\n controller.abort();\n wakeSleeper?.();\n },\n get running() {\n return running;\n },\n };\n}\n","/**\n * Per-project Forge API key helpers — framework-free.\n *\n * Merchants set FORGE_API_KEY from the Forge dashboard. The worker uses it\n * to claim jobs from the gateway; the fetch handler uses it to gate\n * gateway-push fulfillment; execute() can use forgeGatewayLoopbackHeaders()\n * so the merchant's own API middleware can recognize fulfillment traffic.\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/** Synthetic user id for DB rows created by fulfillment traffic. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\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 { InputSchema, SchemaFieldType } from \"./types.js\";\n\n/**\n * Framework-free input validation shared by every execution surface\n * (Express mount, fetch handler, worker).\n */\n\nexport type ValidationResult =\n | { ok: true; value: Record<string, unknown> }\n | { ok: false; error: string };\n\nexport function validateInput(\n body: unknown,\n schema: InputSchema,\n): ValidationResult {\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\nexport function checkType(\n key: string,\n value: unknown,\n type: SchemaFieldType,\n): 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","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live\n * checks when running the fetch handler. Worker-mode merchants report\n * liveness by polling the gateway instead.\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 fulfillment is accepted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Key + actions present — merchant can accept gateway fulfillment. */\n ready: boolean;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.2.0\";\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const ready = opts.fulfillMounted && 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 actions: opts.actions,\n ready,\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\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 { createContext } from \"./define.js\";\nimport { getForgeApiKey } from \"./gateway-auth.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n} from \"./status.js\";\nimport type { DefinedAgentService } from \"./types.js\";\nimport { validateInput } from \"./validate.js\";\n\n/**\n * Web-standard fetch handler — mount Forge fulfillment + liveliness on any\n * runtime that speaks (Request) => Response: Next.js route handlers, Hono,\n * Fastify (via adapter), Bun, Deno, plain node:http (via adapter).\n *\n * This surface is for gateway-push (fulfillment.mode=sdk) merchants and\n * deploy probes. Payment lives on the gateway; prefer forgeWorker() when the\n * merchant can run an outbound worker instead.\n *\n * Routes handled (anything else returns null from tryHandleForgeRequest, or\n * 404 from the plain handler):\n * - GET /forge/health\n * - GET /forge/status\n * - GET /.well-known/clawcash.json\n * - POST {fulfillPath}/{action-path} (requires FORGE_API_KEY auth)\n */\n\nexport interface ForgeHandlerOptions {\n /** Merchant handle — FORGE_HANDLE env when omitted. */\n handle?: string;\n /** Per-project key. FORGE_API_KEY env when omitted. */\n apiKey?: string;\n /** Defaults to /forge/fulfill. */\n fulfillPath?: string;\n /** Reported in /forge/status. Optional. */\n publicBaseUrl?: string;\n /** Reported in /forge/status. Defaults to GATEWAY_URL env or the ClawCash gateway. */\n gatewayUrl?: string;\n}\n\nexport type ForgeFetchHandler = (request: Request) => Promise<Response>;\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nfunction isAuthorized(request: Request, apiKey: string | null): boolean {\n if (!apiKey) return false;\n const header = request.headers.get(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt =\n request.headers.get(\"x-forge-api-key\")?.trim() ||\n request.headers.get(\"x-clawcash-gateway\")?.trim() ||\n \"\";\n return bearer === apiKey || alt === apiKey;\n}\n\n/**\n * Returns a Response when the request targets a Forge route, else null so\n * the host app can fall through to its own routing.\n */\nexport function createForgeRequestMatcher(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): (request: Request) => Promise<Response | null> {\n if (!services?.length) {\n throw new Error(\"createForgeHandler: services must be a non-empty array\");\n }\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const handle =\n options.handle?.trim().toLowerCase() ||\n process.env.FORGE_HANDLE?.trim().toLowerCase() ||\n null;\n const gatewayUrl =\n options.gatewayUrl?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n \"https://gateway.clawca.sh\";\n const context = createContext();\n\n const getStatus = () => {\n const apiKey = getForgeApiKey(options.apiKey);\n return buildForgeSurfaceStatus({\n handle,\n publicBaseUrl: options.publicBaseUrl ?? null,\n gatewayUrl,\n fulfillMounted: Boolean(apiKey),\n fulfillPath,\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n };\n\n return async (request: Request): Promise<Response | null> => {\n const url = new URL(request.url);\n const pathname = url.pathname.replace(/\\/$/, \"\") || \"/\";\n\n if (request.method === \"GET\") {\n if (pathname === \"/forge/health\") {\n const status = getStatus();\n return json({\n ok: true,\n forge: true,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n actions: status.actions.map((a) => a.name),\n });\n }\n if (pathname === \"/forge/status\") {\n const status = getStatus();\n return json(status, status.ready ? 200 : 503);\n }\n if (pathname === \"/.well-known/clawcash.json\") {\n return json(buildWellKnownDocument(getStatus()));\n }\n return null;\n }\n\n if (request.method === \"POST\" && pathname.startsWith(`${fulfillPath}/`)) {\n const actionPath = pathname.slice(fulfillPath.length + 1);\n const service = services.find((s) => s.path === actionPath) ?? null;\n if (!service) {\n return json({ error: `Unknown action ${actionPath}` }, 404);\n }\n const apiKey = getForgeApiKey(options.apiKey);\n if (!isAuthorized(request, apiKey)) {\n return json(\n {\n error:\n \"Unauthorized — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\n },\n 401,\n );\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json({ error: \"Request body must be JSON\" }, 400);\n }\n const validated = validateInput(body, service.input);\n if (!validated.ok) {\n return json({ error: validated.error }, 400);\n }\n try {\n const output = await service.execute(validated.value, context);\n return 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 return json({ error: message }, 500);\n }\n }\n\n return null;\n };\n}\n\n/** Like createForgeRequestMatcher, but 404s instead of returning null. */\nexport function createForgeHandler(\n services: DefinedAgentService[],\n options: ForgeHandlerOptions = {},\n): ForgeFetchHandler {\n const match = createForgeRequestMatcher(services, options);\n return async (request) =>\n (await match(request)) ?? json({ error: \"Not found\" }, 404);\n}\n"],"mappings":"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","names":["json"]}
|
package/package.json
CHANGED