@paybond/kit 0.11.5 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/interceptor.js +12 -3
  3. package/dist/cli/agent-harbor-evidence-smoke-checklist.d.ts +7 -0
  4. package/dist/cli/agent-harbor-evidence-smoke-checklist.js +23 -0
  5. package/dist/cli/agent-production-attach-smoke-checklist.d.ts +7 -0
  6. package/dist/cli/agent-production-attach-smoke-checklist.js +30 -0
  7. package/dist/cli/command-spec.js +13 -4
  8. package/dist/cli/commands/agent.d.ts +2 -0
  9. package/dist/cli/commands/agent.js +236 -15
  10. package/dist/cli/commands/setup.d.ts +11 -1
  11. package/dist/cli/commands/setup.js +42 -9
  12. package/dist/cli/help.js +2 -0
  13. package/dist/cli/http-error-message.d.ts +18 -0
  14. package/dist/cli/http-error-message.js +148 -0
  15. package/dist/cli/offline-session.d.ts +9 -0
  16. package/dist/cli/offline-session.js +33 -0
  17. package/dist/cli/router.js +36 -2
  18. package/dist/cli.js +15 -1
  19. package/dist/gateway-retry.d.ts +16 -0
  20. package/dist/gateway-retry.js +83 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +34 -107
  23. package/dist/mcp-server.js +7 -8
  24. package/dist/payee-evidence.d.ts +9 -0
  25. package/dist/payee-evidence.js +12 -0
  26. package/package.json +2 -1
  27. package/templates/manifest.json +9 -9
  28. package/templates/openai-shopping-agent/package-lock.json +7 -7
  29. package/templates/openai-shopping-agent/package.json +1 -1
  30. package/templates/paybond-aws-operator/package-lock.json +4 -4
  31. package/templates/paybond-aws-operator/package.json +1 -1
  32. package/templates/paybond-claude-agents-demo/package-lock.json +7 -7
  33. package/templates/paybond-claude-agents-demo/package.json +1 -1
  34. package/templates/paybond-invoice-agent/requirements.txt +1 -1
  35. package/templates/paybond-mcp-coding-agent/package-lock.json +4 -4
  36. package/templates/paybond-mcp-coding-agent/package.json +1 -1
  37. package/templates/paybond-openai-agents-demo/package-lock.json +7 -7
  38. package/templates/paybond-openai-agents-demo/package.json +1 -1
  39. package/templates/paybond-procurement-agent/package-lock.json +4 -4
  40. package/templates/paybond-procurement-agent/package.json +1 -1
  41. package/templates/paybond-travel-agent/package-lock.json +7 -7
  42. package/templates/paybond-travel-agent/package.json +1 -1
  43. package/templates/paybond-vercel-shopping-agent/package-lock.json +4 -4
  44. package/templates/paybond-vercel-shopping-agent/package.json +1 -1
@@ -7,7 +7,8 @@ import { assertApiKeyShape, resolveApiKey, resolvedDefaultsForDoctor } from "../
7
7
  import { runAgentMiddlewareDoctorCheck } from "../doctor-agent-middleware.js";
8
8
  import { packageVersion, runAgentMcpChecks } from "../doctor-agent.js";
9
9
  import { runCompletionCatalogDoctorChecks } from "../../doctor-completion.js";
10
- import { consumeBooleanFlag, consumeFlag } from "../globals.js";
10
+ import { consumeBooleanFlag, consumeFlag, parseCliArgv } from "../globals.js";
11
+ import { helpForCommand } from "../help.js";
11
12
  import { maskApiKey, redactConfigValue } from "../redact.js";
12
13
  import { mergeMcpToolPolicy, parseMcpToolAllowlist, parseMcpToolPolicy, resolveMcpToolPolicy, } from "../mcp-policy.js";
13
14
  import { parseMcpInstallFormat, parseMcpInstallHost, parseMcpInstallScope, planMcpInstall, } from "../mcp-install.js";
@@ -197,16 +198,48 @@ export async function handleInitCompletion(ctx, argv) {
197
198
  },
198
199
  };
199
200
  }
