@paybond/kit 0.9.8 → 0.11.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/completion-presets/catalog.json +1187 -0
- package/completion-presets/catalog.sha256 +1 -0
- package/dev/trace-ui/dashboard.html +617 -0
- package/dist/agent/adapter.d.ts +51 -0
- package/dist/agent/adapter.js +66 -0
- package/dist/agent/attach-bundle.d.ts +37 -0
- package/dist/agent/attach-bundle.js +118 -0
- package/dist/agent/authorization-cache.d.ts +22 -0
- package/dist/agent/authorization-cache.js +36 -0
- package/dist/agent/deferred-tools.d.ts +11 -0
- package/dist/agent/deferred-tools.js +62 -0
- package/dist/agent/discover.d.ts +41 -0
- package/dist/agent/discover.js +147 -0
- package/dist/agent/evidence.d.ts +9 -0
- package/dist/agent/evidence.js +28 -0
- package/dist/agent/facade.d.ts +48 -0
- package/dist/agent/facade.js +112 -0
- package/dist/agent/gateway-trace-reporter.d.ts +28 -0
- package/dist/agent/gateway-trace-reporter.js +49 -0
- package/dist/agent/generic-runner.d.ts +14 -0
- package/dist/agent/generic-runner.js +49 -0
- package/dist/agent/generic-sandbox-demo.d.ts +36 -0
- package/dist/agent/generic-sandbox-demo.js +82 -0
- package/dist/agent/guarded-agent.d.ts +49 -0
- package/dist/agent/guarded-agent.js +100 -0
- package/dist/agent/index.d.ts +13 -0
- package/dist/agent/index.js +13 -0
- package/dist/agent/instrument.d.ts +156 -0
- package/dist/agent/instrument.js +442 -0
- package/dist/agent/interceptor.d.ts +24 -0
- package/dist/agent/interceptor.js +440 -0
- package/dist/agent/lazy-context-tools.d.ts +15 -0
- package/dist/agent/lazy-context-tools.js +81 -0
- package/dist/agent/registry-file.d.ts +39 -0
- package/dist/agent/registry-file.js +219 -0
- package/dist/agent/registry.d.ts +37 -0
- package/dist/agent/registry.js +128 -0
- package/dist/agent/run.d.ts +124 -0
- package/dist/agent/run.js +301 -0
- package/dist/agent/types.d.ts +318 -0
- package/dist/agent/types.js +42 -0
- package/dist/agent-recognition.d.ts +72 -0
- package/dist/agent-recognition.js +165 -0
- package/dist/bincode-wire.d.ts +18 -0
- package/dist/bincode-wire.js +93 -0
- package/dist/claude-agents/config.d.ts +21 -0
- package/dist/claude-agents/config.js +145 -0
- package/dist/claude-agents/index.d.ts +2 -0
- package/dist/claude-agents/index.js +2 -0
- package/dist/claude-agents/sandbox-demo.d.ts +30 -0
- package/dist/claude-agents/sandbox-demo.js +91 -0
- package/dist/cli/agent/env-quote.d.ts +1 -0
- package/dist/cli/agent/env-quote.js +6 -0
- package/dist/cli/agent/env-write.d.ts +8 -0
- package/dist/cli/agent/env-write.js +47 -0
- package/dist/cli/agent/paybond.d.ts +16 -0
- package/dist/cli/agent/paybond.js +55 -0
- package/dist/cli/agent/policy-file.d.ts +29 -0
- package/dist/cli/agent/policy-file.js +61 -0
- package/dist/cli/agent/production-evidence.d.ts +24 -0
- package/dist/cli/agent/production-evidence.js +98 -0
- package/dist/cli/agent/run-store.d.ts +30 -0
- package/dist/cli/agent/run-store.js +42 -0
- package/dist/cli/agent/run-trace-store.d.ts +39 -0
- package/dist/cli/agent/run-trace-store.js +143 -0
- package/dist/cli/agent-run-trace-table.d.ts +9 -0
- package/dist/cli/agent-run-trace-table.js +24 -0
- package/dist/cli/agent-sandbox-smoke-checklist.d.ts +9 -0
- package/dist/cli/agent-sandbox-smoke-checklist.js +50 -0
- package/dist/cli/audit-export.d.ts +7 -0
- package/dist/cli/audit-export.js +120 -0
- package/dist/cli/automation.d.ts +25 -0
- package/dist/cli/automation.js +297 -0
- package/dist/cli/body.d.ts +7 -0
- package/dist/cli/body.js +22 -0
- package/dist/cli/color.d.ts +15 -0
- package/dist/cli/color.js +39 -0
- package/dist/cli/command-spec.d.ts +13 -0
- package/dist/cli/command-spec.js +611 -0
- package/dist/cli/commands/agent.d.ts +16 -0
- package/dist/cli/commands/agent.js +1135 -0
- package/dist/cli/commands/dev.d.ts +7 -0
- package/dist/cli/commands/dev.js +348 -0
- package/dist/cli/commands/discovery.d.ts +8 -0
- package/dist/cli/commands/discovery.js +194 -0
- package/dist/cli/commands/policy.d.ts +13 -0
- package/dist/cli/commands/policy.js +769 -0
- package/dist/cli/commands/setup.d.ts +18 -0
- package/dist/cli/commands/setup.js +513 -0
- package/dist/cli/commands/workflows.d.ts +7 -0
- package/dist/cli/commands/workflows.js +214 -0
- package/dist/cli/config.d.ts +16 -0
- package/dist/cli/config.js +98 -0
- package/dist/cli/context.d.ts +22 -0
- package/dist/cli/context.js +110 -0
- package/dist/cli/credentials.d.ts +21 -0
- package/dist/cli/credentials.js +141 -0
- package/dist/cli/doctor-agent-middleware.d.ts +5 -0
- package/dist/cli/doctor-agent-middleware.js +49 -0
- package/dist/cli/doctor-agent.d.ts +15 -0
- package/dist/cli/doctor-agent.js +311 -0
- package/dist/cli/envelope.d.ts +14 -0
- package/dist/cli/envelope.js +46 -0
- package/dist/cli/globals.d.ts +23 -0
- package/dist/cli/globals.js +248 -0
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.js +83 -0
- package/dist/cli/mcp-install.d.ts +41 -0
- package/dist/cli/mcp-install.js +95 -0
- package/dist/cli/mcp-policy.d.ts +26 -0
- package/dist/cli/mcp-policy.js +110 -0
- package/dist/cli/mcp-verify-config.d.ts +37 -0
- package/dist/cli/mcp-verify-config.js +172 -0
- package/dist/cli/redact.d.ts +4 -0
- package/dist/cli/redact.js +88 -0
- package/dist/cli/request-id.d.ts +2 -0
- package/dist/cli/request-id.js +5 -0
- package/dist/cli/router.d.ts +2 -0
- package/dist/cli/router.js +378 -0
- package/dist/cli/smoke-deep-links.d.ts +15 -0
- package/dist/cli/smoke-deep-links.js +63 -0
- package/dist/cli/suggest.d.ts +4 -0
- package/dist/cli/suggest.js +58 -0
- package/dist/cli/support-diagnostics.d.ts +19 -0
- package/dist/cli/support-diagnostics.js +47 -0
- package/dist/cli/telemetry.d.ts +17 -0
- package/dist/cli/telemetry.js +94 -0
- package/dist/cli/types.d.ts +72 -0
- package/dist/cli/types.js +60 -0
- package/dist/cli/ux.d.ts +8 -0
- package/dist/cli/ux.js +164 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +38 -0
- package/dist/completion-catalog-digest.d.ts +2 -0
- package/dist/completion-catalog-digest.js +2 -0
- package/dist/completion-catalog-integrity.d.ts +2 -0
- package/dist/completion-catalog-integrity.js +17 -0
- package/dist/completion-catalog.d.ts +49 -0
- package/dist/completion-catalog.js +62 -0
- package/dist/completion-contract-digest.d.ts +37 -0
- package/dist/completion-contract-digest.js +73 -0
- package/dist/completion-forbidden-fields.d.ts +5 -0
- package/dist/completion-forbidden-fields.js +26 -0
- package/dist/completion-init.d.ts +8 -0
- package/dist/completion-init.js +334 -0
- package/dist/completion-resolve.d.ts +21 -0
- package/dist/completion-resolve.js +86 -0
- package/dist/completion-validate-evidence.d.ts +24 -0
- package/dist/completion-validate-evidence.js +145 -0
- package/dist/dev/offline-gateway.d.ts +24 -0
- package/dist/dev/offline-gateway.js +102 -0
- package/dist/dev/trace-buffer.d.ts +61 -0
- package/dist/dev/trace-buffer.js +330 -0
- package/dist/dev/trace-security-headers.d.ts +11 -0
- package/dist/dev/trace-security-headers.js +16 -0
- package/dist/dev/trace-server.d.ts +8 -0
- package/dist/dev/trace-server.js +38 -0
- package/dist/dev/trace-ui.d.ts +4 -0
- package/dist/dev/trace-ui.js +25 -0
- package/dist/dev/wiremock-up.d.ts +15 -0
- package/dist/dev/wiremock-up.js +118 -0
- package/dist/doctor-completion.d.ts +17 -0
- package/dist/doctor-completion.js +428 -0
- package/dist/gateway-url.d.ts +7 -0
- package/dist/gateway-url.js +69 -0
- package/dist/index.d.ts +119 -2
- package/dist/index.js +267 -6
- package/dist/init.js +380 -68
- package/dist/langgraph/awrap-tool-call.d.ts +25 -0
- package/dist/langgraph/awrap-tool-call.js +81 -0
- package/dist/langgraph/config.d.ts +13 -0
- package/dist/langgraph/config.js +11 -0
- package/dist/langgraph/index.d.ts +4 -0
- package/dist/langgraph/index.js +4 -0
- package/dist/langgraph/sandbox-demo.d.ts +40 -0
- package/dist/langgraph/sandbox-demo.js +96 -0
- package/dist/langgraph/tool-node.d.ts +10 -0
- package/dist/langgraph/tool-node.js +38 -0
- package/dist/login.d.ts +14 -1
- package/dist/login.js +130 -63
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/tool-surface.d.ts +25 -0
- package/dist/mcp/tool-surface.js +17 -0
- package/dist/mcp-capability-token-cache.d.ts +24 -0
- package/dist/mcp-capability-token-cache.js +101 -0
- package/dist/mcp-evidence-policy.d.ts +48 -0
- package/dist/mcp-evidence-policy.js +117 -0
- package/dist/mcp-policy-reload.d.ts +79 -0
- package/dist/mcp-policy-reload.js +200 -0
- package/dist/mcp-sep2828-evidence.d.ts +36 -0
- package/dist/mcp-sep2828-evidence.js +65 -0
- package/dist/mcp-server.d.ts +10 -0
- package/dist/mcp-server.js +423 -115
- package/dist/openai-agents/index.d.ts +40 -0
- package/dist/openai-agents/index.js +151 -0
- package/dist/openai-agents/sandbox-demo.d.ts +34 -0
- package/dist/openai-agents/sandbox-demo.js +118 -0
- package/dist/payee-evidence.js +2 -29
- package/dist/policy/catalog.d.ts +31 -0
- package/dist/policy/catalog.js +48 -0
- package/dist/policy/compose-utils.d.ts +3 -0
- package/dist/policy/compose-utils.js +4 -0
- package/dist/policy/compose.d.ts +20 -0
- package/dist/policy/compose.js +240 -0
- package/dist/policy/digest.d.ts +7 -0
- package/dist/policy/digest.js +75 -0
- package/dist/policy/domain.d.ts +15 -0
- package/dist/policy/domain.js +28 -0
- package/dist/policy/guardrail-spec.d.ts +17 -0
- package/dist/policy/guardrail-spec.js +140 -0
- package/dist/policy/guardrails.d.ts +48 -0
- package/dist/policy/guardrails.js +72 -0
- package/dist/policy/index.d.ts +21 -0
- package/dist/policy/index.js +21 -0
- package/dist/policy/init.d.ts +102 -0
- package/dist/policy/init.js +351 -0
- package/dist/policy/intent-spec.d.ts +52 -0
- package/dist/policy/intent-spec.js +176 -0
- package/dist/policy/json-path.d.ts +4 -0
- package/dist/policy/json-path.js +23 -0
- package/dist/policy/layers-io.d.ts +7 -0
- package/dist/policy/layers-io.js +28 -0
- package/dist/policy/load-effective.d.ts +22 -0
- package/dist/policy/load-effective.js +77 -0
- package/dist/policy/load.d.ts +85 -0
- package/dist/policy/load.js +164 -0
- package/dist/policy/merge.d.ts +29 -0
- package/dist/policy/merge.js +297 -0
- package/dist/policy/parse-text.d.ts +2 -0
- package/dist/policy/parse-text.js +126 -0
- package/dist/policy/policy-api.d.ts +45 -0
- package/dist/policy/policy-api.js +61 -0
- package/dist/policy/presets.d.ts +19 -0
- package/dist/policy/presets.js +66 -0
- package/dist/policy/registry.d.ts +7 -0
- package/dist/policy/registry.js +33 -0
- package/dist/policy/reload.d.ts +86 -0
- package/dist/policy/reload.js +233 -0
- package/dist/policy/render-yaml.d.ts +3 -0
- package/dist/policy/render-yaml.js +69 -0
- package/dist/policy/sandbox-bootstrap.d.ts +19 -0
- package/dist/policy/sandbox-bootstrap.js +66 -0
- package/dist/policy/schema.d.ts +204 -0
- package/dist/policy/schema.js +255 -0
- package/dist/policy/snapshot.d.ts +33 -0
- package/dist/policy/snapshot.js +23 -0
- package/dist/policy/validate-remote.d.ts +43 -0
- package/dist/policy/validate-remote.js +104 -0
- package/dist/policy/validate.d.ts +36 -0
- package/dist/policy/validate.js +150 -0
- package/dist/policy/watcher.d.ts +29 -0
- package/dist/policy/watcher.js +112 -0
- package/dist/principal-intent.d.ts +52 -0
- package/dist/principal-intent.js +193 -37
- package/dist/project-init.d.ts +34 -0
- package/dist/project-init.js +898 -0
- package/dist/sep2828-signature.d.ts +10 -0
- package/dist/sep2828-signature.js +134 -0
- package/dist/solutions/api.d.ts +22 -0
- package/dist/solutions/api.js +40 -0
- package/dist/solutions/catalog.d.ts +34 -0
- package/dist/solutions/catalog.js +129 -0
- package/dist/solutions/index.d.ts +2 -0
- package/dist/solutions/index.js +2 -0
- package/dist/template-init.d.ts +54 -0
- package/dist/template-init.js +203 -0
- package/dist/vercel-ai/config.d.ts +15 -0
- package/dist/vercel-ai/config.js +13 -0
- package/dist/vercel-ai/index.d.ts +4 -0
- package/dist/vercel-ai/index.js +4 -0
- package/dist/vercel-ai/sandbox-demo.d.ts +44 -0
- package/dist/vercel-ai/sandbox-demo.js +153 -0
- package/dist/vercel-ai/tool-approval.d.ts +16 -0
- package/dist/vercel-ai/tool-approval.js +41 -0
- package/dist/vercel-ai/wrap-tools.d.ts +9 -0
- package/dist/vercel-ai/wrap-tools.js +40 -0
- package/dist/x402-receipt-evidence.d.ts +29 -0
- package/dist/x402-receipt-evidence.js +97 -0
- package/dist/x402-receipt-signature.d.ts +12 -0
- package/dist/x402-receipt-signature.js +211 -0
- package/package.json +79 -4
- package/solutions/aws.json +17 -0
- package/solutions/saas.json +17 -0
- package/solutions/shopping.json +17 -0
- package/solutions/travel.json +18 -0
- package/templates/manifest.json +169 -0
- package/templates/openai-shopping-agent/.env.example +3 -0
- package/templates/openai-shopping-agent/.github/workflows/smoke.yml +20 -0
- package/templates/openai-shopping-agent/LICENSE +201 -0
- package/templates/openai-shopping-agent/README.md +29 -0
- package/templates/openai-shopping-agent/package.json +22 -0
- package/templates/openai-shopping-agent/paybond.policy.yaml +22 -0
- package/templates/openai-shopping-agent/src/index.ts +22 -0
- package/templates/openai-shopping-agent/src/paybond.config.ts +51 -0
- package/templates/openai-shopping-agent/tsconfig.json +13 -0
- package/templates/paybond-aws-operator/.env.example +3 -0
- package/templates/paybond-aws-operator/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-aws-operator/LICENSE +201 -0
- package/templates/paybond-aws-operator/README.md +29 -0
- package/templates/paybond-aws-operator/package.json +20 -0
- package/templates/paybond-aws-operator/paybond.policy.yaml +22 -0
- package/templates/paybond-aws-operator/src/index.ts +54 -0
- package/templates/paybond-aws-operator/src/paybond.config.ts +51 -0
- package/templates/paybond-aws-operator/tsconfig.json +13 -0
- package/templates/paybond-claude-agents-demo/.env.example +3 -0
- package/templates/paybond-claude-agents-demo/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-claude-agents-demo/LICENSE +201 -0
- package/templates/paybond-claude-agents-demo/README.md +29 -0
- package/templates/paybond-claude-agents-demo/package.json +22 -0
- package/templates/paybond-claude-agents-demo/paybond.policy.yaml +22 -0
- package/templates/paybond-claude-agents-demo/src/index.ts +22 -0
- package/templates/paybond-claude-agents-demo/src/paybond.config.ts +51 -0
- package/templates/paybond-claude-agents-demo/tsconfig.json +13 -0
- package/templates/paybond-invoice-agent/.env.example +3 -0
- package/templates/paybond-invoice-agent/.github/workflows/smoke.yml +19 -0
- package/templates/paybond-invoice-agent/LICENSE +201 -0
- package/templates/paybond-invoice-agent/README.md +29 -0
- package/templates/paybond-invoice-agent/app.py +27 -0
- package/templates/paybond-invoice-agent/package.json +6 -0
- package/templates/paybond-invoice-agent/paybond.policy.yaml +22 -0
- package/templates/paybond-invoice-agent/paybond_config.py +45 -0
- package/templates/paybond-invoice-agent/requirements.txt +2 -0
- package/templates/paybond-mcp-coding-agent/.env.example +3 -0
- package/templates/paybond-mcp-coding-agent/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-mcp-coding-agent/LICENSE +201 -0
- package/templates/paybond-mcp-coding-agent/README.md +39 -0
- package/templates/paybond-mcp-coding-agent/package.json +20 -0
- package/templates/paybond-mcp-coding-agent/paybond.policy.yaml +25 -0
- package/templates/paybond-mcp-coding-agent/src/index.ts +40 -0
- package/templates/paybond-mcp-coding-agent/src/paybond.config.ts +51 -0
- package/templates/paybond-mcp-coding-agent/tsconfig.json +13 -0
- package/templates/paybond-openai-agents-demo/.env.example +3 -0
- package/templates/paybond-openai-agents-demo/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-openai-agents-demo/LICENSE +201 -0
- package/templates/paybond-openai-agents-demo/README.md +29 -0
- package/templates/paybond-openai-agents-demo/package.json +22 -0
- package/templates/paybond-openai-agents-demo/paybond.policy.yaml +22 -0
- package/templates/paybond-openai-agents-demo/src/index.ts +22 -0
- package/templates/paybond-openai-agents-demo/src/paybond.config.ts +51 -0
- package/templates/paybond-openai-agents-demo/tsconfig.json +13 -0
- package/templates/paybond-procurement-agent/.env.example +3 -0
- package/templates/paybond-procurement-agent/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-procurement-agent/LICENSE +201 -0
- package/templates/paybond-procurement-agent/README.md +29 -0
- package/templates/paybond-procurement-agent/package.json +20 -0
- package/templates/paybond-procurement-agent/paybond.policy.yaml +25 -0
- package/templates/paybond-procurement-agent/src/index.ts +54 -0
- package/templates/paybond-procurement-agent/src/paybond.config.ts +51 -0
- package/templates/paybond-procurement-agent/tsconfig.json +13 -0
- package/templates/paybond-travel-agent/.env.example +3 -0
- package/templates/paybond-travel-agent/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-travel-agent/LICENSE +201 -0
- package/templates/paybond-travel-agent/README.md +29 -0
- package/templates/paybond-travel-agent/package.json +23 -0
- package/templates/paybond-travel-agent/paybond.policy.yaml +22 -0
- package/templates/paybond-travel-agent/src/index.ts +22 -0
- package/templates/paybond-travel-agent/src/paybond.config.ts +51 -0
- package/templates/paybond-travel-agent/tsconfig.json +13 -0
- package/templates/paybond-vercel-shopping-agent/.env.example +3 -0
- package/templates/paybond-vercel-shopping-agent/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-vercel-shopping-agent/LICENSE +201 -0
- package/templates/paybond-vercel-shopping-agent/README.md +29 -0
- package/templates/paybond-vercel-shopping-agent/package.json +22 -0
- package/templates/paybond-vercel-shopping-agent/paybond.policy.yaml +22 -0
- package/templates/paybond-vercel-shopping-agent/src/index.ts +22 -0
- package/templates/paybond-vercel-shopping-agent/src/paybond.config.ts +51 -0
- package/templates/paybond-vercel-shopping-agent/tsconfig.json +13 -0
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
4
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
5
|
+
import { scaffoldPolicyFromPreset } from "./policy/init.js";
|
|
6
|
+
import { getSolutionSmokeDefaults, isKnownSolutionId, loadSolutionManifest, } from "./solutions/catalog.js";
|
|
7
|
+
const SOLUTION_CHOICES = [
|
|
8
|
+
{ id: "shopping", label: "Shopping", presetId: "shopping" },
|
|
9
|
+
{ id: "travel", label: "Travel", presetId: "travel" },
|
|
10
|
+
{ id: "saas", label: "SaaS", presetId: "saas" },
|
|
11
|
+
{ id: "mcp-server", label: "MCP server", presetId: "travel" },
|
|
12
|
+
{ id: "aws", label: "AWS operator", presetId: "aws" },
|
|
13
|
+
];
|
|
14
|
+
const FRAMEWORK_CHOICES = [
|
|
15
|
+
{ id: "openai", label: "OpenAI Agents" },
|
|
16
|
+
{ id: "langgraph", label: "LangGraph" },
|
|
17
|
+
{ id: "mcp", label: "MCP" },
|
|
18
|
+
{ id: "generic", label: "Generic" },
|
|
19
|
+
];
|
|
20
|
+
const SOLUTION_ALIASES = {
|
|
21
|
+
shopping: "shopping",
|
|
22
|
+
shop: "shopping",
|
|
23
|
+
travel: "travel",
|
|
24
|
+
saas: "saas",
|
|
25
|
+
"mcp-server": "mcp-server",
|
|
26
|
+
mcp: "mcp-server",
|
|
27
|
+
aws: "aws",
|
|
28
|
+
"aws-operator": "aws",
|
|
29
|
+
};
|
|
30
|
+
const FRAMEWORK_ALIASES = {
|
|
31
|
+
openai: "openai",
|
|
32
|
+
"openai-agents": "openai",
|
|
33
|
+
langgraph: "langgraph",
|
|
34
|
+
mcp: "mcp",
|
|
35
|
+
generic: "generic",
|
|
36
|
+
};
|
|
37
|
+
function usage() {
|
|
38
|
+
return [
|
|
39
|
+
"Usage: paybond init [--template <id>|--repo <slug>] [--force]",
|
|
40
|
+
" paybond init [--solution shopping|travel|saas|mcp-server|aws] [--max-spend-usd <n>] [--framework openai|langgraph|mcp|generic] [--language typescript|python] [--non-interactive] [--force]",
|
|
41
|
+
"",
|
|
42
|
+
"Interactive first-run scaffold: solution bundle policy, client bootstrap, framework instrument stub, .env.example, and npm smoke script.",
|
|
43
|
+
"",
|
|
44
|
+
"Examples:",
|
|
45
|
+
" paybond init --template travel-agent",
|
|
46
|
+
" paybond init",
|
|
47
|
+
" paybond init --solution travel --max-spend-usd 500 --framework langgraph --non-interactive",
|
|
48
|
+
].join("\n");
|
|
49
|
+
}
|
|
50
|
+
function normalizeSolution(raw) {
|
|
51
|
+
const normalized = SOLUTION_ALIASES[raw.trim().toLowerCase()];
|
|
52
|
+
if (!normalized) {
|
|
53
|
+
throw new Error(`invalid --solution: ${raw}`);
|
|
54
|
+
}
|
|
55
|
+
return normalized;
|
|
56
|
+
}
|
|
57
|
+
function normalizeFramework(raw) {
|
|
58
|
+
const normalized = FRAMEWORK_ALIASES[raw.trim().toLowerCase()];
|
|
59
|
+
if (!normalized) {
|
|
60
|
+
throw new Error(`invalid --framework: ${raw}`);
|
|
61
|
+
}
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
64
|
+
function normalizeLanguage(raw) {
|
|
65
|
+
const value = raw.trim().toLowerCase();
|
|
66
|
+
if (value === "typescript" || value === "ts") {
|
|
67
|
+
return "typescript";
|
|
68
|
+
}
|
|
69
|
+
if (value === "python" || value === "py") {
|
|
70
|
+
return "python";
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`invalid --language: ${raw}`);
|
|
73
|
+
}
|
|
74
|
+
function presetIdForSolution(solution) {
|
|
75
|
+
return SOLUTION_CHOICES.find((entry) => entry.id === solution)?.presetId ?? "travel";
|
|
76
|
+
}
|
|
77
|
+
function defaultMaxSpendUsd(solution) {
|
|
78
|
+
const presetId = presetIdForSolution(solution);
|
|
79
|
+
if (!isKnownSolutionId(presetId)) {
|
|
80
|
+
return 200;
|
|
81
|
+
}
|
|
82
|
+
const manifest = loadSolutionManifest(presetId);
|
|
83
|
+
for (const guardrail of manifest.policy_default.guardrails) {
|
|
84
|
+
const match = guardrail.match(/^max_spend_usd_(\d+)$/);
|
|
85
|
+
if (match) {
|
|
86
|
+
return Number(match[1]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return 200;
|
|
90
|
+
}
|
|
91
|
+
function defaultFrameworkForSolution(solution) {
|
|
92
|
+
return solution === "mcp-server" ? "mcp" : "generic";
|
|
93
|
+
}
|
|
94
|
+
async function detectLanguage(cwd) {
|
|
95
|
+
try {
|
|
96
|
+
await access(`${cwd}/pyproject.toml`, constants.F_OK);
|
|
97
|
+
return "python";
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// continue
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await access(`${cwd}/requirements.txt`, constants.F_OK);
|
|
104
|
+
return "python";
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// continue
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
await access(`${cwd}/package.json`, constants.F_OK);
|
|
111
|
+
return "typescript";
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return "typescript";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function parsePositiveUsd(raw) {
|
|
118
|
+
const value = Number(raw.trim());
|
|
119
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
120
|
+
throw new Error("max spend must be a positive number");
|
|
121
|
+
}
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
export function parseProjectInitArgv(argv) {
|
|
125
|
+
let solution;
|
|
126
|
+
let maxSpendUsd;
|
|
127
|
+
let framework;
|
|
128
|
+
let language;
|
|
129
|
+
let template;
|
|
130
|
+
let nonInteractive = false;
|
|
131
|
+
let force = false;
|
|
132
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
133
|
+
const arg = argv[i];
|
|
134
|
+
if (arg === "--help" || arg === "-h") {
|
|
135
|
+
return "help";
|
|
136
|
+
}
|
|
137
|
+
if (arg === "--non-interactive") {
|
|
138
|
+
nonInteractive = true;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (arg === "--force") {
|
|
142
|
+
force = true;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (arg === "--template" || arg === "--repo") {
|
|
146
|
+
const raw = argv[i + 1];
|
|
147
|
+
i += 1;
|
|
148
|
+
if (!raw) {
|
|
149
|
+
throw new Error("invalid --template");
|
|
150
|
+
}
|
|
151
|
+
template = raw;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (arg === "--solution") {
|
|
155
|
+
const raw = argv[i + 1];
|
|
156
|
+
i += 1;
|
|
157
|
+
if (!raw) {
|
|
158
|
+
throw new Error("invalid --solution");
|
|
159
|
+
}
|
|
160
|
+
solution = normalizeSolution(raw);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (arg === "--max-spend-usd" || arg === "--max-spend") {
|
|
164
|
+
const raw = argv[i + 1];
|
|
165
|
+
i += 1;
|
|
166
|
+
if (!raw) {
|
|
167
|
+
throw new Error("invalid --max-spend-usd");
|
|
168
|
+
}
|
|
169
|
+
maxSpendUsd = parsePositiveUsd(raw);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (arg === "--framework") {
|
|
173
|
+
const raw = argv[i + 1];
|
|
174
|
+
i += 1;
|
|
175
|
+
if (!raw) {
|
|
176
|
+
throw new Error("invalid --framework");
|
|
177
|
+
}
|
|
178
|
+
framework = normalizeFramework(raw);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (arg === "--language") {
|
|
182
|
+
const raw = argv[i + 1];
|
|
183
|
+
i += 1;
|
|
184
|
+
if (!raw) {
|
|
185
|
+
throw new Error("invalid --language");
|
|
186
|
+
}
|
|
187
|
+
language = normalizeLanguage(raw);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
solution,
|
|
194
|
+
maxSpendUsd,
|
|
195
|
+
framework,
|
|
196
|
+
language,
|
|
197
|
+
template,
|
|
198
|
+
nonInteractive,
|
|
199
|
+
force,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
async function defaultPrompt(question) {
|
|
203
|
+
const rl = createInterface({ input, output });
|
|
204
|
+
try {
|
|
205
|
+
return (await rl.question(question)).trim();
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
rl.close();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function formatChoices(choices) {
|
|
212
|
+
return choices.map((choice, index) => `[${index + 1}] ${choice.label}`).join(" ");
|
|
213
|
+
}
|
|
214
|
+
function resolveChoiceIndex(raw, choices, fallbackIndex) {
|
|
215
|
+
const trimmed = raw.trim();
|
|
216
|
+
if (!trimmed) {
|
|
217
|
+
return fallbackIndex;
|
|
218
|
+
}
|
|
219
|
+
const numeric = Number(trimmed);
|
|
220
|
+
if (Number.isInteger(numeric) && numeric >= 1 && numeric <= choices.length) {
|
|
221
|
+
return numeric - 1;
|
|
222
|
+
}
|
|
223
|
+
const lowered = trimmed.toLowerCase();
|
|
224
|
+
const byLabel = choices.findIndex((choice) => {
|
|
225
|
+
const label = choice.label?.toLowerCase();
|
|
226
|
+
const id = choice.id?.toLowerCase();
|
|
227
|
+
return label === lowered || id === lowered || label?.replace(/\s+/g, "-") === lowered;
|
|
228
|
+
});
|
|
229
|
+
if (byLabel >= 0) {
|
|
230
|
+
return byLabel;
|
|
231
|
+
}
|
|
232
|
+
throw new Error(`invalid choice: ${raw}`);
|
|
233
|
+
}
|
|
234
|
+
async function resolveInteractiveOptions(options) {
|
|
235
|
+
const prompt = options.prompt ?? defaultPrompt;
|
|
236
|
+
const interactive = !options.nonInteractive && input.isTTY === true && output.isTTY === true;
|
|
237
|
+
const language = options.language ?? (await detectLanguage(options.cwd));
|
|
238
|
+
let solution = options.solution;
|
|
239
|
+
if (!solution) {
|
|
240
|
+
if (!interactive) {
|
|
241
|
+
solution = "travel";
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
options.writeStdout?.(`What are you building? ${formatChoices(SOLUTION_CHOICES)}\n`);
|
|
245
|
+
const answer = await prompt("> ");
|
|
246
|
+
solution = SOLUTION_CHOICES[resolveChoiceIndex(answer, SOLUTION_CHOICES, 1)].id;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
let maxSpendUsd = options.maxSpendUsd ?? defaultMaxSpendUsd(solution);
|
|
250
|
+
if (options.maxSpendUsd === undefined && interactive) {
|
|
251
|
+
const answer = await prompt(`Maximum spend? [$${maxSpendUsd}] `);
|
|
252
|
+
if (answer.trim()) {
|
|
253
|
+
maxSpendUsd = parsePositiveUsd(answer);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
let framework = options.framework ?? defaultFrameworkForSolution(solution);
|
|
257
|
+
if (options.framework === undefined && solution !== "mcp-server" && interactive) {
|
|
258
|
+
options.writeStdout?.(`Framework? ${formatChoices(FRAMEWORK_CHOICES)}\n`);
|
|
259
|
+
const answer = await prompt("> ");
|
|
260
|
+
framework = FRAMEWORK_CHOICES[resolveChoiceIndex(answer, FRAMEWORK_CHOICES, 3)].id;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
solution,
|
|
264
|
+
maxSpendUsd,
|
|
265
|
+
framework,
|
|
266
|
+
language,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
async function writeFileIfAllowed(path, body, force) {
|
|
270
|
+
try {
|
|
271
|
+
await access(path, constants.F_OK);
|
|
272
|
+
if (!force) {
|
|
273
|
+
throw new Error(`${path} already exists (pass --force to overwrite)`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
if (err instanceof Error && err.message.includes("already exists")) {
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
await writeFile(path, body, "utf8");
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
function smokeCommandFor(presetId) {
|
|
285
|
+
const preset = isKnownSolutionId(presetId) ? presetId : "travel";
|
|
286
|
+
const defaults = getSolutionSmokeDefaults(preset);
|
|
287
|
+
const resultBody = JSON.stringify(defaults.resultBody);
|
|
288
|
+
return [
|
|
289
|
+
"paybond agent sandbox smoke",
|
|
290
|
+
"--policy-file paybond.policy.yaml",
|
|
291
|
+
`--operation ${defaults.operation}`,
|
|
292
|
+
`--requested-spend-cents ${defaults.requestedSpendCents}`,
|
|
293
|
+
`--evidence-preset ${defaults.evidencePreset}`,
|
|
294
|
+
`--result-body '${resultBody}'`,
|
|
295
|
+
"--format json",
|
|
296
|
+
].join(" ");
|
|
297
|
+
}
|
|
298
|
+
function envExampleBody() {
|
|
299
|
+
return [
|
|
300
|
+
"# Copy to .env.local after paybond login",
|
|
301
|
+
"PAYBOND_API_KEY=",
|
|
302
|
+
"PAYBOND_GATEWAY_URL=https://api.paybond.ai",
|
|
303
|
+
"",
|
|
304
|
+
].join("\n");
|
|
305
|
+
}
|
|
306
|
+
function typescriptConfigTemplate() {
|
|
307
|
+
return `import { Paybond } from "@paybond/kit";
|
|
308
|
+
|
|
309
|
+
declare const process: {
|
|
310
|
+
env: Record<string, string | undefined>;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
function readEnvValue(body: string, key: string): string | undefined {
|
|
314
|
+
const pattern = new RegExp("^\\\\s*(?:export\\\\s+)?" + key + "\\\\s*=\\\\s*(.*)$", "m");
|
|
315
|
+
const match = body.match(pattern);
|
|
316
|
+
if (!match) return undefined;
|
|
317
|
+
let value = (match[1] ?? "").trim();
|
|
318
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
319
|
+
try {
|
|
320
|
+
value = JSON.parse(value);
|
|
321
|
+
} catch {
|
|
322
|
+
value = value.slice(1, -1);
|
|
323
|
+
}
|
|
324
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
325
|
+
value = value.slice(1, -1);
|
|
326
|
+
}
|
|
327
|
+
return value.trim() || undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export async function loadPaybondEnvFile(envFile = ".env.local"): Promise<void> {
|
|
331
|
+
if (process.env.PAYBOND_API_KEY?.trim()) return;
|
|
332
|
+
let body: string;
|
|
333
|
+
try {
|
|
334
|
+
const { readFile } = await import("node:fs/promises");
|
|
335
|
+
body = await readFile(envFile, "utf8");
|
|
336
|
+
} catch (err) {
|
|
337
|
+
if ((err as { code?: unknown })?.code === "ENOENT") return;
|
|
338
|
+
throw err;
|
|
339
|
+
}
|
|
340
|
+
const apiKey = readEnvValue(body, "PAYBOND_API_KEY");
|
|
341
|
+
if (apiKey) {
|
|
342
|
+
process.env.PAYBOND_API_KEY = apiKey;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export async function createPaybondClient(): Promise<Paybond> {
|
|
347
|
+
await loadPaybondEnvFile(".env.local");
|
|
348
|
+
const apiKey = process.env.PAYBOND_API_KEY?.trim();
|
|
349
|
+
if (!apiKey) {
|
|
350
|
+
throw new Error("PAYBOND_API_KEY is required; run paybond login");
|
|
351
|
+
}
|
|
352
|
+
return Paybond.open({
|
|
353
|
+
apiKey,
|
|
354
|
+
gatewayBaseUrl: process.env.PAYBOND_GATEWAY_URL ?? process.env.PAYBOND_GATEWAY_BASE_URL,
|
|
355
|
+
expectedEnvironment: "sandbox",
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
function pythonConfigTemplate() {
|
|
361
|
+
return `import os
|
|
362
|
+
from pathlib import Path
|
|
363
|
+
|
|
364
|
+
from paybond_kit import Paybond
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _read_env_value(body: str, key: str) -> str | None:
|
|
368
|
+
for raw_line in body.splitlines():
|
|
369
|
+
line = raw_line.strip()
|
|
370
|
+
for prefix in (f"export {key}=", f"{key}="):
|
|
371
|
+
if line.startswith(prefix):
|
|
372
|
+
value = line[len(prefix):].strip()
|
|
373
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\\"":
|
|
374
|
+
value = value[1:-1]
|
|
375
|
+
return value.strip() or None
|
|
376
|
+
return None
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def load_paybond_env_file(env_file: str = ".env.local") -> None:
|
|
380
|
+
if os.environ.get("PAYBOND_API_KEY", "").strip():
|
|
381
|
+
return
|
|
382
|
+
path = Path(env_file)
|
|
383
|
+
try:
|
|
384
|
+
body = path.read_text(encoding="utf-8")
|
|
385
|
+
except FileNotFoundError:
|
|
386
|
+
return
|
|
387
|
+
api_key = _read_env_value(body, "PAYBOND_API_KEY")
|
|
388
|
+
if api_key:
|
|
389
|
+
os.environ["PAYBOND_API_KEY"] = api_key
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
async def create_paybond_client() -> Paybond:
|
|
393
|
+
load_paybond_env_file(".env.local")
|
|
394
|
+
api_key = os.environ.get("PAYBOND_API_KEY", "").strip()
|
|
395
|
+
if not api_key:
|
|
396
|
+
raise RuntimeError("PAYBOND_API_KEY is required; run paybond login")
|
|
397
|
+
return await Paybond.open(
|
|
398
|
+
api_key=api_key,
|
|
399
|
+
gateway_base_url=(
|
|
400
|
+
os.environ.get("PAYBOND_GATEWAY_URL")
|
|
401
|
+
or os.environ.get("PAYBOND_GATEWAY_BASE_URL")
|
|
402
|
+
or "https://api.paybond.ai"
|
|
403
|
+
),
|
|
404
|
+
expected_environment="sandbox",
|
|
405
|
+
)
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
408
|
+
function toolStubsForSolution(solution) {
|
|
409
|
+
switch (solution) {
|
|
410
|
+
case "shopping":
|
|
411
|
+
return {
|
|
412
|
+
primaryName: "commerce.checkout",
|
|
413
|
+
primaryArgsType: "{ sku: string; quantity: number; estimatedTotalCents: number }",
|
|
414
|
+
primaryHandler: `async function checkout(args: { sku: string; quantity: number; estimatedTotalCents: number }) {
|
|
415
|
+
return {
|
|
416
|
+
order: {
|
|
417
|
+
status: "completed" as const,
|
|
418
|
+
total_cents: args.estimatedTotalCents,
|
|
419
|
+
sku: args.sku,
|
|
420
|
+
quantity: args.quantity,
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}`,
|
|
424
|
+
secondaryName: "search.products",
|
|
425
|
+
secondaryHandler: `async function searchProducts(args: { query: string }) {
|
|
426
|
+
return { products: [{ title: args.query, sku: "SKU-001" }] };
|
|
427
|
+
}`,
|
|
428
|
+
registryPrimary: `"commerce.checkout": {
|
|
429
|
+
spendCents: (args: { estimatedTotalCents: number }) => args.estimatedTotalCents,
|
|
430
|
+
evidencePreset: COMPLETION_PRESET_ID,
|
|
431
|
+
evidenceMapper: (result: Awaited<ReturnType<typeof checkout>>) => ({
|
|
432
|
+
status: result.order.status,
|
|
433
|
+
cost_cents: result.order.total_cents,
|
|
434
|
+
}),
|
|
435
|
+
}`,
|
|
436
|
+
toolMap: `{
|
|
437
|
+
"commerce.checkout": async (args: { sku: string; quantity: number; estimatedTotalCents: number }) =>
|
|
438
|
+
checkout(args),
|
|
439
|
+
searchProducts: async (args: { query: string }) => searchProducts(args),
|
|
440
|
+
}`,
|
|
441
|
+
};
|
|
442
|
+
case "saas":
|
|
443
|
+
return {
|
|
444
|
+
primaryName: "saas.provision_seat",
|
|
445
|
+
primaryArgsType: "{ planId: string; seats: number; monthlyCents: number }",
|
|
446
|
+
primaryHandler: `async function provisionSeat(args: { planId: string; seats: number; monthlyCents: number }) {
|
|
447
|
+
return {
|
|
448
|
+
subscription: {
|
|
449
|
+
status: "active" as const,
|
|
450
|
+
plan_id: args.planId,
|
|
451
|
+
seats: args.seats,
|
|
452
|
+
monthly_cents: args.monthlyCents,
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
}`,
|
|
456
|
+
secondaryName: "saas.list_plans",
|
|
457
|
+
secondaryHandler: `async function listPlans() {
|
|
458
|
+
return { plans: [{ id: "pro", monthly_cents: 2900 }] };
|
|
459
|
+
}`,
|
|
460
|
+
registryPrimary: `"saas.provision_seat": {
|
|
461
|
+
spendCents: (args: { monthlyCents: number }) => args.monthlyCents,
|
|
462
|
+
evidencePreset: COMPLETION_PRESET_ID,
|
|
463
|
+
evidenceMapper: (result: Awaited<ReturnType<typeof provisionSeat>>) => ({
|
|
464
|
+
status: "completed",
|
|
465
|
+
cost_cents: result.subscription.monthly_cents,
|
|
466
|
+
}),
|
|
467
|
+
}`,
|
|
468
|
+
toolMap: `{
|
|
469
|
+
"saas.provision_seat": async (args: { planId: string; seats: number; monthlyCents: number }) =>
|
|
470
|
+
provisionSeat(args),
|
|
471
|
+
listPlans: async () => listPlans(),
|
|
472
|
+
}`,
|
|
473
|
+
};
|
|
474
|
+
case "aws":
|
|
475
|
+
return {
|
|
476
|
+
primaryName: "aws.ec2.start_instance",
|
|
477
|
+
primaryArgsType: "{ instanceId: string; estimatedHourlyCents: number }",
|
|
478
|
+
primaryHandler: `async function startInstance(args: { instanceId: string; estimatedHourlyCents: number }) {
|
|
479
|
+
return {
|
|
480
|
+
instance: {
|
|
481
|
+
id: args.instanceId,
|
|
482
|
+
state: "running" as const,
|
|
483
|
+
hourly_cents: args.estimatedHourlyCents,
|
|
484
|
+
},
|
|
485
|
+
};
|
|
486
|
+
}`,
|
|
487
|
+
secondaryName: "aws.ec2.describe_instances",
|
|
488
|
+
secondaryHandler: `async function describeInstances() {
|
|
489
|
+
return { instances: [{ id: "i-abc123", state: "stopped" }] };
|
|
490
|
+
}`,
|
|
491
|
+
registryPrimary: `"aws.ec2.start_instance": {
|
|
492
|
+
spendCents: (args: { estimatedHourlyCents: number }) => args.estimatedHourlyCents,
|
|
493
|
+
evidencePreset: COMPLETION_PRESET_ID,
|
|
494
|
+
evidenceMapper: (result: Awaited<ReturnType<typeof startInstance>>) => ({
|
|
495
|
+
status: "completed",
|
|
496
|
+
cost_cents: result.instance.hourly_cents,
|
|
497
|
+
}),
|
|
498
|
+
}`,
|
|
499
|
+
toolMap: `{
|
|
500
|
+
"aws.ec2.start_instance": async (args: { instanceId: string; estimatedHourlyCents: number }) =>
|
|
501
|
+
startInstance(args),
|
|
502
|
+
describeInstances: async () => describeInstances(),
|
|
503
|
+
}`,
|
|
504
|
+
};
|
|
505
|
+
case "mcp-server":
|
|
506
|
+
case "travel":
|
|
507
|
+
default:
|
|
508
|
+
return {
|
|
509
|
+
primaryName: "travel.book_hotel",
|
|
510
|
+
primaryArgsType: "{ city: string; estimatedPriceCents: number }",
|
|
511
|
+
primaryHandler: `async function bookHotel(args: { city: string; estimatedPriceCents: number }) {
|
|
512
|
+
return {
|
|
513
|
+
reservation: {
|
|
514
|
+
status: "confirmed" as const,
|
|
515
|
+
price_cents: args.estimatedPriceCents,
|
|
516
|
+
city: args.city,
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
}`,
|
|
520
|
+
secondaryName: "search.web",
|
|
521
|
+
secondaryHandler: `async function searchWeb(args: { query: string }) {
|
|
522
|
+
return { hits: [{ title: args.query, url: "https://example.com" }] };
|
|
523
|
+
}`,
|
|
524
|
+
registryPrimary: `"travel.book_hotel": {
|
|
525
|
+
spendCents: (args: { estimatedPriceCents: number }) => args.estimatedPriceCents,
|
|
526
|
+
evidencePreset: COMPLETION_PRESET_ID,
|
|
527
|
+
evidenceMapper: (result: Awaited<ReturnType<typeof bookHotel>>) => ({
|
|
528
|
+
status: result.reservation.status === "confirmed" ? "completed" : result.reservation.status,
|
|
529
|
+
cost_cents: result.reservation.price_cents,
|
|
530
|
+
}),
|
|
531
|
+
}`,
|
|
532
|
+
toolMap: `{
|
|
533
|
+
"travel.book_hotel": async (args: { city: string; estimatedPriceCents: number }) => bookHotel(args),
|
|
534
|
+
searchWeb: async (args: { query: string }) => searchWeb(args),
|
|
535
|
+
}`,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function instrumentMethod(framework) {
|
|
540
|
+
switch (framework) {
|
|
541
|
+
case "openai":
|
|
542
|
+
return "instrumentOpenAI";
|
|
543
|
+
case "langgraph":
|
|
544
|
+
return "instrumentLangGraph";
|
|
545
|
+
case "mcp":
|
|
546
|
+
return "instrumentMCP";
|
|
547
|
+
default:
|
|
548
|
+
return "instrument";
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function typescriptInstrumentTemplate(solution, framework, maxSpendUsd) {
|
|
552
|
+
const presetId = presetIdForSolution(solution);
|
|
553
|
+
const manifest = isKnownSolutionId(presetId) ? loadSolutionManifest(presetId) : null;
|
|
554
|
+
const completionPreset = manifest?.completion_preset ?? "cost_and_completion";
|
|
555
|
+
const primaryOperation = manifest?.primary_operation ?? "travel.book_hotel";
|
|
556
|
+
const stubs = toolStubsForSolution(solution);
|
|
557
|
+
const requestedSpendCents = Math.round(maxSpendUsd * 100);
|
|
558
|
+
return `import { createPaybondClient } from "./paybond.config.js";
|
|
559
|
+
import { Paybond, createPaybondToolRegistry } from "@paybond/kit";
|
|
560
|
+
|
|
561
|
+
const POLICY_FILE = "./paybond.policy.yaml";
|
|
562
|
+
const COMPLETION_PRESET_ID = "${completionPreset}";
|
|
563
|
+
const DEFAULT_OPERATION = "${primaryOperation}";
|
|
564
|
+
const DEFAULT_REQUESTED_SPEND_CENTS = ${requestedSpendCents};
|
|
565
|
+
|
|
566
|
+
${stubs.primaryHandler}
|
|
567
|
+
|
|
568
|
+
${stubs.secondaryHandler}
|
|
569
|
+
|
|
570
|
+
export function createAgentToolRegistry() {
|
|
571
|
+
return createPaybondToolRegistry({
|
|
572
|
+
sideEffecting: {
|
|
573
|
+
${stubs.registryPrimary},
|
|
574
|
+
},
|
|
575
|
+
defaultDeny: true,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export async function createInstrumentedAgent() {
|
|
580
|
+
const paybond = await createPaybondClient();
|
|
581
|
+
const tools = ${stubs.toolMap};
|
|
582
|
+
return paybond.${instrumentMethod(framework)}({
|
|
583
|
+
policy: POLICY_FILE,
|
|
584
|
+
tools,
|
|
585
|
+
sandbox: true,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Sandbox bind helper when you need an explicit PaybondAgentRun. */
|
|
590
|
+
export async function bindSandboxAgentRun() {
|
|
591
|
+
const paybond = await createPaybondClient();
|
|
592
|
+
return paybond.agentRun.bind({
|
|
593
|
+
bootstrap: {
|
|
594
|
+
kind: "sandbox",
|
|
595
|
+
operation: DEFAULT_OPERATION,
|
|
596
|
+
requestedSpendCents: DEFAULT_REQUESTED_SPEND_CENTS,
|
|
597
|
+
completionPreset: COMPLETION_PRESET_ID,
|
|
598
|
+
},
|
|
599
|
+
registry: createAgentToolRegistry(),
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
`;
|
|
603
|
+
}
|
|
604
|
+
function pythonInstrumentTemplate(solution, framework, maxSpendUsd) {
|
|
605
|
+
const presetId = presetIdForSolution(solution);
|
|
606
|
+
const manifest = isKnownSolutionId(presetId) ? loadSolutionManifest(presetId) : null;
|
|
607
|
+
const completionPreset = manifest?.completion_preset ?? "cost_and_completion";
|
|
608
|
+
const primaryOperation = manifest?.primary_operation ?? "travel.book_hotel";
|
|
609
|
+
const requestedSpendCents = Math.round(maxSpendUsd * 100);
|
|
610
|
+
const instrumentCall = framework === "generic"
|
|
611
|
+
? "await paybond.instrument(policy=POLICY_FILE, tools=tools, sandbox=True)"
|
|
612
|
+
: framework === "langgraph"
|
|
613
|
+
? "await paybond.instrument_langgraph(policy=POLICY_FILE, tools=tools, sandbox=True)"
|
|
614
|
+
: framework === "openai"
|
|
615
|
+
? "await paybond.instrument_openai(policy=POLICY_FILE, tools=tools, sandbox=True)"
|
|
616
|
+
: "await paybond.instrument_mcp(policy=POLICY_FILE, tools=tools, sandbox=True)";
|
|
617
|
+
const toolBlock = (() => {
|
|
618
|
+
switch (solution) {
|
|
619
|
+
case "shopping":
|
|
620
|
+
return `async def checkout(args: dict[str, object]) -> dict[str, object]:
|
|
621
|
+
total_cents = int(args["estimated_total_cents"])
|
|
622
|
+
return {
|
|
623
|
+
"order": {
|
|
624
|
+
"status": "completed",
|
|
625
|
+
"total_cents": total_cents,
|
|
626
|
+
"sku": str(args["sku"]),
|
|
627
|
+
"quantity": int(args["quantity"]),
|
|
628
|
+
},
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
async def search_products(args: dict[str, object]) -> dict[str, object]:
|
|
633
|
+
return {"products": [{"title": str(args["query"]), "sku": "SKU-001"}]}
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def create_agent_tool_registry() -> object:
|
|
637
|
+
return create_paybond_tool_registry(
|
|
638
|
+
{
|
|
639
|
+
"side_effecting": {
|
|
640
|
+
"commerce.checkout": {
|
|
641
|
+
"spend_cents": lambda args: int(args["estimated_total_cents"]),
|
|
642
|
+
"evidence_preset": COMPLETION_PRESET_ID,
|
|
643
|
+
"evidence_mapper": lambda result, _ctx: {
|
|
644
|
+
"status": result["order"]["status"],
|
|
645
|
+
"cost_cents": result["order"]["total_cents"],
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
"default_deny": True,
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
TOOLS = {
|
|
655
|
+
"commerce.checkout": checkout,
|
|
656
|
+
"search.products": search_products,
|
|
657
|
+
}`;
|
|
658
|
+
case "saas":
|
|
659
|
+
return `async def provision_seat(args: dict[str, object]) -> dict[str, object]:
|
|
660
|
+
monthly_cents = int(args["monthly_cents"])
|
|
661
|
+
return {
|
|
662
|
+
"subscription": {
|
|
663
|
+
"status": "active",
|
|
664
|
+
"plan_id": str(args["plan_id"]),
|
|
665
|
+
"seats": int(args["seats"]),
|
|
666
|
+
"monthly_cents": monthly_cents,
|
|
667
|
+
},
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
async def list_plans() -> dict[str, object]:
|
|
672
|
+
return {"plans": [{"id": "pro", "monthly_cents": 2900}]}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def create_agent_tool_registry() -> object:
|
|
676
|
+
return create_paybond_tool_registry(
|
|
677
|
+
{
|
|
678
|
+
"side_effecting": {
|
|
679
|
+
"saas.provision_seat": {
|
|
680
|
+
"spend_cents": lambda args: int(args["monthly_cents"]),
|
|
681
|
+
"evidence_preset": COMPLETION_PRESET_ID,
|
|
682
|
+
"evidence_mapper": lambda result, _ctx: {
|
|
683
|
+
"status": "completed",
|
|
684
|
+
"cost_cents": result["subscription"]["monthly_cents"],
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
"default_deny": True,
|
|
689
|
+
}
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
TOOLS = {
|
|
694
|
+
"saas.provision_seat": provision_seat,
|
|
695
|
+
"saas.list_plans": list_plans,
|
|
696
|
+
}`;
|
|
697
|
+
case "aws":
|
|
698
|
+
return `async def start_instance(args: dict[str, object]) -> dict[str, object]:
|
|
699
|
+
hourly_cents = int(args["estimated_hourly_cents"])
|
|
700
|
+
return {
|
|
701
|
+
"instance": {
|
|
702
|
+
"id": str(args["instance_id"]),
|
|
703
|
+
"state": "running",
|
|
704
|
+
"hourly_cents": hourly_cents,
|
|
705
|
+
},
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
async def describe_instances() -> dict[str, object]:
|
|
710
|
+
return {"instances": [{"id": "i-abc123", "state": "stopped"}]}
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def create_agent_tool_registry() -> object:
|
|
714
|
+
return create_paybond_tool_registry(
|
|
715
|
+
{
|
|
716
|
+
"side_effecting": {
|
|
717
|
+
"aws.ec2.start_instance": {
|
|
718
|
+
"spend_cents": lambda args: int(args["estimated_hourly_cents"]),
|
|
719
|
+
"evidence_preset": COMPLETION_PRESET_ID,
|
|
720
|
+
"evidence_mapper": lambda result, _ctx: {
|
|
721
|
+
"status": "completed",
|
|
722
|
+
"cost_cents": result["instance"]["hourly_cents"],
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
"default_deny": True,
|
|
727
|
+
}
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
TOOLS = {
|
|
732
|
+
"aws.ec2.start_instance": start_instance,
|
|
733
|
+
"aws.ec2.describe_instances": describe_instances,
|
|
734
|
+
}`;
|
|
735
|
+
default:
|
|
736
|
+
return `async def book_hotel(args: dict[str, object]) -> dict[str, object]:
|
|
737
|
+
price_cents = int(args["estimated_price_cents"])
|
|
738
|
+
return {
|
|
739
|
+
"reservation": {
|
|
740
|
+
"status": "confirmed",
|
|
741
|
+
"price_cents": price_cents,
|
|
742
|
+
"city": str(args["city"]),
|
|
743
|
+
},
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
async def search_web(args: dict[str, object]) -> dict[str, object]:
|
|
748
|
+
return {"hits": [{"title": str(args["query"]), "url": "https://example.com"}]}
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def create_agent_tool_registry() -> object:
|
|
752
|
+
return create_paybond_tool_registry(
|
|
753
|
+
{
|
|
754
|
+
"side_effecting": {
|
|
755
|
+
"travel.book_hotel": {
|
|
756
|
+
"spend_cents": lambda args: int(args["estimated_price_cents"]),
|
|
757
|
+
"evidence_preset": COMPLETION_PRESET_ID,
|
|
758
|
+
"evidence_mapper": lambda result, _ctx: {
|
|
759
|
+
"status": (
|
|
760
|
+
"completed"
|
|
761
|
+
if result["reservation"]["status"] == "confirmed"
|
|
762
|
+
else result["reservation"]["status"]
|
|
763
|
+
),
|
|
764
|
+
"cost_cents": result["reservation"]["price_cents"],
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
"default_deny": True,
|
|
769
|
+
}
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
TOOLS = {
|
|
774
|
+
"travel.book_hotel": book_hotel,
|
|
775
|
+
"search.web": search_web,
|
|
776
|
+
}`;
|
|
777
|
+
}
|
|
778
|
+
})();
|
|
779
|
+
return `from paybond_config import create_paybond_client
|
|
780
|
+
from paybond_kit.agent import create_paybond_tool_registry
|
|
781
|
+
|
|
782
|
+
POLICY_FILE = "./paybond.policy.yaml"
|
|
783
|
+
COMPLETION_PRESET_ID = "${completionPreset}"
|
|
784
|
+
DEFAULT_OPERATION = "${primaryOperation}"
|
|
785
|
+
DEFAULT_REQUESTED_SPEND_CENTS = ${requestedSpendCents}
|
|
786
|
+
|
|
787
|
+
${toolBlock}
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
async def create_instrumented_agent():
|
|
791
|
+
paybond = await create_paybond_client()
|
|
792
|
+
tools = TOOLS
|
|
793
|
+
return ${instrumentCall}
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
async def bind_sandbox_agent_run():
|
|
797
|
+
paybond = await create_paybond_client()
|
|
798
|
+
return await paybond.agent_run.bind(
|
|
799
|
+
{
|
|
800
|
+
"bootstrap": {
|
|
801
|
+
"kind": "sandbox",
|
|
802
|
+
"operation": DEFAULT_OPERATION,
|
|
803
|
+
"requested_spend_cents": DEFAULT_REQUESTED_SPEND_CENTS,
|
|
804
|
+
"completion_preset": COMPLETION_PRESET_ID,
|
|
805
|
+
},
|
|
806
|
+
"registry": create_agent_tool_registry(),
|
|
807
|
+
}
|
|
808
|
+
)
|
|
809
|
+
`;
|
|
810
|
+
}
|
|
811
|
+
async function upsertPackageJsonSmokeScript(cwd, smokeCommand, force) {
|
|
812
|
+
const packageJsonPath = `${cwd}/package.json`;
|
|
813
|
+
let existing = null;
|
|
814
|
+
try {
|
|
815
|
+
existing = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
existing = null;
|
|
819
|
+
}
|
|
820
|
+
if (!existing) {
|
|
821
|
+
const body = `${JSON.stringify({
|
|
822
|
+
private: true,
|
|
823
|
+
type: "module",
|
|
824
|
+
scripts: { smoke: smokeCommand },
|
|
825
|
+
}, null, 2)}\n`;
|
|
826
|
+
await writeFileIfAllowed(packageJsonPath, body, force);
|
|
827
|
+
return packageJsonPath;
|
|
828
|
+
}
|
|
829
|
+
const scripts = existing.scripts && typeof existing.scripts === "object" && !Array.isArray(existing.scripts)
|
|
830
|
+
? { ...existing.scripts }
|
|
831
|
+
: {};
|
|
832
|
+
if (scripts.smoke && scripts.smoke !== smokeCommand && !force) {
|
|
833
|
+
throw new Error(`${packageJsonPath} already defines scripts.smoke (pass --force to overwrite)`);
|
|
834
|
+
}
|
|
835
|
+
scripts.smoke = smokeCommand;
|
|
836
|
+
existing.scripts = scripts;
|
|
837
|
+
await writeFile(packageJsonPath, `${JSON.stringify(existing, null, 2)}\n`, "utf8");
|
|
838
|
+
return packageJsonPath;
|
|
839
|
+
}
|
|
840
|
+
/** Run the interactive or flag-driven project scaffold wizard. */
|
|
841
|
+
export async function runProjectInit(options) {
|
|
842
|
+
const resolved = await resolveInteractiveOptions(options);
|
|
843
|
+
const presetId = presetIdForSolution(resolved.solution);
|
|
844
|
+
const policyFile = `${options.cwd}/paybond.policy.yaml`;
|
|
845
|
+
const configFile = resolved.language === "python"
|
|
846
|
+
? `${options.cwd}/paybond.config.py`
|
|
847
|
+
: `${options.cwd}/paybond.config.ts`;
|
|
848
|
+
const instrumentFile = resolved.language === "python"
|
|
849
|
+
? `${options.cwd}/paybond.instrument.py`
|
|
850
|
+
: `${options.cwd}/paybond.instrument.ts`;
|
|
851
|
+
const envExampleFile = `${options.cwd}/.env.example`;
|
|
852
|
+
const files = [];
|
|
853
|
+
await scaffoldPolicyFromPreset({
|
|
854
|
+
out: policyFile,
|
|
855
|
+
presetId,
|
|
856
|
+
maxSpendUsd: resolved.maxSpendUsd,
|
|
857
|
+
force: options.force,
|
|
858
|
+
});
|
|
859
|
+
files.push("paybond.policy.yaml");
|
|
860
|
+
await writeFileIfAllowed(configFile, resolved.language === "python" ? pythonConfigTemplate() : typescriptConfigTemplate(), options.force ?? false);
|
|
861
|
+
files.push(resolved.language === "python" ? "paybond.config.py" : "paybond.config.ts");
|
|
862
|
+
await writeFileIfAllowed(instrumentFile, resolved.language === "python"
|
|
863
|
+
? pythonInstrumentTemplate(resolved.solution, resolved.framework, resolved.maxSpendUsd)
|
|
864
|
+
: typescriptInstrumentTemplate(resolved.solution, resolved.framework, resolved.maxSpendUsd), options.force ?? false);
|
|
865
|
+
files.push(resolved.language === "python" ? "paybond.instrument.py" : "paybond.instrument.ts");
|
|
866
|
+
await writeFileIfAllowed(envExampleFile, envExampleBody(), options.force ?? false);
|
|
867
|
+
files.push(".env.example");
|
|
868
|
+
const smokeCommand = smokeCommandFor(presetId);
|
|
869
|
+
if (resolved.language === "typescript") {
|
|
870
|
+
const packageJsonPath = await upsertPackageJsonSmokeScript(options.cwd, smokeCommand, options.force ?? false);
|
|
871
|
+
if (packageJsonPath) {
|
|
872
|
+
files.push("package.json");
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const writeStdout = options.writeStdout ?? ((line) => output.write(`${line}\n`));
|
|
876
|
+
for (const file of files) {
|
|
877
|
+
writeStdout(`Created ${file}`);
|
|
878
|
+
}
|
|
879
|
+
writeStdout("");
|
|
880
|
+
writeStdout("Ready to run:");
|
|
881
|
+
writeStdout(" paybond login");
|
|
882
|
+
if (resolved.language === "typescript") {
|
|
883
|
+
writeStdout(" npm run smoke");
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
writeStdout(` ${smokeCommand}`);
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
solution: resolved.solution,
|
|
890
|
+
preset_id: presetId,
|
|
891
|
+
max_spend_usd: resolved.maxSpendUsd,
|
|
892
|
+
framework: resolved.framework,
|
|
893
|
+
language: resolved.language,
|
|
894
|
+
files,
|
|
895
|
+
smoke_command: smokeCommand,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
export { usage as projectInitUsage };
|