@clawcash/forge 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -45
- package/dist/index.cjs +320 -562
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -171
- package/dist/index.d.ts +75 -171
- package/dist/index.js +305 -549
- package/dist/index.js.map +1 -1
- package/package.json +2 -12
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/define.ts","../src/mount.ts","../src/base-url.ts","../src/gateway.ts","../src/gateway-auth.ts","../src/payto.ts","../src/status.ts","../src/skill.ts"],"sourcesContent":["export { defineAgentService, waitUntil, createContext, toKebabCase } from \"./define.js\";\nexport { mountAgentServices, createForgeRouter } from \"./mount.js\";\nexport { generateSkillMarkdown } from \"./skill.js\";\nexport { resolvePayTo, isZeroAddress } from \"./payto.js\";\nexport { resolveBaseUrlFromEnv, PublicBaseUrlResolver } from \"./base-url.js\";\nexport { heartbeatGateway } from \"./gateway.js\";\nexport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n forgeSdkVersion,\n getLastHeartbeat,\n recordHeartbeatResult,\n} from \"./status.js\";\nexport type { ForgeHeartbeatState, ForgeSurfaceStatus } from \"./status.js\";\nexport {\n isForgeGatewayRequest,\n requireForgeGateway,\n getForgeApiKey,\n getForgeGatewaySecret,\n forgeGatewayLoopbackHeaders,\n FORGE_GATEWAY_USER_ID,\n} from \"./gateway-auth.js\";\nexport type {\n SchemaFieldType,\n InputSchema,\n OutputSchema,\n PaymentConfig,\n WaitUntilOptions,\n AgentServiceContext,\n AgentServiceConfig,\n DefinedAgentService,\n MountOptions,\n ForgePayToSource,\n SkillMeta,\n} from \"./types.js\";\n","import type {\n AgentServiceConfig,\n AgentServiceContext,\n DefinedAgentService,\n WaitUntilOptions,\n} from \"./types.js\";\n\nexport async function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 60_000;\n const started = Date.now();\n\n for (;;) {\n const value = await options.run();\n if (options.completedWhen(value)) {\n return value;\n }\n if (Date.now() - started >= timeoutMs) {\n throw new Error(`waitUntil timed out after ${timeoutMs}ms`);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function createContext(): AgentServiceContext {\n return {\n waitUntil,\n };\n}\n\n/** Convert snake_case or camelCase service name to kebab-case path segment. */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\nexport function defineAgentService<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TOutput extends Record<string, unknown> = Record<string, unknown>,\n>(\n config: AgentServiceConfig<TInput, TOutput>,\n): DefinedAgentService<TInput, TOutput> {\n if (!config.name?.trim()) {\n throw new Error(\"defineAgentService: name is required\");\n }\n if (!config.execute) {\n throw new Error(\"defineAgentService: execute is required\");\n }\n\n return {\n ...config,\n __forge: true,\n path: toKebabCase(config.name),\n };\n}\n","import type { Application, Request, Response, NextFunction, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { paymentMiddleware, x402ResourceServer } from \"@x402/express\";\nimport type { Network } from \"@x402/express\";\nimport { HTTPFacilitatorClient } from \"@x402/core/server\";\nimport type { RoutesConfig } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\nimport {\n PublicBaseUrlResolver,\n resolveBaseUrlFromEnv,\n} from \"./base-url.js\";\nimport { createContext } from \"./define.js\";\nimport { heartbeatGateway } from \"./gateway.js\";\nimport {\n getForgeApiKey,\n requireForgeGateway,\n} from \"./gateway-auth.js\";\nimport { isZeroAddress, resolvePayTo } from \"./payto.js\";\nimport {\n buildForgeSurfaceStatus,\n buildWellKnownDocument,\n recordHeartbeatResult,\n} from \"./status.js\";\nimport type {\n DefinedAgentService,\n InputSchema,\n MountOptions,\n SchemaFieldType,\n} from \"./types.js\";\n\n/** Base mainnet — settled via the public XPay facilitator (no API keys). */\nconst DEFAULT_NETWORK = \"eip155:8453\" as const satisfies Network;\n/** Open facilitator: Base mainnet + Sepolia, no API keys. https://github.com/xpaysh/xpay-x402 */\nconst DEFAULT_FACILITATOR_URL = \"https://facilitator.xpay.sh\";\n/** ClawCash gateway — override with GATEWAY_URL or options.gateway.url. */\nconst DEFAULT_GATEWAY_URL = \"https://gateway.clawca.sh\";\n\nfunction asNetwork(value: string | undefined): Network {\n return (value ?? DEFAULT_NETWORK) as Network;\n}\n\nfunction createFacilitatorClient(options: MountOptions): HTTPFacilitatorClient {\n return new HTTPFacilitatorClient({\n url: options.facilitatorUrl ?? DEFAULT_FACILITATOR_URL,\n createAuthHeaders: options.createAuthHeaders,\n });\n}\n\nfunction validateInput(\n body: unknown,\n schema: InputSchema,\n): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) {\n return { ok: false, error: \"Request body must be a JSON object\" };\n }\n\n const input = body as Record<string, unknown>;\n const value: Record<string, unknown> = {};\n\n for (const [key, type] of Object.entries(schema)) {\n if (!(key in input) || input[key] === undefined || input[key] === null) {\n return { ok: false, error: `Missing required field: ${key}` };\n }\n const err = checkType(key, input[key], type);\n if (err) return { ok: false, error: err };\n value[key] = input[key];\n }\n\n return { ok: true, value };\n}\n\nfunction checkType(key: string, value: unknown, type: SchemaFieldType): string | null {\n switch (type) {\n case \"string\":\n case \"url\":\n if (typeof value !== \"string\" || !value.trim()) {\n return `Field ${key} must be a non-empty string`;\n }\n if (type === \"url\") {\n try {\n new URL(value);\n } catch {\n return `Field ${key} must be a valid URL`;\n }\n }\n return null;\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return `Field ${key} must be a number`;\n }\n return null;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return `Field ${key} must be a boolean`;\n }\n return null;\n default:\n return null;\n }\n}\n\nfunction formatPrice(amount: string): string {\n const normalized = amount.startsWith(\"$\") ? amount.slice(1) : amount;\n return `$${normalized}`;\n}\n\nfunction mountFulfillmentRouter(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): void {\n const secret = getForgeApiKey(options.gateway?.registerSecret);\n if (!secret) {\n console.warn(\n \"[forge] /forge/fulfill not mounted — set FORGE_API_KEY (from Forge dashboard, min 16 chars) so the gateway can fulfill after payment\",\n );\n return;\n }\n\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const router = createRouter();\n const context = createContext();\n const gate = requireForgeGateway(secret);\n\n for (const service of services) {\n router.post(\n `/${service.path}`,\n gate,\n async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] fulfill ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n },\n );\n }\n\n app.use(fulfillPath, router);\n console.log(\n `[forge] fulfillment mounted at POST ${fulfillPath}/{action} (gateway secret)`,\n );\n}\n\nfunction mountForgeStatusRoutes(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n opts: {\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n handle: string | null;\n resolver: PublicBaseUrlResolver;\n gatewayUrl: string | null;\n },\n): void {\n const getStatus = () =>\n buildForgeSurfaceStatus({\n handle: opts.handle,\n publicBaseUrl: opts.resolver.get(),\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath: opts.fulfillPath,\n agentBasePath: opts.agentBasePath,\n skipPayment: Boolean(options.skipPayment),\n actions: services.map((s) => ({\n name: s.name,\n path: s.path,\n price: s.payment.amount,\n })),\n });\n\n app.get(\"/forge/health\", (req: Request, res: Response) => {\n opts.resolver.considerRequest(req);\n const status = getStatus();\n res.json({\n ok: true,\n forge: true as const,\n ready: status.ready,\n handle: status.handle,\n version: status.version,\n // Per-boot nonce so this instance can self-verify an inferred base URL.\n instance: opts.resolver.instanceNonce,\n actions: status.actions.map((a) => a.name),\n });\n });\n\n app.get(\"/forge/status\", (req: Request, res: Response) => {\n opts.resolver.considerRequest(req);\n const status = getStatus();\n res.status(status.ready ? 200 : 503).json(status);\n });\n\n app.get(\"/.well-known/clawcash.json\", (req: Request, res: Response) => {\n opts.resolver.considerRequest(req);\n res.json(buildWellKnownDocument(getStatus()));\n });\n\n console.log(\n \"[forge] liveliness: GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json\",\n );\n}\n\n/**\n * Mount Forge agent services on an Express app.\n * - GET /forge/health, GET /forge/status, GET /.well-known/clawcash.json — liveliness\n * - POST {basePath}/{kebab-name} — optional public x402 agent surface\n * - POST /forge/fulfill/{kebab-name} — private gateway fulfillment after payment\n *\n * payTo resolves automatically: PAY_TO env (optional) → Forge API → baked fallback.\n */\nexport async function mountAgentServices(\n app: Application,\n services: DefinedAgentService[],\n options: MountOptions,\n): Promise<Router> {\n let payTo = \"\";\n if (!options.skipPayment) {\n const fallback =\n options.payToFallback?.trim() || options.payTo?.trim() || \"\";\n payTo = await resolvePayTo({\n fallback,\n payTo: options.payTo,\n forge: options.forge,\n });\n if (isZeroAddress(payTo)) {\n throw new Error(\n \"mountAgentServices: resolved payTo is the zero address. Re-run Forge to provision a wallet.\",\n );\n }\n }\n\n const basePath = (options.basePath ?? \"/agent\").replace(/\\/$/, \"\") || \"/agent\";\n const fulfillPath =\n (options.fulfillPath ?? \"/forge/fulfill\").replace(/\\/$/, \"\") ||\n \"/forge/fulfill\";\n const router = createRouter();\n const context = createContext();\n\n const routes: RoutesConfig = {};\n\n for (const service of services) {\n const routePath = `/${service.path}`;\n const fullPath = `${basePath}${routePath}`;\n const network = asNetwork(service.payment.network);\n\n if (!options.skipPayment) {\n routes[`POST ${fullPath}`] = {\n accepts: {\n scheme: \"exact\",\n price: formatPrice(service.payment.amount),\n network,\n payTo,\n },\n description: service.description,\n mimeType: \"application/json\",\n };\n }\n\n router.post(routePath, async (req: Request, res: Response) => {\n try {\n const validated = validateInput(req.body, service.input);\n if (!validated.ok) {\n res.status(400).json({ error: validated.error });\n return;\n }\n\n const output = await service.execute(validated.value, context);\n res.json(output);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal error\";\n console.error(`[forge] ${service.name} failed:`, err);\n res.status(500).json({ error: message });\n }\n });\n }\n\n if (!options.skipPayment && Object.keys(routes).length > 0) {\n const facilitator = createFacilitatorClient(options);\n const networks = new Set(\n services.map((s) => asNetwork(s.payment.network)),\n );\n const resourceServer = new x402ResourceServer(facilitator);\n for (const network of networks) {\n resourceServer.register(network, new ExactEvmScheme());\n }\n app.use(paymentMiddleware(routes, resourceServer, undefined, undefined, true));\n }\n\n app.use(basePath, router);\n const fulfillMounted = Boolean(\n getForgeApiKey(options.gateway?.registerSecret),\n );\n mountFulfillmentRouter(app, services, options);\n\n const gatewayUrl =\n options.gateway?.url?.trim() ||\n process.env.GATEWAY_URL?.trim() ||\n DEFAULT_GATEWAY_URL;\n const handle =\n options.gateway?.handle?.trim() || process.env.FORGE_HANDLE?.trim() || null;\n const registerSecret =\n options.gateway?.registerSecret?.trim() ||\n process.env.FORGE_API_KEY?.trim() ||\n process.env.GATEWAY_REGISTER_SECRET?.trim();\n\n const sendHeartbeat = (baseUrl: string) => {\n if (!handle || !registerSecret) return;\n void heartbeatGateway({\n gatewayUrl,\n handle,\n baseUrl,\n registerSecret,\n })\n .then(() => {\n recordHeartbeatResult({ ok: true });\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n recordHeartbeatResult({ ok: false, error: message });\n console.warn(\"[forge] gateway heartbeat skipped:\", err);\n });\n };\n\n const resolver = new PublicBaseUrlResolver({\n initial: resolveBaseUrlFromEnv(options.gateway?.publicBaseUrl),\n // Base URL learned late (inferred from the first probe): heartbeat now.\n onResolved: sendHeartbeat,\n });\n\n mountForgeStatusRoutes(app, services, options, {\n fulfillMounted,\n fulfillPath,\n agentBasePath: basePath,\n handle,\n resolver,\n gatewayUrl,\n });\n\n const bootBaseUrl = resolver.get();\n if (bootBaseUrl) {\n sendHeartbeat(bootBaseUrl);\n }\n\n return router;\n}\n\nexport function createForgeRouter(\n services: DefinedAgentService[],\n options: MountOptions,\n): (app: Application) => Promise<Router> {\n return (app) => mountAgentServices(app, services, options);\n}\n\nexport function forgeErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction,\n): void {\n const message = err instanceof Error ? err.message : \"Internal error\";\n res.status(500).json({ error: message });\n}\n","import { randomBytes } from \"node:crypto\";\nimport type { Request } from \"express\";\n\n/**\n * Public base URL resolution, in trust order:\n * 1. PUBLIC_BASE_URL env — explicit override.\n * 2. Platform-provided vars (Railway, Render, Vercel, Fly, Heroku) — set by\n * the host, not by request input, so safe to trust as-is.\n * 3. Lazy inference from the first inbound request to the Forge surface\n * (X-Forwarded-Proto + Host). Host is attacker-controlled, so a candidate\n * is only accepted after self-verification: we fetch our own\n * /forge/health at the candidate URL and check it returns this\n * instance's boot nonce.\n */\n\nexport function resolveBaseUrlFromEnv(explicit?: string): string | null {\n const fromExplicit = explicit?.trim();\n if (fromExplicit) return stripSlash(fromExplicit);\n\n const fromEnv = process.env.PUBLIC_BASE_URL?.trim();\n if (fromEnv) return stripSlash(fromEnv);\n\n const railway = process.env.RAILWAY_PUBLIC_DOMAIN?.trim();\n if (railway) return `https://${stripSlash(railway)}`;\n\n const render = process.env.RENDER_EXTERNAL_URL?.trim();\n if (render) return stripSlash(render);\n\n const vercel =\n process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim() ||\n process.env.VERCEL_URL?.trim();\n if (vercel) return `https://${stripSlash(vercel)}`;\n\n const fly = process.env.FLY_APP_NAME?.trim();\n if (fly) return `https://${fly}.fly.dev`;\n\n const heroku = process.env.HEROKU_APP_NAME?.trim();\n if (heroku) return `https://${heroku}.herokuapp.com`;\n\n return null;\n}\n\nfunction stripSlash(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nconst MAX_VERIFY_ATTEMPTS = 5;\nconst VERIFY_TIMEOUT_MS = 3000;\n\nexport class PublicBaseUrlResolver {\n /** Per-boot nonce, echoed by /forge/health for self-verification. */\n readonly instanceNonce: string;\n\n private resolved: string | null;\n private verifying = false;\n private rejected = new Set<string>();\n private attempts = 0;\n private onResolved: ((baseUrl: string) => void) | null;\n\n constructor(opts: {\n /** URL already known from env/platform vars (trusted, no verification). */\n initial: string | null;\n /** Called once when a base URL first becomes known via inference. */\n onResolved?: (baseUrl: string) => void;\n }) {\n this.instanceNonce = randomBytes(16).toString(\"hex\");\n this.resolved = opts.initial;\n this.onResolved = opts.onResolved ?? null;\n }\n\n get(): string | null {\n return this.resolved;\n }\n\n /**\n * Feed an inbound Forge-surface request. No-op once resolved. Kicks off\n * async self-verification of the candidate URL derived from the request.\n */\n considerRequest(req: Request): void {\n if (this.resolved || this.verifying) return;\n if (this.attempts >= MAX_VERIFY_ATTEMPTS) return;\n\n const candidate = candidateFromRequest(req);\n if (!candidate || this.rejected.has(candidate)) return;\n\n this.verifying = true;\n this.attempts += 1;\n void this.verify(candidate)\n .then((ok) => {\n if (ok) {\n this.resolved = candidate;\n console.log(`[forge] public base URL inferred and verified: ${candidate}`);\n this.onResolved?.(candidate);\n } else {\n this.rejected.add(candidate);\n console.warn(\n `[forge] candidate base URL failed self-verification, ignoring: ${candidate}`,\n );\n }\n })\n .finally(() => {\n this.verifying = false;\n });\n }\n\n /** Fetch our own /forge/health at the candidate and match the boot nonce. */\n private async verify(candidate: string): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), VERIFY_TIMEOUT_MS);\n const res = await fetch(`${candidate}/forge/health`, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n redirect: \"manual\",\n });\n clearTimeout(timer);\n if (!res.ok) return false;\n const json = (await res.json()) as { instance?: string };\n return json.instance === this.instanceNonce;\n } catch {\n return false;\n }\n }\n}\n\nfunction candidateFromRequest(req: Request): string | null {\n const host = firstHeaderValue(req.headers[\"x-forwarded-host\"]) ?? req.headers.host;\n if (!host || !isValidHost(host)) return null;\n\n const proto =\n firstHeaderValue(req.headers[\"x-forwarded-proto\"]) ??\n (req.protocol || \"http\");\n if (proto !== \"https\" && proto !== \"http\") return null;\n\n const isLocal = /^(localhost|127\\.0\\.0\\.1|\\[::1\\])(:\\d+)?$/i.test(host);\n // A deployed merchant is always behind HTTPS; only allow http locally.\n if (proto === \"http\" && !isLocal) return null;\n\n return `${proto}://${host}`;\n}\n\nfunction firstHeaderValue(value: string | string[] | undefined): string | null {\n const raw = Array.isArray(value) ? value[0] : value;\n const first = raw?.split(\",\")[0]?.trim();\n return first || null;\n}\n\nfunction isValidHost(host: string): boolean {\n // hostname with optional port; rejects paths, spaces, credentials\n return /^[a-z0-9.-]+(:\\d{1,5})?$/i.test(host) || /^\\[[0-9a-f:]+\\](:\\d{1,5})?$/i.test(host);\n}\n","/**\n * Notify ClawCash gateway of this merchant's live public base URL.\n * Full action registration still happens from Forge \"Go live\".\n */\nexport async function heartbeatGateway(opts: {\n gatewayUrl: string;\n handle: string;\n baseUrl: string;\n registerSecret: string;\n}): Promise<void> {\n const gateway = opts.gatewayUrl.replace(/\\/$/, \"\");\n const res = await fetch(`${gateway}/register/heartbeat`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${opts.registerSecret}`,\n },\n body: JSON.stringify({\n handle: opts.handle.trim().toLowerCase(),\n baseUrl: opts.baseUrl.replace(/\\/$/, \"\"),\n }),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(\n `gateway heartbeat failed (${res.status}): ${text.slice(0, 240)}`,\n );\n }\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Per-project Forge API key for gateway → merchant fulfillment.\n * Merchants set FORGE_API_KEY from the Forge dashboard (never the shared\n * GATEWAY_REGISTER_SECRET — that is Forge↔gateway platform auth only).\n *\n * GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.\n */\nexport function getForgeApiKey(explicit?: string | null): string | null {\n const value =\n (explicit ?? process.env.FORGE_API_KEY ?? process.env.GATEWAY_REGISTER_SECRET)?.trim();\n if (!value || value.length < 16) return null;\n return value;\n}\n\n/** @deprecated Use getForgeApiKey */\nexport function getForgeGatewaySecret(\n explicit?: string | null,\n): string | null {\n return getForgeApiKey(explicit);\n}\n\nexport function isForgeGatewayRequest(\n req: Request,\n secret?: string | null,\n): boolean {\n const expected = getForgeApiKey(secret);\n if (!expected) return false;\n const header = req.header(\"authorization\") ?? \"\";\n const bearer = header.toLowerCase().startsWith(\"bearer \")\n ? header.slice(7).trim()\n : \"\";\n const alt =\n req.header(\"x-forge-api-key\")?.trim() ||\n req.header(\"x-clawcash-gateway\")?.trim() ||\n \"\";\n return bearer === expected || alt === expected;\n}\n\n/** Synthetic user id for DB rows created by gateway-driven fulfillment. */\nexport const FORGE_GATEWAY_USER_ID = \"forge_gateway_agent\";\n\nexport function requireForgeGateway(\n secret?: string | null,\n): (req: Request, res: Response, next: NextFunction) => void {\n return (req, res, next) => {\n if (!isForgeGatewayRequest(req, secret)) {\n res.status(401).json({\n error:\n \"Unauthorized — Forge API key required (FORGE_API_KEY from Forge dashboard)\",\n });\n return;\n }\n next();\n };\n}\n\n/** Headers merchant execute() should send on loopback calls during fulfillment. */\nexport function forgeGatewayLoopbackHeaders(\n secret?: string | null,\n): Record<string, string> {\n const expected = getForgeApiKey(secret);\n if (!expected) return {};\n return {\n Authorization: `Bearer ${expected}`,\n \"X-Forge-Api-Key\": expected,\n };\n}\n","import type { ForgePayToSource } from \"./types.js\";\n\nexport type { ForgePayToSource };\n\nconst ZERO = /^0x0{40}$/i;\n\nexport function isZeroAddress(address: string | undefined | null): boolean {\n if (!address) return true;\n return ZERO.test(address.trim());\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction pickForgeBaseUrl(source?: ForgePayToSource): string | null {\n const fromEnv = process.env.FORGE_URL?.trim();\n if (fromEnv) return normalizeBaseUrl(fromEnv);\n\n const fromOpts = source?.baseUrl?.trim();\n if (!fromOpts) return null;\n\n const normalized = normalizeBaseUrl(fromOpts);\n // Never call a laptop Forge from a deployed merchant app.\n if (\n /localhost|127\\.0\\.0\\.1/i.test(normalized) &&\n process.env.NODE_ENV === \"production\"\n ) {\n return null;\n }\n return normalized;\n}\n\n/**\n * Resolve merchant payTo with minimal host config:\n * 1. `PAY_TO` env (optional override)\n * 2. Live address from Forge public API (when reachable)\n * 3. Baked-in fallback from the Forge PR\n */\nexport async function resolvePayTo(opts: {\n /** Baked-in address from Forge codegen (required fallback). */\n fallback: string;\n /** Optional explicit override (usually process.env.PAY_TO). */\n payTo?: string;\n forge?: ForgePayToSource;\n /** Fetch timeout ms. Defaults to 2500. */\n timeoutMs?: number;\n}): Promise<string> {\n const envOverride = process.env.PAY_TO?.trim();\n if (envOverride && !isZeroAddress(envOverride)) {\n return envOverride;\n }\n\n const explicit = opts.payTo?.trim();\n if (explicit && !isZeroAddress(explicit) && !opts.forge) {\n return explicit;\n }\n\n const baseUrl = pickForgeBaseUrl(opts.forge);\n if (baseUrl && opts.forge) {\n const url = `${baseUrl}/api/public/pay-to/${encodeURIComponent(opts.forge.owner)}/${encodeURIComponent(opts.forge.repo)}`;\n try {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n opts.timeoutMs ?? 2500,\n );\n const res = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n clearTimeout(timer);\n if (res.ok) {\n const json = (await res.json()) as { address?: string };\n if (json.address && !isZeroAddress(json.address)) {\n return json.address.trim();\n }\n }\n } catch (err) {\n console.warn(\"[forge] live payTo fetch failed, using fallback:\", err);\n }\n }\n\n if (explicit && !isZeroAddress(explicit)) return explicit;\n if (opts.fallback && !isZeroAddress(opts.fallback)) return opts.fallback;\n\n throw new Error(\n \"resolvePayTo: no usable address (set Forge wallet / PAY_TO, or re-run Forge PR)\",\n );\n}\n","/**\n * Merchant-side Forge surface status — public probes for deploy / go-live checks.\n */\n\nexport type ForgeHeartbeatState = {\n attempted: boolean;\n ok: boolean | null;\n at: string | null;\n error: string | null;\n};\n\nexport type ForgeSurfaceStatus = {\n ok: boolean;\n forge: true;\n version: string;\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n /** True when FORGE_API_KEY is set so /forge/fulfill is mounted. */\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n /** Env + fulfill ready — merchant can accept gateway fulfillment. */\n ready: boolean;\n heartbeat: ForgeHeartbeatState;\n endpoints: {\n health: string;\n status: string;\n wellKnown: string;\n fulfill: string;\n agent: string;\n };\n};\n\nconst PACKAGE_VERSION = \"0.1.8\";\n\nlet lastHeartbeat: ForgeHeartbeatState = {\n attempted: false,\n ok: null,\n at: null,\n error: null,\n};\n\nexport function recordHeartbeatResult(\n result: { ok: true } | { ok: false; error: string },\n): void {\n lastHeartbeat = {\n attempted: true,\n ok: result.ok,\n at: new Date().toISOString(),\n error: result.ok ? null : result.error,\n };\n}\n\nexport function getLastHeartbeat(): ForgeHeartbeatState {\n return { ...lastHeartbeat };\n}\n\nexport function buildForgeSurfaceStatus(opts: {\n handle: string | null;\n publicBaseUrl: string | null;\n gatewayUrl: string | null;\n fulfillMounted: boolean;\n fulfillPath: string;\n agentBasePath: string;\n skipPayment: boolean;\n actions: Array<{ name: string; path: string; price: string }>;\n}): ForgeSurfaceStatus {\n const fulfillPath = opts.fulfillPath.replace(/\\/$/, \"\") || \"/forge/fulfill\";\n const agentBasePath = opts.agentBasePath.replace(/\\/$/, \"\") || \"/agent\";\n const ready =\n opts.fulfillMounted &&\n Boolean(opts.publicBaseUrl) &&\n opts.actions.length > 0;\n\n return {\n ok: true,\n forge: true,\n version: PACKAGE_VERSION,\n handle: opts.handle,\n publicBaseUrl: opts.publicBaseUrl,\n gatewayUrl: opts.gatewayUrl,\n fulfillMounted: opts.fulfillMounted,\n fulfillPath,\n agentBasePath,\n skipPayment: opts.skipPayment,\n actions: opts.actions,\n ready,\n heartbeat: getLastHeartbeat(),\n endpoints: {\n health: \"/forge/health\",\n status: \"/forge/status\",\n wellKnown: \"/.well-known/clawcash.json\",\n fulfill: fulfillPath,\n agent: agentBasePath,\n },\n };\n}\n\nexport function buildWellKnownDocument(status: ForgeSurfaceStatus): {\n forge: true;\n version: string;\n health: string;\n status: string;\n handle: string | null;\n ready: boolean;\n} {\n return {\n forge: true,\n version: status.version,\n health: status.endpoints.health,\n status: status.endpoints.status,\n handle: status.handle,\n ready: status.ready,\n };\n}\n\nexport function forgeSdkVersion(): string {\n return PACKAGE_VERSION;\n}\n","import type { DefinedAgentService, SkillMeta } from \"./types.js\";\n\n/**\n * Generate a SKILL.md document for agents from defined Forge services.\n */\nexport function generateSkillMarkdown(\n services: DefinedAgentService[],\n meta: SkillMeta,\n): string {\n const base = meta.baseUrl.replace(/\\/$/, \"\");\n const lines: string[] = [\n `---`,\n `name: ${meta.name}`,\n `description: ${meta.description}`,\n `---`,\n ``,\n `# ${meta.name}`,\n ``,\n meta.description,\n ``,\n ];\n\n if (meta.homepage) {\n lines.push(`Homepage: ${meta.homepage}`, ``);\n }\n\n lines.push(\n `## How to use`,\n ``,\n `1. Read this skill.`,\n `2. Call the agent endpoint with a JSON body matching the input schema.`,\n `3. If you receive HTTP 402, pay via x402 (USDC) and retry with the payment header.`,\n `4. Use the returned output fields.`,\n ``,\n `## Services`,\n ``,\n );\n\n for (const service of services) {\n const path = `${base}/agent/${service.path}`;\n const price = service.payment.amount.startsWith(\"$\")\n ? service.payment.amount\n : `$${service.payment.amount}`;\n\n lines.push(\n `### \\`${service.name}\\``,\n ``,\n service.description,\n ``,\n `- **Method:** \\`POST\\``,\n `- **URL:** \\`${path}\\``,\n `- **Price:** ${price} ${service.payment.currency} via ${service.payment.protocols.join(\", \")}`,\n ``,\n `**Input**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Output**`,\n ``,\n \"```json\",\n JSON.stringify(\n Object.fromEntries(\n Object.entries(service.output).map(([k, t]) => [k, `<${t}>`]),\n ),\n null,\n 2,\n ),\n \"```\",\n ``,\n `**Example**`,\n ``,\n \"```bash\",\n `curl -X POST ${path} \\\\`,\n ` -H \"Content-Type: application/json\" \\\\`,\n ` -d '${JSON.stringify(\n Object.fromEntries(\n Object.entries(service.input).map(([k, t]) => [\n k,\n t === \"url\" ? \"https://example.com/image.png\" : `example-${k}`,\n ]),\n ),\n )}'`,\n \"```\",\n ``,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["import_express","import_server","createRouter"]}
|
|
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":"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","names":["json","os"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Application, Router, Request, Response, NextFunction } from 'express';
|
|
2
|
-
|
|
3
1
|
type SchemaFieldType = "string" | "url" | "number" | "boolean";
|
|
4
2
|
type InputSchema = Record<string, SchemaFieldType>;
|
|
5
3
|
type OutputSchema = Record<string, SchemaFieldType>;
|
|
@@ -32,73 +30,6 @@ interface DefinedAgentService<TInput extends Record<string, unknown> = Record<st
|
|
|
32
30
|
readonly __forge: true;
|
|
33
31
|
path: string;
|
|
34
32
|
}
|
|
35
|
-
interface FacilitatorAuthHeaders {
|
|
36
|
-
verify: Record<string, string>;
|
|
37
|
-
settle: Record<string, string>;
|
|
38
|
-
supported: Record<string, string>;
|
|
39
|
-
bazaar?: Record<string, string>;
|
|
40
|
-
}
|
|
41
|
-
interface ForgePayToSource {
|
|
42
|
-
owner: string;
|
|
43
|
-
repo: string;
|
|
44
|
-
/** Forge web base URL. Optional — uses FORGE_URL env when set. */
|
|
45
|
-
baseUrl?: string;
|
|
46
|
-
}
|
|
47
|
-
interface MountOptions {
|
|
48
|
-
/**
|
|
49
|
-
* Recipient wallet. Prefer omitting and letting `forge` + baked fallback
|
|
50
|
-
* resolve automatically so merchants never set PAY_TO on their host.
|
|
51
|
-
* Still honored as override when set (or via process.env.PAY_TO).
|
|
52
|
-
*/
|
|
53
|
-
payTo?: string;
|
|
54
|
-
/**
|
|
55
|
-
* Baked-in Forge wallet from codegen. Used when live Forge fetch is
|
|
56
|
-
* unavailable. Required unless skipPayment or payTo is set.
|
|
57
|
-
*/
|
|
58
|
-
payToFallback?: string;
|
|
59
|
-
/**
|
|
60
|
-
* Auto-resolve payTo from Forge public API (no merchant env required).
|
|
61
|
-
*/
|
|
62
|
-
forge?: ForgePayToSource;
|
|
63
|
-
/**
|
|
64
|
-
* Facilitator URL. Defaults to XPay (`https://facilitator.xpay.sh`) —
|
|
65
|
-
* Base mainnet, no API keys. Override with CDP or others if needed.
|
|
66
|
-
*/
|
|
67
|
-
facilitatorUrl?: string;
|
|
68
|
-
/** Optional auth header factory (e.g. CDP JWT). Not required for XPay. */
|
|
69
|
-
createAuthHeaders?: () => Promise<FacilitatorAuthHeaders>;
|
|
70
|
-
/** Path prefix for agent routes. Defaults to /agent */
|
|
71
|
-
basePath?: string;
|
|
72
|
-
/** Skip x402 (local dev only). Defaults to false. */
|
|
73
|
-
skipPayment?: boolean;
|
|
74
|
-
/**
|
|
75
|
-
* Gateway heartbeat after mount. Only FORGE_API_KEY is required as env;
|
|
76
|
-
* the URL defaults to the ClawCash gateway, the handle is baked by Forge
|
|
77
|
-
* codegen, and the public base URL is resolved from platform vars or
|
|
78
|
-
* inferred (self-verified) from the first inbound probe.
|
|
79
|
-
*/
|
|
80
|
-
gateway?: {
|
|
81
|
-
/** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */
|
|
82
|
-
url?: string;
|
|
83
|
-
/** Merchant handle — baked by Forge codegen (or FORGE_HANDLE env). */
|
|
84
|
-
handle?: string;
|
|
85
|
-
/** @deprecated Prefer FORGE_API_KEY env — per-project key from Forge dashboard. */
|
|
86
|
-
registerSecret?: string;
|
|
87
|
-
/** Merchant public base URL. Usually omitted — resolved automatically. */
|
|
88
|
-
publicBaseUrl?: string;
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* Path prefix for private fulfillment (gateway → merchant after x402).
|
|
92
|
-
* Defaults to /forge/fulfill. Not end-user / x402 routes.
|
|
93
|
-
*/
|
|
94
|
-
fulfillPath?: string;
|
|
95
|
-
}
|
|
96
|
-
interface SkillMeta {
|
|
97
|
-
name: string;
|
|
98
|
-
description: string;
|
|
99
|
-
baseUrl: string;
|
|
100
|
-
homepage?: string;
|
|
101
|
-
}
|
|
102
33
|
|
|
103
34
|
declare function waitUntil<T>(options: WaitUntilOptions<T>): Promise<T>;
|
|
104
35
|
declare function createContext(): AgentServiceContext;
|
|
@@ -106,95 +37,82 @@ declare function createContext(): AgentServiceContext;
|
|
|
106
37
|
declare function toKebabCase(name: string): string;
|
|
107
38
|
declare function defineAgentService<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> = Record<string, unknown>>(config: AgentServiceConfig<TInput, TOutput>): DefinedAgentService<TInput, TOutput>;
|
|
108
39
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
40
|
+
interface ForgeWorkerOptions {
|
|
41
|
+
services: DefinedAgentService[];
|
|
42
|
+
/** Merchant handle. Defaults to FORGE_HANDLE env. */
|
|
43
|
+
handle?: string;
|
|
44
|
+
/** Per-project key from the Forge dashboard. Defaults to FORGE_API_KEY env. */
|
|
45
|
+
apiKey?: string;
|
|
46
|
+
/** Defaults to https://gateway.clawca.sh (or GATEWAY_URL env). */
|
|
47
|
+
gatewayUrl?: string;
|
|
48
|
+
/** Shown in gateway job rows. Defaults to hostname-pid. */
|
|
49
|
+
workerId?: string;
|
|
50
|
+
/** Long-poll window per claim request. Defaults to 20s (gateway caps at 25s). */
|
|
51
|
+
waitMs?: number;
|
|
52
|
+
onError?: (error: Error) => void;
|
|
53
|
+
}
|
|
54
|
+
interface ForgeWorkerHandle {
|
|
55
|
+
stop: () => void;
|
|
56
|
+
readonly running: boolean;
|
|
57
|
+
}
|
|
58
|
+
declare function forgeWorker(options: ForgeWorkerOptions): ForgeWorkerHandle;
|
|
124
59
|
|
|
125
|
-
declare function isZeroAddress(address: string | undefined | null): boolean;
|
|
126
60
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
61
|
+
* Web-standard fetch handler — mount Forge fulfillment + liveliness on any
|
|
62
|
+
* runtime that speaks (Request) => Response: Next.js route handlers, Hono,
|
|
63
|
+
* Fastify (via adapter), Bun, Deno, plain node:http (via adapter).
|
|
64
|
+
*
|
|
65
|
+
* This surface is for gateway-push (fulfillment.mode=sdk) merchants and
|
|
66
|
+
* deploy probes. Payment lives on the gateway; prefer forgeWorker() when the
|
|
67
|
+
* merchant can run an outbound worker instead.
|
|
68
|
+
*
|
|
69
|
+
* Routes handled (anything else returns null from tryHandleForgeRequest, or
|
|
70
|
+
* 404 from the plain handler):
|
|
71
|
+
* - GET /forge/health
|
|
72
|
+
* - GET /forge/status
|
|
73
|
+
* - GET /.well-known/clawcash.json
|
|
74
|
+
* - POST {fulfillPath}/{action-path} (requires FORGE_API_KEY auth)
|
|
131
75
|
*/
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
|
|
137
|
-
forge
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
76
|
+
interface ForgeHandlerOptions {
|
|
77
|
+
/** Merchant handle — FORGE_HANDLE env when omitted. */
|
|
78
|
+
handle?: string;
|
|
79
|
+
/** Per-project key. FORGE_API_KEY env when omitted. */
|
|
80
|
+
apiKey?: string;
|
|
81
|
+
/** Defaults to /forge/fulfill. */
|
|
82
|
+
fulfillPath?: string;
|
|
83
|
+
/** Reported in /forge/status. Optional. */
|
|
84
|
+
publicBaseUrl?: string;
|
|
85
|
+
/** Reported in /forge/status. Defaults to GATEWAY_URL env or the ClawCash gateway. */
|
|
86
|
+
gatewayUrl?: string;
|
|
87
|
+
}
|
|
88
|
+
type ForgeFetchHandler = (request: Request) => Promise<Response>;
|
|
142
89
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* 2. Platform-provided vars (Railway, Render, Vercel, Fly, Heroku) — set by
|
|
146
|
-
* the host, not by request input, so safe to trust as-is.
|
|
147
|
-
* 3. Lazy inference from the first inbound request to the Forge surface
|
|
148
|
-
* (X-Forwarded-Proto + Host). Host is attacker-controlled, so a candidate
|
|
149
|
-
* is only accepted after self-verification: we fetch our own
|
|
150
|
-
* /forge/health at the candidate URL and check it returns this
|
|
151
|
-
* instance's boot nonce.
|
|
90
|
+
* Returns a Response when the request targets a Forge route, else null so
|
|
91
|
+
* the host app can fall through to its own routing.
|
|
152
92
|
*/
|
|
153
|
-
declare function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
readonly instanceNonce: string;
|
|
157
|
-
private resolved;
|
|
158
|
-
private verifying;
|
|
159
|
-
private rejected;
|
|
160
|
-
private attempts;
|
|
161
|
-
private onResolved;
|
|
162
|
-
constructor(opts: {
|
|
163
|
-
/** URL already known from env/platform vars (trusted, no verification). */
|
|
164
|
-
initial: string | null;
|
|
165
|
-
/** Called once when a base URL first becomes known via inference. */
|
|
166
|
-
onResolved?: (baseUrl: string) => void;
|
|
167
|
-
});
|
|
168
|
-
get(): string | null;
|
|
169
|
-
/**
|
|
170
|
-
* Feed an inbound Forge-surface request. No-op once resolved. Kicks off
|
|
171
|
-
* async self-verification of the candidate URL derived from the request.
|
|
172
|
-
*/
|
|
173
|
-
considerRequest(req: Request): void;
|
|
174
|
-
/** Fetch our own /forge/health at the candidate and match the boot nonce. */
|
|
175
|
-
private verify;
|
|
176
|
-
}
|
|
93
|
+
declare function createForgeRequestMatcher(services: DefinedAgentService[], options?: ForgeHandlerOptions): (request: Request) => Promise<Response | null>;
|
|
94
|
+
/** Like createForgeRequestMatcher, but 404s instead of returning null. */
|
|
95
|
+
declare function createForgeHandler(services: DefinedAgentService[], options?: ForgeHandlerOptions): ForgeFetchHandler;
|
|
177
96
|
|
|
178
97
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
98
|
+
* Framework-free input validation shared by every execution surface
|
|
99
|
+
* (Express mount, fetch handler, worker).
|
|
181
100
|
*/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
101
|
+
type ValidationResult = {
|
|
102
|
+
ok: true;
|
|
103
|
+
value: Record<string, unknown>;
|
|
104
|
+
} | {
|
|
105
|
+
ok: false;
|
|
106
|
+
error: string;
|
|
107
|
+
};
|
|
108
|
+
declare function validateInput(body: unknown, schema: InputSchema): ValidationResult;
|
|
109
|
+
declare function checkType(key: string, value: unknown, type: SchemaFieldType): string | null;
|
|
188
110
|
|
|
189
111
|
/**
|
|
190
|
-
* Merchant-side Forge surface status — public probes for deploy / go-live
|
|
112
|
+
* Merchant-side Forge surface status — public probes for deploy / go-live
|
|
113
|
+
* checks when running the fetch handler. Worker-mode merchants report
|
|
114
|
+
* liveness by polling the gateway instead.
|
|
191
115
|
*/
|
|
192
|
-
type ForgeHeartbeatState = {
|
|
193
|
-
attempted: boolean;
|
|
194
|
-
ok: boolean | null;
|
|
195
|
-
at: string | null;
|
|
196
|
-
error: string | null;
|
|
197
|
-
};
|
|
198
116
|
type ForgeSurfaceStatus = {
|
|
199
117
|
ok: boolean;
|
|
200
118
|
forge: true;
|
|
@@ -202,42 +120,29 @@ type ForgeSurfaceStatus = {
|
|
|
202
120
|
handle: string | null;
|
|
203
121
|
publicBaseUrl: string | null;
|
|
204
122
|
gatewayUrl: string | null;
|
|
205
|
-
/** True when FORGE_API_KEY is set so
|
|
123
|
+
/** True when FORGE_API_KEY is set so fulfillment is accepted. */
|
|
206
124
|
fulfillMounted: boolean;
|
|
207
125
|
fulfillPath: string;
|
|
208
|
-
agentBasePath: string;
|
|
209
|
-
skipPayment: boolean;
|
|
210
126
|
actions: Array<{
|
|
211
127
|
name: string;
|
|
212
128
|
path: string;
|
|
213
129
|
price: string;
|
|
214
130
|
}>;
|
|
215
|
-
/**
|
|
131
|
+
/** Key + actions present — merchant can accept gateway fulfillment. */
|
|
216
132
|
ready: boolean;
|
|
217
|
-
heartbeat: ForgeHeartbeatState;
|
|
218
133
|
endpoints: {
|
|
219
134
|
health: string;
|
|
220
135
|
status: string;
|
|
221
136
|
wellKnown: string;
|
|
222
137
|
fulfill: string;
|
|
223
|
-
agent: string;
|
|
224
138
|
};
|
|
225
139
|
};
|
|
226
|
-
declare function recordHeartbeatResult(result: {
|
|
227
|
-
ok: true;
|
|
228
|
-
} | {
|
|
229
|
-
ok: false;
|
|
230
|
-
error: string;
|
|
231
|
-
}): void;
|
|
232
|
-
declare function getLastHeartbeat(): ForgeHeartbeatState;
|
|
233
140
|
declare function buildForgeSurfaceStatus(opts: {
|
|
234
141
|
handle: string | null;
|
|
235
142
|
publicBaseUrl: string | null;
|
|
236
143
|
gatewayUrl: string | null;
|
|
237
144
|
fulfillMounted: boolean;
|
|
238
145
|
fulfillPath: string;
|
|
239
|
-
agentBasePath: string;
|
|
240
|
-
skipPayment: boolean;
|
|
241
146
|
actions: Array<{
|
|
242
147
|
name: string;
|
|
243
148
|
path: string;
|
|
@@ -255,20 +160,19 @@ declare function buildWellKnownDocument(status: ForgeSurfaceStatus): {
|
|
|
255
160
|
declare function forgeSdkVersion(): string;
|
|
256
161
|
|
|
257
162
|
/**
|
|
258
|
-
* Per-project Forge API key
|
|
259
|
-
*
|
|
260
|
-
*
|
|
163
|
+
* Per-project Forge API key helpers — framework-free.
|
|
164
|
+
*
|
|
165
|
+
* Merchants set FORGE_API_KEY from the Forge dashboard. The worker uses it
|
|
166
|
+
* to claim jobs from the gateway; the fetch handler uses it to gate
|
|
167
|
+
* gateway-push fulfillment; execute() can use forgeGatewayLoopbackHeaders()
|
|
168
|
+
* so the merchant's own API middleware can recognize fulfillment traffic.
|
|
261
169
|
*
|
|
262
170
|
* GATEWAY_REGISTER_SECRET is accepted as a deprecated fallback for older deploys.
|
|
263
171
|
*/
|
|
264
172
|
declare function getForgeApiKey(explicit?: string | null): string | null;
|
|
265
|
-
/**
|
|
266
|
-
declare function getForgeGatewaySecret(explicit?: string | null): string | null;
|
|
267
|
-
declare function isForgeGatewayRequest(req: Request, secret?: string | null): boolean;
|
|
268
|
-
/** Synthetic user id for DB rows created by gateway-driven fulfillment. */
|
|
173
|
+
/** Synthetic user id for DB rows created by fulfillment traffic. */
|
|
269
174
|
declare const FORGE_GATEWAY_USER_ID = "forge_gateway_agent";
|
|
270
|
-
declare function requireForgeGateway(secret?: string | null): (req: Request, res: Response, next: NextFunction) => void;
|
|
271
175
|
/** Headers merchant execute() should send on loopback calls during fulfillment. */
|
|
272
176
|
declare function forgeGatewayLoopbackHeaders(secret?: string | null): Record<string, string>;
|
|
273
177
|
|
|
274
|
-
export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type
|
|
178
|
+
export { type AgentServiceConfig, type AgentServiceContext, type DefinedAgentService, FORGE_GATEWAY_USER_ID, type ForgeFetchHandler, type ForgeHandlerOptions, type ForgeSurfaceStatus, type ForgeWorkerHandle, type ForgeWorkerOptions, type InputSchema, type OutputSchema, type PaymentConfig, type SchemaFieldType, type ValidationResult, type WaitUntilOptions, buildForgeSurfaceStatus, buildWellKnownDocument, checkType, createContext, createForgeHandler, createForgeRequestMatcher, defineAgentService, forgeGatewayLoopbackHeaders, forgeSdkVersion, forgeWorker, getForgeApiKey, toKebabCase, validateInput, waitUntil };
|