200
- export async function handleMcpServe(ctx, argv) {
201
- if (argv.length > 0 && argv[0] !== "--help" && argv[0] !== "-h") {
202
- throw new CliError(`unexpected arguments: ${argv.join(" ")}`, { category: "usage", code: "cli.usage.unexpected_args" });
201
+ export function mcpServeArgvMatches(argv) {
202
+ try {
203
+ const { command } = parseCliArgv(argv);
204
+ return command.length >= 2 && command[0] === "mcp" && command[1] === "serve";
203
205
  }
204
- ctx.stderr.write("Starting Paybond MCP stdio server (stdout is reserved for MCP JSON-RPC).\n");
205
- const code = runMcpServerMain([]);
206
- if (code !== 0) {
207
- throw new CliError("mcp serve failed", { category: "internal", code: "cli.mcp.serve_failed", exitCode: code });
206
+ catch {
207
+ return false;
208
+ }
209
+ }
210
+ function isMcpServeHelpCommand(command) {
211
+ return command.length === 0 || command.includes("--help") || command.includes("-h");
212
+ }
213
+ /** Run the blocking MCP stdio server. Must not run through the async CLI dispatcher. */
214
+ export function runMcpServeCommandSync(argv, writers) {
215
+ let command;
216
+ try {
217
+ ({ command } = parseCliArgv(argv));
208
218
  }
209
- return { data: { started: true } };
219
+ catch (err) {
220
+ const message = err instanceof CliError ? err.message : String(err);
221
+ writers.stderr.write(`${message}\n`);
222
+ return err instanceof CliError ? err.exitCode : 1;
223
+ }
224
+ if (isMcpServeHelpCommand(command)) {
225
+ const helpPath = command.filter((part) => part !== "--help" && part !== "-h").join(" ") || "mcp serve";
226
+ writers.stdout.write(`${helpForCommand(helpPath)}\n`);
227
+ return 0;
228
+ }
229
+ const rest = command.slice(2);
230
+ if (rest.length > 0 && rest[0] !== "--help" && rest[0] !== "-h") {
231
+ writers.stderr.write(`unexpected arguments: ${rest.join(" ")}\n`);
232
+ return 2;
233
+ }
234
+ writers.stderr.write("Starting Paybond MCP stdio server (stdout is reserved for MCP JSON-RPC).\n");
235
+ const code = runMcpServerMain([]);
236
+ return code;
237
+ }
238
+ export async function handleMcpServe(_ctx, _argv) {
239
+ throw new CliError("mcp serve must run via the sync CLI entrypoint (not the async dispatcher)", {
240
+ category: "internal",
241
+ code: "cli.mcp.serve_async_forbidden",
242
+ });
210
243
  }
211
244
  export async function handleMcpTools(ctx) {
212
245
  const server = new PaybondMCPServer({ gatewayBaseUrl: ctx.globals.gateway, apiKey: "paybond_sk_sandbox_redacted_redacted_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" });
package/dist/cli/help.js CHANGED
@@ -68,6 +68,8 @@ export const COMMAND_HELP = {
68
68
  "agent tool validate": "Usage: paybond agent tool validate --run-id <id> --operation <name> [--requested-spend-cents <n>] [--arguments <json>]\n\nAuthorize-only dry run (no execute, no evidence). Useful for agents checking budget before side effects.\n\nExamples:\n $ paybond agent tool validate --run-id run-123 --operation travel.book_hotel --requested-spend-cents 18700 --format json\n\nDocs: https://docs.paybond.ai/kit/agent-middleware",
69
69
  "agent registry validate": "Usage: paybond agent registry validate --file <path>\n\nValidate a registry YAML/JSON file before bind (evidence_preset required on side-effecting tools).\n\nExamples:\n $ paybond agent registry validate --file ./paybond.agent.registry.yaml --format json\n\nDocs: https://docs.paybond.ai/kit/agent-middleware",
70
70
  "agent sandbox smoke": "Usage: paybond agent sandbox smoke [--production] [--preset <id> | --policy-file <path> [--operation <name>] [--requested-spend-cents <n>]] | (--operation <name> --requested-spend-cents <n> --evidence-preset <id>) --result-body <json>|--result-file <path>\n\nOne-shot end-to-end sandbox lifecycle: bind, tool execute, combined result. With --policy-file, completion_preset is derived from each tool's evidence_preset in YAML — do not pass --evidence-preset (Gateway rejects conflicting bootstrap fields).\n\nExamples:\n $ paybond agent sandbox smoke --preset travel --result-body '{\"status\":\"completed\",\"cost_cents\":18700}' --format json\n $ paybond agent sandbox smoke --policy-file paybond.policy.yaml --result-body '{\"status\":\"completed\",\"cost_cents\":18700}' --format json\n $ paybond agent sandbox smoke --operation paid-tool --requested-spend-cents 100 --evidence-preset cost_and_completion --result-body '{\"status\":\"ok\",\"cost_cents\":100}' --format json\n\nDocs: https://docs.paybond.ai/kit/quickstart-agent",
71
+ "agent production attach smoke": "Usage: paybond agent production attach smoke --attach-intent-id <id> --capability-token <token> --operation <name> [--requested-spend-cents <n>] [--policy-file <path>] [--payee-did <did> --payee-signing-seed-hex <hex> --agent-recognition-key-id <id> --agent-recognition-signing-seed-hex <hex>] --result-body <json>|--result-file <path>\n\nOne-shot production attach lifecycle against live /harbor/* with agent recognition proofs: bind an existing funded intent, execute a guarded tool, and submit Kit-signed payee evidence. Requires production credentials (flags or PAYBOND_ATTACH_INTENT_ID, PAYBOND_CAPABILITY_TOKEN, APP_PAYEE_*, APP_AGENT_RECOGNITION_*). Not a substitute for sandbox smoke — run in staging before production attach rollouts.\n\nExamples:\n $ paybond agent production attach smoke --attach-intent-id <intent-id> --capability-token <token> --operation paid-tool --requested-spend-cents 100 --result-body '{\"status\":\"ok\",\"cost_cents\":100}' --format json\n\nDocs: https://docs.paybond.ai/kit/production-attach",
72
+ "agent harbor evidence smoke": "Usage: paybond agent harbor evidence smoke --intent-id <id> [--payee-did <did> --payee-signing-seed-hex <hex> --agent-recognition-key-id <id> --agent-recognition-signing-seed-hex <hex>] [--idempotency-key <key>] --result-body <json>|--result-file <path>\n\nSingle POST /harbor/intents/{id}/evidence with Kit-signed payee evidence and agent recognition proof. Use in staging with Harbor SEC-003 enforcement on to confirm gateway proxy + Harbor nonce semantics (no double-claim replay). Requires a funded intent not yet in evidence_submitted state.\n\nExamples:\n $ paybond agent harbor evidence smoke --intent-id <intent-id> --result-body '{\"status\":\"ok\",\"cost_cents\":100}' --format json\n\nDocs: https://docs.paybond.ai/kit/production-attach",
71
73
  "agent demo vercel-ai smoke": "Usage: paybond agent demo vercel-ai smoke [--production] --operation <name> --requested-spend-cents <n> --evidence-preset <id>\n\nBundled no-LLM Vercel AI SDK sandbox demo: toolApproval, wrapped execute, and auto-evidence via MockLanguageModelV4.\n\nExamples:\n $ paybond agent demo vercel-ai smoke --operation paid-tool --requested-spend-cents 100 --evidence-preset cost_and_completion --format json\n\nDocs: https://docs.paybond.ai/kit/vercel-ai",
72
74
  "agent demo langgraph smoke": "Usage: paybond agent demo langgraph smoke [--production] [--runtime typescript|python] --operation <name> --requested-spend-cents <n> --evidence-preset <id>\n\nBundled no-LLM LangGraph sandbox demo: ToolNode interceptor, authorize, execute, and auto-evidence.\n\nExamples:\n $ paybond agent demo langgraph smoke --operation paid-tool --requested-spend-cents 100 --evidence-preset cost_and_completion --format json\n\nDocs: https://docs.paybond.ai/kit/langgraph",
73
75
  "agent demo generic smoke": "Usage: paybond agent demo generic smoke [--production] [--runtime typescript|python] --operation <name> --requested-spend-cents <n> --evidence-preset <id>\n\nBundled no-LLM agent-agnostic sandbox demo: createPaybondGenericAgentConfig, wrapped execute, and auto-evidence.\n\nExamples:\n $ paybond agent demo generic smoke --operation paid-tool --requested-spend-cents 100 --evidence-preset cost_and_completion --format json\n\nDocs: https://docs.paybond.ai/kit/agent-agnostic",
@@ -0,0 +1,18 @@
1
+ import type { CliErrorDetails } from "./types.js";
2
+ /**
3
+ * Summarize an HTTP error body for operator-facing CLI output.
4
+ * Never includes raw edge payloads (Cloudflare ray IDs, zones, HTML, etc.).
5
+ */
6
+ export declare function summarizeGatewayHttpError(statusCode: number, bodyText: string): {
7
+ message: string;
8
+ details: CliErrorDetails;
9
+ };
10
+ /**
11
+ * Build a CLI-safe message for SDK HTTP failures that embed raw bodies in `.message`.
12
+ */
13
+ export declare function formatSdkHttpErrorMessage(rawMessage: string, statusCode: number, bodyText: string): string;
14
+ /**
15
+ * Resolve a CLI-safe gateway error message from SDK or agent middleware failures.
16
+ * Never echoes Cloudflare edge JSON, HTML, or raw upstream bodies.
17
+ */
18
+ export declare function resolveCliGatewayErrorMessage(err: unknown): string;
@@ -0,0 +1,148 @@
1
+ function asRecord(value) {
2
+ return value && typeof value === "object" && !Array.isArray(value)
3
+ ? value
4
+ : undefined;
5
+ }
6
+ /**
7
+ * Summarize an HTTP error body for operator-facing CLI output.
8
+ * Never includes raw edge payloads (Cloudflare ray IDs, zones, HTML, etc.).
9
+ */
10
+ export function summarizeGatewayHttpError(statusCode, bodyText) {
11
+ const trimmed = bodyText.trim();
12
+ if (!trimmed) {
13
+ return {
14
+ message: `Gateway HTTP ${statusCode}`,
15
+ details: { gateway_status: statusCode },
16
+ };
17
+ }
18
+ try {
19
+ const body = JSON.parse(trimmed);
20
+ if (body.cloudflare_error === true) {
21
+ const retryAfter = typeof body.retry_after === "number" ? body.retry_after : undefined;
22
+ const title = typeof body.title === "string"
23
+ ? body.title.replace(/^Error \d+:\s*/i, "")
24
+ : "service temporarily unavailable";
25
+ let message = `Gateway unavailable (HTTP ${statusCode}): ${title}`;
26
+ if (retryAfter !== undefined) {
27
+ message += `. Retry after ${retryAfter} seconds.`;
28
+ }
29
+ return {
30
+ message,
31
+ details: {
32
+ gateway_status: statusCode,
33
+ ...(retryAfter !== undefined ? { retry_after: retryAfter } : {}),
34
+ },
35
+ };
36
+ }
37
+ const nested = asRecord(body.error);
38
+ if (nested) {
39
+ const gatewayCode = typeof nested.code === "string" ? nested.code : String(nested.code ?? "");
40
+ const gatewayMessage = typeof nested.message === "string" ? nested.message : "";
41
+ if (gatewayMessage) {
42
+ return {
43
+ message: gatewayMessage,
44
+ details: {
45
+ gateway_status: statusCode,
46
+ ...(gatewayCode ? { gateway_code: gatewayCode } : {}),
47
+ },
48
+ };
49
+ }
50
+ }
51
+ const flatMessage = typeof body.message === "string" ? body.message : "";
52
+ if (flatMessage) {
53
+ const gatewayCode = typeof body.code === "string" ? body.code : "";
54
+ return {
55
+ message: flatMessage,
56
+ details: {
57
+ gateway_status: statusCode,
58
+ ...(gatewayCode ? { gateway_code: gatewayCode } : {}),
59
+ },
60
+ };
61
+ }
62
+ if (typeof body.title === "string") {
63
+ const detail = typeof body.detail === "string" ? body.detail : "";
64
+ const combined = detail ? `${body.title}: ${detail}` : body.title;
65
+ const message = combined.length > 240 ? `${combined.slice(0, 237)}...` : combined;
66
+ return {
67
+ message,
68
+ details: { gateway_status: statusCode },
69
+ };
70
+ }
71
+ }
72
+ catch {
73
+ // Non-JSON bodies must not be echoed (HTML, stack traces, etc.).
74
+ }
75
+ return {
76
+ message: `Gateway HTTP ${statusCode}`,
77
+ details: { gateway_status: statusCode },
78
+ };
79
+ }
80
+ /**
81
+ * Build a CLI-safe message for SDK HTTP failures that embed raw bodies in `.message`.
82
+ */
83
+ export function formatSdkHttpErrorMessage(rawMessage, statusCode, bodyText) {
84
+ const operation = rawMessage.replace(/ HTTP \d+:[\s\S]*$/u, "").trim() || "Gateway request";
85
+ const summary = summarizeGatewayHttpError(statusCode, bodyText);
86
+ if (summary.message === `Gateway HTTP ${statusCode}`) {
87
+ return `${operation} HTTP ${statusCode}`;
88
+ }
89
+ if (summary.message.startsWith("Gateway unavailable")) {
90
+ return `${operation}: ${summary.message}`;
91
+ }
92
+ return `${operation} HTTP ${statusCode}: ${summary.message}`;
93
+ }
94
+ function isSdkHttpErrorLike(err) {
95
+ if (!err || typeof err !== "object") {
96
+ return false;
97
+ }
98
+ const candidate = err;
99
+ return (typeof candidate.message === "string" &&
100
+ typeof candidate.statusCode === "number" &&
101
+ typeof candidate.bodyText === "string");
102
+ }
103
+ function parseEmbeddedHttpErrorBody(message) {
104
+ const match = / HTTP (\d{3}):\s*(\{[\s\S]*\})\s*$/u.exec(message);
105
+ if (!match) {
106
+ return undefined;
107
+ }
108
+ return {
109
+ statusCode: Number(match[1]),
110
+ bodyText: match[2] ?? "",
111
+ };
112
+ }
113
+ function extractSdkHttpError(err) {
114
+ const chain = [];
115
+ let current = err;
116
+ while (current) {
117
+ chain.push(current);
118
+ current =
119
+ current instanceof Error && "cause" in current
120
+ ? current.cause
121
+ : undefined;
122
+ }
123
+ for (const item of chain) {
124
+ if (isSdkHttpErrorLike(item)) {
125
+ return item;
126
+ }
127
+ }
128
+ return undefined;
129
+ }
130
+ /**
131
+ * Resolve a CLI-safe gateway error message from SDK or agent middleware failures.
132
+ * Never echoes Cloudflare edge JSON, HTML, or raw upstream bodies.
133
+ */
134
+ export function resolveCliGatewayErrorMessage(err) {
135
+ const sdkError = extractSdkHttpError(err);
136
+ if (sdkError) {
137
+ return formatSdkHttpErrorMessage(sdkError.message, sdkError.statusCode, sdkError.bodyText);
138
+ }
139
+ if (err instanceof Error) {
140
+ const embedded = parseEmbeddedHttpErrorBody(err.message);
141
+ if (embedded) {
142
+ const operation = err.message.replace(/ HTTP \d{3}:[\s\S]*$/u, "").trim() || "Gateway request";
143
+ return formatSdkHttpErrorMessage(operation, embedded.statusCode, embedded.bodyText);
144
+ }
145
+ return err.message;
146
+ }
147
+ return String(err);
148
+ }
@@ -0,0 +1,9 @@
1
+ import type { CliContext } from "./context.js";
2
+ export type OfflineDevSession = {
3
+ ctx: CliContext;
4
+ restore: () => void;
5
+ };
6
+ /** Reject offline mode when production credentials are present in env or env file. */
7
+ export declare function assertOfflineDevCredentialsSafe(ctx: CliContext): Promise<void>;
8
+ /** Patch ctx.fetch with the in-process offline Gateway mock. */
9
+ export declare function beginOfflineDevSession(ctx: CliContext): OfflineDevSession;
@@ -0,0 +1,33 @@
1
+ import { activateOfflineDevMode, createOfflineDevGatewayFetch, isProductionApiKey, } from "../dev/offline-gateway.js";
2
+ import { loadEnvFile } from "./credentials.js";
3
+ import { CliError } from "./types.js";
4
+ function rejectOfflineWithProductionKey(apiKey) {
5
+ const trimmed = apiKey?.trim();
6
+ if (trimmed && isProductionApiKey(trimmed)) {
7
+ throw new CliError("offline dev mode cannot be used with production API keys (paybond_sk_live_...); unset PAYBOND_API_KEY or use a sandbox key", {
8
+ category: "validation",
9
+ code: "cli.dev.offline_production_key",
10
+ exitCode: 1,
11
+ });
12
+ }
13
+ }
14
+ /** Reject offline mode when production credentials are present in env or env file. */
15
+ export async function assertOfflineDevCredentialsSafe(ctx) {
16
+ rejectOfflineWithProductionKey(process.env.PAYBOND_API_KEY);
17
+ if (process.env.PAYBOND_API_KEY?.trim()) {
18
+ return;
19
+ }
20
+ const fromFile = await loadEnvFile(ctx.globals.envFile, ctx.cwd);
21
+ rejectOfflineWithProductionKey(fromFile);
22
+ }
23
+ /** Patch ctx.fetch with the in-process offline Gateway mock. */
24
+ export function beginOfflineDevSession(ctx) {
25
+ const { restore } = activateOfflineDevMode();
26
+ return {
27
+ ctx: {
28
+ ...ctx,
29
+ fetch: createOfflineDevGatewayFetch(),
30
+ },
31
+ restore,
32
+ };
33
+ }
@@ -1,4 +1,6 @@
1
+ import { HarborHttpError, SignalHttpError } from "../index.js";
1
2
  import { commandPath, createContext } from "./context.js";
3
+ import { formatSdkHttpErrorMessage, summarizeGatewayHttpError } from "./http-error-message.js";
2
4
  import { handleA2a, handleAuditExports, handleMandates, handleReceipts, handleSignal, } from "./commands/discovery.js";
3
5
  import { handleConfig, handleDoctor, handleDiagnose, handleInitCompletion, handleInitAgentMiddleware, handleInitGuardrail, handleInitWizard, handleLogin, handleMcpInstall, handleMcpServe, handleMcpTools, handleMcpVerifyConfig, handleVersion, handleWhoami, } from "./commands/setup.js";
4
6
  import { handlePolicyExtend, handlePolicyImportMcpReceipt, handlePolicyImportX402Receipt, handlePolicyInit, handlePolicyInitOrg, handlePolicyPresetsList, handlePolicyPresetsShow, handlePolicyPreview, handlePolicyTemplates, handlePolicyValidateEvidence, handlePolicyValidateTools, } from "./commands/policy.js";
@@ -13,7 +15,7 @@ import { generateRequestId } from "./request-id.js";
13
15
  import { colorize, shouldUseColor } from "./color.js";
14
16
  import { formatUnknownCommandMessage } from "./suggest.js";
15
17
  import { handleCompletionCommand, handleExamplesCommand, handleHelpCommand, handleOnboarding, } from "./ux.js";
16
- import { CliError, EXIT_SUCCESS, } from "./types.js";
18
+ import { CliError, EXIT_SUCCESS, exitCodeForHttpStatus, } from "./types.js";
17
19
  function isHelp(argv) {
18
20
  return argv.length === 0 || argv.includes("--help") || argv.includes("-h");
19
21
  }
@@ -36,6 +38,32 @@ function renderTable(command, result, globals) {
36
38
  }
37
39
  return lines;
38
40
  }
41
+ function isSdkHttpError(err) {
42
+ if (err instanceof HarborHttpError || err instanceof SignalHttpError) {
43
+ return true;
44
+ }
45
+ if (!(err instanceof Error)) {
46
+ return false;
47
+ }
48
+ const candidate = err;
49
+ return typeof candidate.statusCode === "number" && typeof candidate.bodyText === "string";
50
+ }
51
+ function sdkHttpErrorShape(err) {
52
+ const mapped = exitCodeForHttpStatus(err.statusCode);
53
+ const summary = summarizeGatewayHttpError(err.statusCode, err.bodyText);
54
+ const gatewayCode = typeof summary.details.gateway_code === "string"
55
+ ? summary.details.gateway_code
56
+ : undefined;
57
+ return {
58
+ shape: {
59
+ category: mapped.category,
60
+ code: gatewayCode || `cli.gateway.http_${err.statusCode}`,
61
+ message: formatSdkHttpErrorMessage(err.message, err.statusCode, err.bodyText),
62
+ details: summary.details,
63
+ },
64
+ exitCode: mapped.exitCode,
65
+ };
66
+ }
39
67
  function toErrorShape(err) {
40
68
  if (err instanceof CliError) {
41
69
  if (err.message === "help") {
@@ -54,6 +82,9 @@ function toErrorShape(err) {
54
82
  exitCode: err.exitCode,
55
83
  };
56
84
  }
85
+ if (isSdkHttpError(err)) {
86
+ return sdkHttpErrorShape(err);
87
+ }
57
88
  return {
58
89
  shape: {
59
90
  category: "internal",
@@ -319,7 +350,10 @@ export async function runCli(argv, deps = {}) {
319
350
  ctx.stdout.write(`${line}\n`);
320
351
  }
321
352
  }
322
- else if (canonical === "agent sandbox smoke" && Array.isArray(result.data.checklist_lines)) {
353
+ else if ((canonical === "agent sandbox smoke" ||
354
+ canonical === "agent production attach smoke" ||
355
+ canonical === "agent harbor evidence smoke") &&
356
+ Array.isArray(result.data.checklist_lines)) {
323
357
  writeTableLines(ctx.stdout, result.data.checklist_lines);
324
358
  if (output.warnings.length) {
325
359
  for (const warning of output.warnings) {
package/dist/cli.js CHANGED
@@ -2,6 +2,8 @@
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { deprecatedAliasWarning } from "./cli/automation.js";
6
+ import { mcpServeArgvMatches, runMcpServeCommandSync } from "./cli/commands/setup.js";
5
7
  import { runCli } from "./cli/router.js";
6
8
  async function invokedFromCLI() {
7
9
  const scriptPath = process.argv[1];
@@ -25,7 +27,19 @@ invokedFromCLI().then((invoked) => {
25
27
  if (!invoked) {
26
28
  return;
27
29
  }
28
- runCli(process.argv.slice(2)).then((code) => {
30
+ const argv = process.argv.slice(2);
31
+ const aliasWarning = deprecatedAliasWarning(process.argv[1]);
32
+ if (aliasWarning) {
33
+ process.stderr.write(`${aliasWarning}\n`);
34
+ }
35
+ if (mcpServeArgvMatches(argv)) {
36
+ process.exitCode = runMcpServeCommandSync(argv, {
37
+ stdout: process.stdout,
38
+ stderr: process.stderr,
39
+ });
40
+ return;
41
+ }
42
+ runCli(argv).then((code) => {
29
43
  process.exitCode = code;
30
44
  }, (err) => {
31
45
  process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
@@ -0,0 +1,16 @@
1
+ /** True when the body is a Cloudflare-generated edge error (not Paybond gateway JSON). */
2
+ export declare function isCloudflareEdgeErrorBody(bodyText: string): boolean;
3
+ /** Whether an HTTP status/body pair should be retried by SDK gateway clients. */
4
+ export declare function shouldRetryGatewayHttpStatus(status: number, bodyText: string): boolean;
5
+ /** Inspect a response body (via clone) to decide whether to retry transient gateway errors. */
6
+ export declare function shouldRetryGatewayResponse(res: Response): Promise<boolean>;
7
+ export declare function backoffMs(attempt: number): number;
8
+ export declare function parseRetryAfterSeconds(value: string | null): number | null;
9
+ /** Prefer `Retry-After` when present; otherwise exponential backoff with jitter. */
10
+ export declare function gatewayRetryDelayMs(attempt: number, retryAfterHeader: string | null): number;
11
+ /**
12
+ * Shared 429/5xx retry loop for Paybond gateway HTTP clients.
13
+ * Skips retries on Cloudflare edge error bodies. Returns the response on success;
14
+ * otherwise throws the last network error.
15
+ */
16
+ export declare function fetchWithGatewayRetries(url: string, init: RequestInit, maxRetries: number): Promise<Response>;
@@ -0,0 +1,83 @@
1
+ /** True when the body is a Cloudflare-generated edge error (not Paybond gateway JSON). */
2
+ export function isCloudflareEdgeErrorBody(bodyText) {
3
+ const trimmed = bodyText.trim();
4
+ if (!trimmed) {
5
+ return false;
6
+ }
7
+ try {
8
+ const body = JSON.parse(trimmed);
9
+ return body.cloudflare_error === true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ /** Whether an HTTP status/body pair should be retried by SDK gateway clients. */
16
+ export function shouldRetryGatewayHttpStatus(status, bodyText) {
17
+ if (![429, 500, 502, 503, 504].includes(status)) {
18
+ return false;
19
+ }
20
+ if (isCloudflareEdgeErrorBody(bodyText)) {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ /** Inspect a response body (via clone) to decide whether to retry transient gateway errors. */
26
+ export async function shouldRetryGatewayResponse(res) {
27
+ if (![429, 500, 502, 503, 504].includes(res.status)) {
28
+ return false;
29
+ }
30
+ const peek = await res.clone().text();
31
+ return shouldRetryGatewayHttpStatus(res.status, peek);
32
+ }
33
+ export function backoffMs(attempt) {
34
+ const base = 200 * 2 ** attempt;
35
+ const jitter = Math.random() * 100;
36
+ return Math.min(base + jitter, 5000);
37
+ }
38
+ export function parseRetryAfterSeconds(value) {
39
+ if (!value) {
40
+ return null;
41
+ }
42
+ const n = Number.parseFloat(value.trim());
43
+ if (!Number.isFinite(n)) {
44
+ return null;
45
+ }
46
+ return Math.min(n, 30);
47
+ }
48
+ /** Prefer `Retry-After` when present; otherwise exponential backoff with jitter. */
49
+ export function gatewayRetryDelayMs(attempt, retryAfterHeader) {
50
+ const raSec = parseRetryAfterSeconds(retryAfterHeader);
51
+ return raSec != null ? raSec * 1000 : backoffMs(attempt);
52
+ }
53
+ /**
54
+ * Shared 429/5xx retry loop for Paybond gateway HTTP clients.
55
+ * Skips retries on Cloudflare edge error bodies. Returns the response on success;
56
+ * otherwise throws the last network error.
57
+ */
58
+ export async function fetchWithGatewayRetries(url, init, maxRetries) {
59
+ let lastErr;
60
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
61
+ let res;
62
+ try {
63
+ res = await fetch(url, init);
64
+ }
65
+ catch (e) {
66
+ lastErr = e;
67
+ if (attempt + 1 >= maxRetries) {
68
+ throw e;
69
+ }
70
+ await new Promise((r) => setTimeout(r, gatewayRetryDelayMs(attempt, null)));
71
+ continue;
72
+ }
73
+ if ([429, 500, 502, 503, 504].includes(res.status) && attempt + 1 < maxRetries) {
74
+ if (!(await shouldRetryGatewayResponse(res))) {
75
+ return res;
76
+ }
77
+ await new Promise((r) => setTimeout(r, gatewayRetryDelayMs(attempt, res.headers.get("retry-after"))));
78
+ continue;
79
+ }
80
+ return res;
81
+ }
82
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
83
+ }
package/dist/index.d.ts CHANGED
@@ -1301,7 +1301,7 @@ export declare class Paybond {
1301
1301
  }
1302
1302
  export { normalizeJson, jsonValueDigest } from "./json-digest.js";
1303
1303
  export { buildSignedCreateIntentBody, buildSignedCreateIntentBodyWithPolicyBinding, intentCreationSignBytesRaw, intentCreationSignBytesWithPolicyBinding, type BuildSignedCreateIntentParams, type BuildSignedCreateIntentWithPolicyBindingParams, type PolicyBindingRef, type PublishedPolicyHead, type SettlementRail, } from "./principal-intent.js";
1304
- export { artifactsDigest, signPayeeEvidenceBinding, type SignPayeeEvidenceParams } from "./payee-evidence.js";
1304
+ export { artifactsDigest, evidenceSignBytesV1, signPayeeEvidenceBinding, type SignPayeeEvidenceParams, } from "./payee-evidence.js";
1305
1305
  export { AGENT_RECOGNITION_GATEWAY_VERIFIER_ID, AGENT_RECOGNITION_PROOF_KIND_V1, AGENT_RECOGNITION_PURPOSE_EVIDENCE_SUBMIT, newAgentRecognitionRequestEnvelope, signAgentRecognitionProofV1, signHarborEvidenceSubmitRecognitionProof, type SignAgentRecognitionProofV1Params, type SignedAgentRecognitionProofV1, } from "./agent-recognition.js";
1306
1306
  export { validateCompletionEvidence, type CompletionEvidenceValidationReport, } from "./completion-validate-evidence.js";
1307
1307
  export { completionSchemaDigestHex, computeVendorContractDigests, verifyVendorContract, verifyCatalogVendorContracts, type VendorContract, type VendorContractDigests, } from "./completion-contract-digest.js";