@paybond/kit 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -15
- 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/demo-loaders.d.ts +4 -0
- package/dist/cli/agent/demo-loaders.js +66 -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/command-spec.d.ts +1 -0
- package/dist/cli/command-spec.js +286 -5
- package/dist/cli/commands/agent.d.ts +16 -0
- package/dist/cli/commands/agent.js +1136 -0
- package/dist/cli/commands/dev.d.ts +7 -0
- package/dist/cli/commands/dev.js +348 -0
- package/dist/cli/commands/discovery.js +3 -3
- package/dist/cli/commands/policy.d.ts +13 -0
- package/dist/cli/commands/policy.js +769 -0
- package/dist/cli/commands/setup.d.ts +3 -0
- package/dist/cli/commands/setup.js +124 -8
- package/dist/cli/commands/workflows.js +9 -4
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +4 -2
- package/dist/cli/context.js +2 -1
- package/dist/cli/credentials.js +2 -2
- package/dist/cli/doctor-agent-middleware.d.ts +5 -0
- package/dist/cli/doctor-agent-middleware.js +49 -0
- package/dist/cli/globals.d.ts +1 -0
- package/dist/cli/globals.js +11 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +35 -3
- package/dist/cli/mcp-install.js +2 -2
- package/dist/cli/mcp-policy.d.ts +3 -0
- package/dist/cli/mcp-policy.js +19 -13
- package/dist/cli/mcp-verify-config.js +9 -11
- package/dist/cli/redact.js +29 -8
- package/dist/cli/router.js +105 -2
- package/dist/cli/smoke-deep-links.d.ts +15 -0
- package/dist/cli/smoke-deep-links.js +63 -0
- package/dist/cli/telemetry.d.ts +17 -0
- package/dist/cli/telemetry.js +94 -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 +374 -57
- 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.js +7 -0
- 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 +6 -0
- package/dist/mcp-server.js +224 -44
- 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 +75 -3
- 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-lock.json +1525 -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-lock.json +151 -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-lock.json +1489 -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-lock.json +151 -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-lock.json +1525 -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-lock.json +151 -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-lock.json +444 -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-lock.json +265 -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,16 @@
|
|
|
1
|
+
/** Security headers applied to every dev trace dashboard HTTP response. */
|
|
2
|
+
export const DEV_TRACE_SECURITY_HEADERS = {
|
|
3
|
+
"cache-control": "no-store",
|
|
4
|
+
"content-security-policy": "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
|
|
5
|
+
"permissions-policy": "camera=(), microphone=(), geolocation=(), payment=()",
|
|
6
|
+
"referrer-policy": "no-referrer",
|
|
7
|
+
"x-content-type-options": "nosniff",
|
|
8
|
+
"x-frame-options": "DENY",
|
|
9
|
+
};
|
|
10
|
+
/** Merge dev trace security headers with a response content type. */
|
|
11
|
+
export function devTraceResponseHeaders(contentType) {
|
|
12
|
+
return {
|
|
13
|
+
...DEV_TRACE_SECURITY_HEADERS,
|
|
14
|
+
"content-type": contentType,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
export type DevTraceServerOptions = {
|
|
3
|
+
port?: number;
|
|
4
|
+
host?: string;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
onListen?: (url: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function startDevTraceServer(options?: DevTraceServerOptions): Promise<Server>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { DEV_TRACE_DEFAULT_PORT, listDevTraceEvents, devTraceHasCredentials } from "./trace-buffer.js";
|
|
3
|
+
import { devTraceResponseHeaders } from "./trace-security-headers.js";
|
|
4
|
+
import { loadDevTraceDashboardHtml } from "./trace-ui.js";
|
|
5
|
+
export async function startDevTraceServer(options = {}) {
|
|
6
|
+
const port = options.port ?? DEV_TRACE_DEFAULT_PORT;
|
|
7
|
+
const host = options.host ?? "127.0.0.1";
|
|
8
|
+
const cwd = options.cwd ?? process.cwd();
|
|
9
|
+
const dashboardHtml = loadDevTraceDashboardHtml();
|
|
10
|
+
const server = createServer((req, res) => {
|
|
11
|
+
const url = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
12
|
+
const events = listDevTraceEvents(cwd);
|
|
13
|
+
if (url.pathname === "/api/events") {
|
|
14
|
+
res.writeHead(200, devTraceResponseHeaders("application/json; charset=utf-8"));
|
|
15
|
+
res.end(JSON.stringify({
|
|
16
|
+
events,
|
|
17
|
+
has_credentials: devTraceHasCredentials(),
|
|
18
|
+
}, null, 2));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (url.pathname === "/" || url.pathname.startsWith("/runs/")) {
|
|
22
|
+
res.writeHead(200, devTraceResponseHeaders("text/html; charset=utf-8"));
|
|
23
|
+
res.end(dashboardHtml);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
res.writeHead(404, devTraceResponseHeaders("text/plain; charset=utf-8"));
|
|
27
|
+
res.end("Not found");
|
|
28
|
+
});
|
|
29
|
+
await new Promise((resolve, reject) => {
|
|
30
|
+
server.once("error", reject);
|
|
31
|
+
server.listen(port, host, () => {
|
|
32
|
+
server.off("error", reject);
|
|
33
|
+
options.onListen?.(`http://${host}:${port}`);
|
|
34
|
+
resolve();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
return server;
|
|
38
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Resolve bundled or monorepo dev trace dashboard HTML. */
|
|
2
|
+
export declare function resolveDevTraceUiDashboardPath(cwd?: string): string;
|
|
3
|
+
/** Load the self-contained dev trace dashboard HTML shell. */
|
|
4
|
+
export declare function loadDevTraceDashboardHtml(cwd?: string): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
/** Resolve bundled or monorepo dev trace dashboard HTML. */
|
|
7
|
+
export function resolveDevTraceUiDashboardPath(cwd = process.cwd()) {
|
|
8
|
+
const candidates = [
|
|
9
|
+
join(cwd, "kit/dev/trace-ui/dashboard.html"),
|
|
10
|
+
join(MODULE_DIR, "../../dev/trace-ui/dashboard.html"),
|
|
11
|
+
join(MODULE_DIR, "../../../dev/trace-ui/dashboard.html"),
|
|
12
|
+
join(MODULE_DIR, "../../../../dev/trace-ui/dashboard.html"),
|
|
13
|
+
join(MODULE_DIR, "../../../../../kit/dev/trace-ui/dashboard.html"),
|
|
14
|
+
];
|
|
15
|
+
for (const candidate of candidates) {
|
|
16
|
+
if (existsSync(candidate)) {
|
|
17
|
+
return candidate;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
throw new Error("Dev trace dashboard not found. Run from the Paybond monorepo or install @paybond/kit with bundled dev assets.");
|
|
21
|
+
}
|
|
22
|
+
/** Load the self-contained dev trace dashboard HTML shell. */
|
|
23
|
+
export function loadDevTraceDashboardHtml(cwd = process.cwd()) {
|
|
24
|
+
return readFileSync(resolveDevTraceUiDashboardPath(cwd), "utf8");
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type DevWiremockUpOptions = {
|
|
2
|
+
port?: number;
|
|
3
|
+
down?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type DevWiremockUpResult = {
|
|
6
|
+
gateway_url: string;
|
|
7
|
+
port: number;
|
|
8
|
+
wiremock_dir: string;
|
|
9
|
+
container_name: string;
|
|
10
|
+
status: "started" | "already_running" | "stopped";
|
|
11
|
+
next_commands: string[];
|
|
12
|
+
};
|
|
13
|
+
/** Resolve bundled or monorepo WireMock mapping directory for `paybond dev up`. */
|
|
14
|
+
export declare function resolveDevWiremockDir(cwd?: string): string;
|
|
15
|
+
export declare function runDevWiremockUp(options?: DevWiremockUpOptions): Promise<DevWiremockUpResult>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { DEV_WIREMOCK_CONTAINER_NAME, DEV_WIREMOCK_DEFAULT_PORT, } from "./offline-gateway.js";
|
|
6
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
function spawnCommand(command, args) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
10
|
+
const stdoutChunks = [];
|
|
11
|
+
const stderrChunks = [];
|
|
12
|
+
child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
13
|
+
child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
14
|
+
child.on("error", reject);
|
|
15
|
+
child.on("close", (code) => {
|
|
16
|
+
resolve({
|
|
17
|
+
code: code ?? 1,
|
|
18
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
19
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Resolve bundled or monorepo WireMock mapping directory for `paybond dev up`. */
|
|
25
|
+
export function resolveDevWiremockDir(cwd = process.cwd()) {
|
|
26
|
+
const candidates = [
|
|
27
|
+
join(cwd, "examples/partner-dry-run-wiremock/gateway-wiremock"),
|
|
28
|
+
join(cwd, "kit/dev/wiremock"),
|
|
29
|
+
join(MODULE_DIR, "../../../dev/wiremock"),
|
|
30
|
+
join(MODULE_DIR, "../../../../dev/wiremock"),
|
|
31
|
+
join(MODULE_DIR, "../../../../../kit/dev/wiremock"),
|
|
32
|
+
];
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
if (existsSync(join(candidate, "mappings"))) {
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error("WireMock mappings not found. Run from the Paybond monorepo or install @paybond/kit with bundled dev assets.");
|
|
39
|
+
}
|
|
40
|
+
async function dockerContainerRunning(name) {
|
|
41
|
+
const result = await spawnCommand("docker", ["inspect", "-f", "{{.State.Running}}", name]);
|
|
42
|
+
return result.code === 0 && result.stdout.trim() === "true";
|
|
43
|
+
}
|
|
44
|
+
async function waitWiremockReady(baseUrl, attempts = 40, delayMs = 250) {
|
|
45
|
+
const adminUrl = `${baseUrl.replace(/\/+$/, "")}/__admin/mappings`;
|
|
46
|
+
let lastError;
|
|
47
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(adminUrl, { signal: AbortSignal.timeout(2_000) });
|
|
50
|
+
if (response.ok) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
lastError = err;
|
|
56
|
+
}
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`WireMock not ready at ${adminUrl}: ${String(lastError)}`);
|
|
60
|
+
}
|
|
61
|
+
function buildNextCommands(gatewayUrl) {
|
|
62
|
+
return [
|
|
63
|
+
`paybond dev loop --gateway ${gatewayUrl} --no-login`,
|
|
64
|
+
"paybond dev smoke --offline",
|
|
65
|
+
"paybond dev trace",
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
export async function runDevWiremockUp(options = {}) {
|
|
69
|
+
const port = options.port ?? DEV_WIREMOCK_DEFAULT_PORT;
|
|
70
|
+
const gatewayUrl = `http://127.0.0.1:${port}`;
|
|
71
|
+
const wiremockDir = resolveDevWiremockDir();
|
|
72
|
+
if (options.down) {
|
|
73
|
+
if (await dockerContainerRunning(DEV_WIREMOCK_CONTAINER_NAME)) {
|
|
74
|
+
await spawnCommand("docker", ["rm", "-f", DEV_WIREMOCK_CONTAINER_NAME]);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
gateway_url: gatewayUrl,
|
|
78
|
+
port,
|
|
79
|
+
wiremock_dir: wiremockDir,
|
|
80
|
+
container_name: DEV_WIREMOCK_CONTAINER_NAME,
|
|
81
|
+
status: "stopped",
|
|
82
|
+
next_commands: [],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (await dockerContainerRunning(DEV_WIREMOCK_CONTAINER_NAME)) {
|
|
86
|
+
return {
|
|
87
|
+
gateway_url: gatewayUrl,
|
|
88
|
+
port,
|
|
89
|
+
wiremock_dir: wiremockDir,
|
|
90
|
+
container_name: DEV_WIREMOCK_CONTAINER_NAME,
|
|
91
|
+
status: "already_running",
|
|
92
|
+
next_commands: buildNextCommands(gatewayUrl),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const run = await spawnCommand("docker", [
|
|
96
|
+
"run",
|
|
97
|
+
"-d",
|
|
98
|
+
"--name",
|
|
99
|
+
DEV_WIREMOCK_CONTAINER_NAME,
|
|
100
|
+
"-p",
|
|
101
|
+
`${port}:8080`,
|
|
102
|
+
"-v",
|
|
103
|
+
`${wiremockDir}:/home/wiremock`,
|
|
104
|
+
"wiremock/wiremock:3.3.1",
|
|
105
|
+
]);
|
|
106
|
+
if (run.code !== 0) {
|
|
107
|
+
throw new Error(run.stderr.trim() || run.stdout.trim() || "docker run failed");
|
|
108
|
+
}
|
|
109
|
+
await waitWiremockReady(gatewayUrl);
|
|
110
|
+
return {
|
|
111
|
+
gateway_url: gatewayUrl,
|
|
112
|
+
port,
|
|
113
|
+
wiremock_dir: wiremockDir,
|
|
114
|
+
container_name: DEV_WIREMOCK_CONTAINER_NAME,
|
|
115
|
+
status: "started",
|
|
116
|
+
next_commands: buildNextCommands(gatewayUrl),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Stripe payment-rail webhook event types that must not appear as tool-completion predicates. */
|
|
2
|
+
export declare const STRIPE_FUNDING_WEBHOOK_EVENT_TYPES: readonly ["payment_intent.succeeded", "charge.succeeded"];
|
|
3
|
+
export type CompletionDoctorCheck = {
|
|
4
|
+
name: string;
|
|
5
|
+
ok: boolean;
|
|
6
|
+
message: string;
|
|
7
|
+
details?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
export declare function isStripeFundingWebhookEventType(eventType: unknown): boolean;
|
|
10
|
+
type GatewayJsonClient = {
|
|
11
|
+
getJson(path: string): Promise<Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
export declare function runCompletionCatalogDoctorChecks(options: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
gateway?: GatewayJsonClient;
|
|
16
|
+
}): Promise<CompletionDoctorCheck[]>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadCompletionCatalog } from "./completion-catalog.js";
|
|
4
|
+
import { completionPresetDeprecationWarning, isVendorPack, resolveCompletionPreset, vendorEvidenceSchema, } from "./completion-resolve.js";
|
|
5
|
+
/** Stripe payment-rail webhook event types that must not appear as tool-completion predicates. */
|
|
6
|
+
export const STRIPE_FUNDING_WEBHOOK_EVENT_TYPES = [
|
|
7
|
+
"payment_intent.succeeded",
|
|
8
|
+
"charge.succeeded",
|
|
9
|
+
];
|
|
10
|
+
function stableJson(value) {
|
|
11
|
+
return JSON.stringify(value);
|
|
12
|
+
}
|
|
13
|
+
function schemasMatch(expected, actual) {
|
|
14
|
+
return stableJson(expected) === stableJson(actual);
|
|
15
|
+
}
|
|
16
|
+
function parametersMatch(expected, actual) {
|
|
17
|
+
return stableJson(expected) === stableJson(actual);
|
|
18
|
+
}
|
|
19
|
+
const SCAFFOLD_GLOBS = [
|
|
20
|
+
/^paybond-completion-[\w-]+\.(ts|js|py)$/,
|
|
21
|
+
/^paybond-paid-tool-guard\.(ts|js|py)$/,
|
|
22
|
+
/^paybond_completion_[\w]+\.py$/,
|
|
23
|
+
];
|
|
24
|
+
async function findCompletionScaffolds(cwd) {
|
|
25
|
+
const matches = [];
|
|
26
|
+
try {
|
|
27
|
+
const entries = await readdir(cwd);
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
if (SCAFFOLD_GLOBS.some((pattern) => pattern.test(entry))) {
|
|
30
|
+
matches.push(path.join(cwd, entry));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return matches;
|
|
36
|
+
}
|
|
37
|
+
return matches;
|
|
38
|
+
}
|
|
39
|
+
function extractPresetIdFromScaffold(body) {
|
|
40
|
+
const tsMatch = body.match(/export const COMPLETION_PRESET_ID = "([^"]+)"/);
|
|
41
|
+
if (tsMatch?.[1]) {
|
|
42
|
+
return tsMatch[1];
|
|
43
|
+
}
|
|
44
|
+
const pyMatch = body.match(/^COMPLETION_PRESET_ID = "([^"]+)"/m);
|
|
45
|
+
return pyMatch?.[1];
|
|
46
|
+
}
|
|
47
|
+
function schemaPropertyKeys(schema) {
|
|
48
|
+
if (!schema) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const properties = schema.properties;
|
|
52
|
+
if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
|
|
53
|
+
return Object.keys(properties);
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
function vendorSchemaForbiddenHits(preset, vendorSchema, forbidden) {
|
|
58
|
+
if (!vendorSchema) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const fieldMap = preset.evidence_field_map ?? {};
|
|
62
|
+
const unmappedVendorKeys = schemaPropertyKeys(vendorSchema).filter((key) => !(key in fieldMap));
|
|
63
|
+
return forbiddenFieldHits(unmappedVendorKeys, forbidden);
|
|
64
|
+
}
|
|
65
|
+
function forbiddenFieldHits(propertyKeys, forbidden) {
|
|
66
|
+
if (!forbidden?.length) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const blocked = new Set(forbidden);
|
|
70
|
+
return propertyKeys.filter((key) => blocked.has(key));
|
|
71
|
+
}
|
|
72
|
+
export function isStripeFundingWebhookEventType(eventType) {
|
|
73
|
+
return (typeof eventType === "string" &&
|
|
74
|
+
STRIPE_FUNDING_WEBHOOK_EVENT_TYPES.includes(eventType));
|
|
75
|
+
}
|
|
76
|
+
function extractTemplateParametersFromScaffold(body) {
|
|
77
|
+
const tsMatch = body.match(/export const completionTemplateParameters = (\{[\s\S]*?\}) as const;/);
|
|
78
|
+
if (tsMatch?.[1]) {
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(tsMatch[1]);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const pyMatch = body.match(/^completion_template_parameters: dict\[str, Any\] = (\{[\s\S]*?\})\n\n/m);
|
|
87
|
+
if (pyMatch?.[1]) {
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(pyMatch[1].replace(/'/g, '"'));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
function extractVendorContractPinFromScaffold(body) {
|
|
98
|
+
const tsApi = body.match(/export const VENDOR_CONTRACT_API_VERSION = "([^"]+)"/);
|
|
99
|
+
const pyApi = body.match(/^VENDOR_CONTRACT_API_VERSION = "([^"]+)"/m);
|
|
100
|
+
const tsVendorDigest = body.match(/export const VENDOR_SCHEMA_DIGEST_HEX = "([^"]+)"/);
|
|
101
|
+
const pyVendorDigest = body.match(/^VENDOR_SCHEMA_DIGEST_HEX = "([^"]+)"/m);
|
|
102
|
+
const tsCanonicalDigest = body.match(/export const CANONICAL_SCHEMA_DIGEST_HEX = "([^"]+)"/);
|
|
103
|
+
const pyCanonicalDigest = body.match(/^CANONICAL_SCHEMA_DIGEST_HEX = "([^"]+)"/m);
|
|
104
|
+
const tsQuality = body.match(/export const VENDOR_QUALITY_FIELDS = (\[[\s\S]*?\]) as const;/);
|
|
105
|
+
const pyQuality = body.match(/^VENDOR_QUALITY_FIELDS: tuple\[str, \.\.\.\] = tuple\((\[[\s\S]*?\])\)/m);
|
|
106
|
+
let qualityFields;
|
|
107
|
+
const qualityRaw = tsQuality?.[1] ?? pyQuality?.[1];
|
|
108
|
+
if (qualityRaw) {
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(qualityRaw);
|
|
111
|
+
if (Array.isArray(parsed)) {
|
|
112
|
+
qualityFields = parsed.filter((entry) => typeof entry === "string");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
qualityFields = undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
apiVersion: tsApi?.[1] ?? pyApi?.[1],
|
|
121
|
+
vendorSchemaDigest: tsVendorDigest?.[1] ?? pyVendorDigest?.[1],
|
|
122
|
+
canonicalSchemaDigest: tsCanonicalDigest?.[1] ?? pyCanonicalDigest?.[1],
|
|
123
|
+
qualityFields,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function extractEvidenceSchemaFromScaffold(body) {
|
|
127
|
+
const tsMatch = body.match(/export const completionEvidenceSchema = (\{[\s\S]*?\}) as const;/);
|
|
128
|
+
if (tsMatch?.[1]) {
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(tsMatch[1]);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const pyMatch = body.match(/^completion_evidence_schema: dict\[str, Any\] = (\{[\s\S]*?\})\n\n/m);
|
|
137
|
+
if (pyMatch?.[1]) {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(pyMatch[1].replace(/'/g, '"'));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
export async function runCompletionCatalogDoctorChecks(options) {
|
|
148
|
+
const checks = [];
|
|
149
|
+
let catalog;
|
|
150
|
+
try {
|
|
151
|
+
catalog = loadCompletionCatalog();
|
|
152
|
+
checks.push({
|
|
153
|
+
name: "completion_catalog",
|
|
154
|
+
ok: true,
|
|
155
|
+
message: `catalog v${catalog.version} loaded (${catalog.presets.length} presets)`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
checks.push({
|
|
160
|
+
name: "completion_catalog",
|
|
161
|
+
ok: false,
|
|
162
|
+
message: err instanceof Error ? err.message : String(err),
|
|
163
|
+
});
|
|
164
|
+
return checks;
|
|
165
|
+
}
|
|
166
|
+
const scaffoldPaths = await findCompletionScaffolds(options.cwd);
|
|
167
|
+
const fundingEventWarnings = [];
|
|
168
|
+
const packStaleWarnings = [];
|
|
169
|
+
const qualityFieldWarnings = [];
|
|
170
|
+
if (scaffoldPaths.length === 0) {
|
|
171
|
+
checks.push({
|
|
172
|
+
name: "completion_local_scaffold",
|
|
173
|
+
ok: true,
|
|
174
|
+
message: "no local completion scaffold files in cwd",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const divergences = [];
|
|
179
|
+
const deprecatedPresets = [];
|
|
180
|
+
const forbiddenFieldWarnings = [];
|
|
181
|
+
for (const scaffoldPath of scaffoldPaths) {
|
|
182
|
+
const body = await readFile(scaffoldPath, "utf8");
|
|
183
|
+
const scaffoldName = path.basename(scaffoldPath);
|
|
184
|
+
const presetId = extractPresetIdFromScaffold(body);
|
|
185
|
+
if (!presetId) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const deprecationWarning = completionPresetDeprecationWarning(presetId);
|
|
189
|
+
if (deprecationWarning) {
|
|
190
|
+
deprecatedPresets.push(`${scaffoldName}: ${deprecationWarning}`);
|
|
191
|
+
}
|
|
192
|
+
let resolved;
|
|
193
|
+
try {
|
|
194
|
+
resolved = resolveCompletionPreset(presetId);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
divergences.push(`${scaffoldName}: unknown preset ${presetId}`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const embedded = extractEvidenceSchemaFromScaffold(body);
|
|
201
|
+
const expected = isVendorPack(resolved.preset)
|
|
202
|
+
? resolved.preset.evidence_schema
|
|
203
|
+
: resolved.evidenceSchema;
|
|
204
|
+
if (embedded && !schemasMatch(expected, embedded)) {
|
|
205
|
+
divergences.push(`${scaffoldName}: evidence_schema diverges from catalog for ${presetId}`);
|
|
206
|
+
}
|
|
207
|
+
const forbidden = resolved.preset.forbidden_evidence_fields;
|
|
208
|
+
const hits = [
|
|
209
|
+
...forbiddenFieldHits(schemaPropertyKeys(embedded), forbidden),
|
|
210
|
+
...vendorSchemaForbiddenHits(resolved.preset, vendorEvidenceSchema(resolved.preset), forbidden),
|
|
211
|
+
];
|
|
212
|
+
const uniqueHits = [...new Set(hits)];
|
|
213
|
+
if (uniqueHits.length > 0) {
|
|
214
|
+
forbiddenFieldWarnings.push(`${scaffoldName}: evidence schema includes forbidden field(s) for ${presetId}: ${uniqueHits.join(", ")}`);
|
|
215
|
+
}
|
|
216
|
+
if (resolved.harborTemplateId === "webhook_confirmation_v1") {
|
|
217
|
+
const embeddedParams = extractTemplateParametersFromScaffold(body);
|
|
218
|
+
const expectedEventType = embeddedParams?.expected_event_type ?? resolved.parameters.expected_event_type;
|
|
219
|
+
if (isStripeFundingWebhookEventType(expectedEventType)) {
|
|
220
|
+
fundingEventWarnings.push(`${scaffoldName}: expected_event_type ${String(expectedEventType)} is a Stripe funding webhook, not tool-completion evidence`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isVendorPack(resolved.preset)) {
|
|
224
|
+
const contract = resolved.preset.vendor_contract;
|
|
225
|
+
const pin = extractVendorContractPinFromScaffold(body);
|
|
226
|
+
if (contract) {
|
|
227
|
+
if (!pin.apiVersion) {
|
|
228
|
+
packStaleWarnings.push(`${scaffoldName}: missing VENDOR_CONTRACT_API_VERSION pin for ${presetId}; re-run paybond init completion`);
|
|
229
|
+
}
|
|
230
|
+
else if (pin.apiVersion !== contract.api_version) {
|
|
231
|
+
packStaleWarnings.push(`${scaffoldName}: pinned api_version ${pin.apiVersion} lags catalog ${contract.api_version} for ${presetId}`);
|
|
232
|
+
}
|
|
233
|
+
if (pin.vendorSchemaDigest && pin.vendorSchemaDigest !== contract.schema_digest_hex) {
|
|
234
|
+
packStaleWarnings.push(`${scaffoldName}: pinned vendor schema digest diverges from catalog for ${presetId}`);
|
|
235
|
+
}
|
|
236
|
+
if (pin.canonicalSchemaDigest && pin.canonicalSchemaDigest !== contract.canonical_schema_digest_hex) {
|
|
237
|
+
packStaleWarnings.push(`${scaffoldName}: pinned canonical schema digest diverges from catalog for ${presetId}`);
|
|
238
|
+
}
|
|
239
|
+
const expectedQuality = contract.quality_fields ?? [];
|
|
240
|
+
if (expectedQuality.length > 0 && !pin.qualityFields) {
|
|
241
|
+
qualityFieldWarnings.push(`${scaffoldName}: missing VENDOR_QUALITY_FIELDS export for ${presetId}`);
|
|
242
|
+
}
|
|
243
|
+
else if (pin.qualityFields &&
|
|
244
|
+
stableJson([...pin.qualityFields].sort()) !== stableJson([...expectedQuality].sort())) {
|
|
245
|
+
qualityFieldWarnings.push(`${scaffoldName}: VENDOR_QUALITY_FIELDS diverge from catalog for ${presetId}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (divergences.length > 0) {
|
|
251
|
+
checks.push({
|
|
252
|
+
name: "completion_local_scaffold",
|
|
253
|
+
ok: true,
|
|
254
|
+
message: `warn: ${divergences.join("; ")}`,
|
|
255
|
+
details: { divergences },
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
checks.push({
|
|
260
|
+
name: "completion_local_scaffold",
|
|
261
|
+
ok: true,
|
|
262
|
+
message: `checked ${scaffoldPaths.length} scaffold file(s); evidence schemas match catalog`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (deprecatedPresets.length > 0) {
|
|
266
|
+
checks.push({
|
|
267
|
+
name: "completion_deprecated_preset",
|
|
268
|
+
ok: true,
|
|
269
|
+
message: `warn: ${deprecatedPresets.join("; ")}`,
|
|
270
|
+
details: { warnings: deprecatedPresets },
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
checks.push({
|
|
275
|
+
name: "completion_deprecated_preset",
|
|
276
|
+
ok: true,
|
|
277
|
+
message: "no deprecated completion presets in local scaffolds",
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (forbiddenFieldWarnings.length > 0) {
|
|
281
|
+
checks.push({
|
|
282
|
+
name: "completion_forbidden_fields",
|
|
283
|
+
ok: true,
|
|
284
|
+
message: `warn: ${forbiddenFieldWarnings.join("; ")}`,
|
|
285
|
+
details: { warnings: forbiddenFieldWarnings },
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
checks.push({
|
|
290
|
+
name: "completion_forbidden_fields",
|
|
291
|
+
ok: true,
|
|
292
|
+
message: "local scaffold evidence schemas exclude catalog forbidden fields",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (scaffoldPaths.length === 0) {
|
|
297
|
+
checks.push({
|
|
298
|
+
name: "completion_deprecated_preset",
|
|
299
|
+
ok: true,
|
|
300
|
+
message: "no local completion scaffold files in cwd",
|
|
301
|
+
});
|
|
302
|
+
checks.push({
|
|
303
|
+
name: "completion_forbidden_fields",
|
|
304
|
+
ok: true,
|
|
305
|
+
message: "no local completion scaffold files in cwd",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
pushPackStaleCheck(checks, packStaleWarnings, scaffoldPaths.length);
|
|
309
|
+
pushQualityFieldsCheck(checks, qualityFieldWarnings, scaffoldPaths.length);
|
|
310
|
+
if (!options.gateway) {
|
|
311
|
+
checks.push({
|
|
312
|
+
name: "completion_policy_heads",
|
|
313
|
+
ok: true,
|
|
314
|
+
message: "skipped policy head check (no gateway credentials)",
|
|
315
|
+
});
|
|
316
|
+
pushFundingEventMisuseCheck(checks, fundingEventWarnings, scaffoldPaths.length);
|
|
317
|
+
return checks;
|
|
318
|
+
}
|
|
319
|
+
const archetypes = catalog.presets.filter((preset) => !isVendorPack(preset));
|
|
320
|
+
const headWarnings = [];
|
|
321
|
+
for (const preset of archetypes) {
|
|
322
|
+
if (preset.harbor_template_id === "true_v1") {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
const versions = await options.gateway.getJson(`/harbor/policy/v1/versions?${new URLSearchParams({ template_id: preset.harbor_template_id }).toString()}`);
|
|
327
|
+
const headSeq = versions.current_head_seq;
|
|
328
|
+
if (headSeq === null || headSeq === undefined) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const versionRows = Array.isArray(versions.versions) ? versions.versions : [];
|
|
332
|
+
const head = versionRows.find((row) => typeof row === "object" &&
|
|
333
|
+
row !== null &&
|
|
334
|
+
row.version_seq === headSeq);
|
|
335
|
+
if (!head) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const headParams = readObject(head.parameters) ?? {};
|
|
339
|
+
if (!parametersMatch(preset.parameters, headParams)) {
|
|
340
|
+
headWarnings.push(`${preset.harbor_template_id} head seq ${String(headSeq)} parameters diverge from catalog preset ${preset.preset_id}`);
|
|
341
|
+
}
|
|
342
|
+
if (preset.harbor_template_id === "webhook_confirmation_v1" &&
|
|
343
|
+
isStripeFundingWebhookEventType(headParams.expected_event_type)) {
|
|
344
|
+
fundingEventWarnings.push(`${preset.harbor_template_id} head seq ${String(headSeq)} uses Stripe funding event type ${String(headParams.expected_event_type)}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
headWarnings.push(`${preset.harbor_template_id}: could not load policy versions (${err instanceof Error ? err.message : String(err)})`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (headWarnings.length > 0) {
|
|
352
|
+
checks.push({
|
|
353
|
+
name: "completion_policy_heads",
|
|
354
|
+
ok: true,
|
|
355
|
+
message: `warn: ${headWarnings.join("; ")}`,
|
|
356
|
+
details: { warnings: headWarnings },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
checks.push({
|
|
361
|
+
name: "completion_policy_heads",
|
|
362
|
+
ok: true,
|
|
363
|
+
message: "published policy heads match catalog defaults (or no heads published)",
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
pushFundingEventMisuseCheck(checks, fundingEventWarnings, scaffoldPaths.length);
|
|
367
|
+
return checks;
|
|
368
|
+
}
|
|
369
|
+
function pushPackStaleCheck(checks, warnings, scaffoldCount) {
|
|
370
|
+
if (warnings.length > 0) {
|
|
371
|
+
checks.push({
|
|
372
|
+
name: "completion_pack_stale",
|
|
373
|
+
ok: true,
|
|
374
|
+
message: `warn: ${warnings.join("; ")}`,
|
|
375
|
+
details: { warnings },
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
checks.push({
|
|
380
|
+
name: "completion_pack_stale",
|
|
381
|
+
ok: true,
|
|
382
|
+
message: scaffoldCount === 0
|
|
383
|
+
? "no local completion scaffold files in cwd"
|
|
384
|
+
: "local vendor pack scaffolds match catalog contract pins",
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
function pushQualityFieldsCheck(checks, warnings, scaffoldCount) {
|
|
388
|
+
if (warnings.length > 0) {
|
|
389
|
+
checks.push({
|
|
390
|
+
name: "completion_quality_fields",
|
|
391
|
+
ok: true,
|
|
392
|
+
message: `warn: ${warnings.join("; ")}`,
|
|
393
|
+
details: { warnings },
|
|
394
|
+
});
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
checks.push({
|
|
398
|
+
name: "completion_quality_fields",
|
|
399
|
+
ok: true,
|
|
400
|
+
message: scaffoldCount === 0
|
|
401
|
+
? "no local completion scaffold files in cwd"
|
|
402
|
+
: "vendor pack scaffolds export catalog quality_fields pins",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
function pushFundingEventMisuseCheck(checks, warnings, scaffoldCount) {
|
|
406
|
+
if (warnings.length > 0) {
|
|
407
|
+
checks.push({
|
|
408
|
+
name: "completion_funding_event_misuse",
|
|
409
|
+
ok: true,
|
|
410
|
+
message: `warn: ${warnings.join("; ")}`,
|
|
411
|
+
details: { warnings },
|
|
412
|
+
});
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
checks.push({
|
|
416
|
+
name: "completion_funding_event_misuse",
|
|
417
|
+
ok: true,
|
|
418
|
+
message: scaffoldCount === 0
|
|
419
|
+
? "no local completion scaffold files in cwd"
|
|
420
|
+
: "no Stripe funding webhook event types in webhook_confirmed scaffolds or policy heads",
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
function readObject(value) {
|
|
424
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|