settld 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/README.md +93 -3
  2. package/SETTLD_VERSION +1 -1
  3. package/bin/settld-mcp +2 -0
  4. package/bin/settld.js +71 -0
  5. package/conformance/kernel-v0/README.md +7 -0
  6. package/conformance/kernel-v0/run.mjs +292 -4
  7. package/docs/ACCESS.md +57 -0
  8. package/docs/ADOPTION_CHECKLIST.md +44 -0
  9. package/docs/ALERTS.md +198 -0
  10. package/docs/ARCHITECTURE.md +69 -0
  11. package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
  12. package/docs/ARTIFACTS.md +60 -0
  13. package/docs/CERTIFICATION_CHECKLIST.md +33 -0
  14. package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
  15. package/docs/CONFIG.md +297 -0
  16. package/docs/CONTRACTS_APIS.md +23 -0
  17. package/docs/DEPRECATION.md +31 -0
  18. package/docs/DOMAIN_MODEL.md +92 -0
  19. package/docs/EVENT_ENVELOPE.md +53 -0
  20. package/docs/FINANCE_PACK_FORMAT.md +53 -0
  21. package/docs/INCIDENT_TAXONOMY.md +30 -0
  22. package/docs/JOB_STATE_MACHINE.md +66 -0
  23. package/docs/KERNEL_COMPATIBLE.md +60 -0
  24. package/docs/KERNEL_V0.md +40 -0
  25. package/docs/KEY_ROTATION.md +80 -0
  26. package/docs/LEDGER.md +82 -0
  27. package/docs/LIVENESS.md +76 -0
  28. package/docs/MVP_BUILD_ORDER.md +36 -0
  29. package/docs/ONCALL_PLAYBOOK.md +39 -0
  30. package/docs/OPERATIONS_SIGNING.md +20 -0
  31. package/docs/OVERVIEW.md +190 -0
  32. package/docs/PERF_BASELINE.md +85 -0
  33. package/docs/PRD.md +77 -0
  34. package/docs/QUICKSTART_KERNEL_V0.md +96 -0
  35. package/docs/QUICKSTART_MCP.md +377 -0
  36. package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
  37. package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
  38. package/docs/QUICKSTART_PRODUCE.md +61 -0
  39. package/docs/QUICKSTART_PROFILES.md +198 -0
  40. package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
  41. package/docs/QUICKSTART_SDK.md +125 -0
  42. package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
  43. package/docs/QUICKSTART_VERIFY.md +54 -0
  44. package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
  45. package/docs/README.md +33 -0
  46. package/docs/RELEASE_CHECKLIST.md +182 -0
  47. package/docs/RELEASING.md +82 -0
  48. package/docs/REPO_SETTINGS.md +37 -0
  49. package/docs/RUNBOOK.md +86 -0
  50. package/docs/SKILLS.md +42 -0
  51. package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
  52. package/docs/SLO.md +131 -0
  53. package/docs/SUMMARY.md +17 -0
  54. package/docs/SUPPORT.md +31 -0
  55. package/docs/THREAT_MODEL.md +36 -0
  56. package/docs/TRUST.md +59 -0
  57. package/docs/WORKFLOW.md +35 -0
  58. package/docs/X402_BATCH_SETTLEMENT.md +126 -0
  59. package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
  60. package/docs/examples/x402-provider-payout-registry.example.json +14 -0
  61. package/docs/gitbook/README.md +64 -0
  62. package/docs/gitbook/SETUP.md +25 -0
  63. package/docs/gitbook/SUMMARY.md +15 -0
  64. package/docs/gitbook/api-reference.md +73 -0
  65. package/docs/gitbook/closepacks.md +55 -0
  66. package/docs/gitbook/conformance.md +59 -0
  67. package/docs/gitbook/core-primitives.md +85 -0
  68. package/docs/gitbook/dispute-lifecycle.md +33 -0
  69. package/docs/gitbook/faq.md +21 -0
  70. package/docs/gitbook/guides.md +49 -0
  71. package/docs/gitbook/operations-runbook.md +36 -0
  72. package/docs/gitbook/quickstart.md +103 -0
  73. package/docs/gitbook/replay-and-audit.md +30 -0
  74. package/docs/gitbook/sdk-reference.md +35 -0
  75. package/docs/gitbook/security-model.md +58 -0
  76. package/docs/integrations/README.md +15 -0
  77. package/docs/integrations/github-actions-verify.yml +31 -0
  78. package/docs/integrations/github-actions.md +34 -0
  79. package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
  80. package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
  81. package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
  82. package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
  83. package/docs/kernel-compatible/capabilities.json +36 -0
  84. package/docs/marketing/agent-commerce-substrate.md +78 -0
  85. package/docs/marketing/hn-repost-2026-02-17.md +102 -0
  86. package/docs/marketing/show-hn-post.md +45 -0
  87. package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
  88. package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
  89. package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
  90. package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
  91. package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
  92. package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
  93. package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
  94. package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
  95. package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
  96. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
  97. package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
  98. package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
  99. package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
  100. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
  101. package/docs/ops/R1_SLOS.md +66 -0
  102. package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
  103. package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
  104. package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
  105. package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
  106. package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
  107. package/docs/ops/tool-call-disputes-holdback.md +52 -0
  108. package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
  109. package/docs/pilot-kit/README.md +29 -0
  110. package/docs/pilot-kit/architecture-one-pager.md +48 -0
  111. package/docs/pilot-kit/buyer-email.txt +19 -0
  112. package/docs/pilot-kit/buyer-one-pager.md +31 -0
  113. package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
  114. package/docs/pilot-kit/offline-verify.md +33 -0
  115. package/docs/pilot-kit/procurement-one-pager.md +50 -0
  116. package/docs/pilot-kit/rfp-clause.md +46 -0
  117. package/docs/pilot-kit/roi-calculator-template.csv +2 -0
  118. package/docs/pilot-kit/security-qa.md +153 -0
  119. package/docs/pilot-kit/security-summary.md +35 -0
  120. package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
  121. package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
  122. package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
  123. package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
  124. package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
  125. package/docs/spec/AcceptanceCriteria.v1.md +17 -0
  126. package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
  127. package/docs/spec/AgentEvent.v1.md +47 -0
  128. package/docs/spec/AgentIdentity.v1.md +62 -0
  129. package/docs/spec/AgentPassport.v1.md +95 -0
  130. package/docs/spec/AgentReputation.v1.md +59 -0
  131. package/docs/spec/AgentReputation.v2.md +52 -0
  132. package/docs/spec/AgentRun.v1.md +47 -0
  133. package/docs/spec/AgentRunSettlement.v1.md +52 -0
  134. package/docs/spec/AgentWallet.v1.md +43 -0
  135. package/docs/spec/AgreementDelegation.v1.md +109 -0
  136. package/docs/spec/ArbitrationCase.v1.md +67 -0
  137. package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
  138. package/docs/spec/ArbitrationVerdict.v1.md +60 -0
  139. package/docs/spec/BundleHeadAttestation.v1.md +32 -0
  140. package/docs/spec/CANONICAL_JSON.md +31 -0
  141. package/docs/spec/CRYPTOGRAPHY.md +61 -0
  142. package/docs/spec/ClosePack.v1.md +49 -0
  143. package/docs/spec/ClosePackManifest.v1.md +24 -0
  144. package/docs/spec/DelegationGrant.v1.md +90 -0
  145. package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
  146. package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
  147. package/docs/spec/ERRORS.md +76 -0
  148. package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
  149. package/docs/spec/EvidenceIndex.v1.md +20 -0
  150. package/docs/spec/ExecutionIntent.v1.md +90 -0
  151. package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
  152. package/docs/spec/FundingHold.v1.md +60 -0
  153. package/docs/spec/GovernancePolicy.v1.md +34 -0
  154. package/docs/spec/GovernancePolicy.v2.md +30 -0
  155. package/docs/spec/INVARIANTS.md +389 -0
  156. package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
  157. package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
  158. package/docs/spec/InvoiceClaim.v1.md +11 -0
  159. package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
  160. package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
  161. package/docs/spec/MarketplaceOffer.v2.md +54 -0
  162. package/docs/spec/MeteringReport.v1.md +18 -0
  163. package/docs/spec/OperatorAction.v1.md +90 -0
  164. package/docs/spec/PRODUCER_ERRORS.md +42 -0
  165. package/docs/spec/PolicyDecision.v1.md +83 -0
  166. package/docs/spec/PricingMatrix.v1.md +20 -0
  167. package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
  168. package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
  169. package/docs/spec/ProduceCliOutput.v1.md +46 -0
  170. package/docs/spec/ProofBundleManifest.v1.md +24 -0
  171. package/docs/spec/README.md +109 -0
  172. package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
  173. package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
  174. package/docs/spec/REMOTE_SIGNER.md +66 -0
  175. package/docs/spec/ReleaseIndex.v1.md +32 -0
  176. package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
  177. package/docs/spec/ReleaseTrust.v1.md +13 -0
  178. package/docs/spec/ReleaseTrust.v2.md +26 -0
  179. package/docs/spec/RemoteSignerRequest.v1.md +21 -0
  180. package/docs/spec/RemoteSignerResponse.v1.md +16 -0
  181. package/docs/spec/ReputationEvent.v1.md +63 -0
  182. package/docs/spec/RevocationList.v1.md +28 -0
  183. package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
  184. package/docs/spec/STRICTNESS.md +68 -0
  185. package/docs/spec/SUPPLY_CHAIN.md +33 -0
  186. package/docs/spec/SettlementAdjustment.v1.md +45 -0
  187. package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
  188. package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
  189. package/docs/spec/SettlementDecisionReport.v1.md +44 -0
  190. package/docs/spec/SettlementKernel.v1.md +59 -0
  191. package/docs/spec/SettlementReceipt.v1.md +63 -0
  192. package/docs/spec/SlaDefinition.v1.md +24 -0
  193. package/docs/spec/SlaEvaluation.v1.md +12 -0
  194. package/docs/spec/THREAT_MODEL.md +113 -0
  195. package/docs/spec/TOOL_PROVENANCE.md +30 -0
  196. package/docs/spec/TRUST_ANCHORS.md +84 -0
  197. package/docs/spec/TenantSettings.v1.md +90 -0
  198. package/docs/spec/TenantSettings.v2.md +99 -0
  199. package/docs/spec/TimestampProof.v1.md +25 -0
  200. package/docs/spec/ToolCallAgreement.v1.md +34 -0
  201. package/docs/spec/ToolCallEvidence.v1.md +47 -0
  202. package/docs/spec/ToolManifest.v1.md +47 -0
  203. package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
  204. package/docs/spec/VERSIONING.md +107 -0
  205. package/docs/spec/VerificationReport.v1.md +50 -0
  206. package/docs/spec/VerifyAboutOutput.v1.md +10 -0
  207. package/docs/spec/VerifyCliOutput.v1.md +28 -0
  208. package/docs/spec/WARNINGS.md +83 -0
  209. package/docs/spec/error-codes.v1.txt +285 -0
  210. package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
  211. package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
  212. package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
  213. package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
  214. package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
  215. package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
  216. package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
  217. package/docs/spec/examples/release_index_v1.example.json +15 -0
  218. package/docs/spec/examples/release_trust_v1.example.json +7 -0
  219. package/docs/spec/examples/release_trust_v2.example.json +22 -0
  220. package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
  221. package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
  222. package/docs/spec/examples/reputation_event_v1.example.json +29 -0
  223. package/docs/spec/examples/verification_report_v1.example.json +24 -0
  224. package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
  225. package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
  226. package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
  227. package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
  228. package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
  229. package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
  230. package/docs/spec/producer-error-codes.v1.txt +14 -0
  231. package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
  232. package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
  233. package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
  234. package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
  235. package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
  236. package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
  237. package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
  238. package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
  239. package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
  240. package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
  241. package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
  242. package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
  243. package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
  244. package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
  245. package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
  246. package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
  247. package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
  248. package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
  249. package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
  250. package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
  251. package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
  252. package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
  253. package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
  254. package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
  255. package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
  256. package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
  257. package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
  258. package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
  259. package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
  260. package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
  261. package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
  262. package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
  263. package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
  264. package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
  265. package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
  266. package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
  267. package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
  268. package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
  269. package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
  270. package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
  271. package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
  272. package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
  273. package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
  274. package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
  275. package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
  276. package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
  277. package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
  278. package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
  279. package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
  280. package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
  281. package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
  282. package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
  283. package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
  284. package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
  285. package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
  286. package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
  287. package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
  288. package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
  289. package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
  290. package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
  291. package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
  292. package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
  293. package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
  294. package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
  295. package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
  296. package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
  297. package/docs/spec/x402-error-codes.v1.txt +35 -0
  298. package/docs/templates/buyer-email.txt +18 -0
  299. package/docs/templates/buyer-one-pager.md +24 -0
  300. package/package.json +53 -6
  301. package/scripts/acceptance/full-stack.mjs +734 -0
  302. package/scripts/acceptance/full-stack.sh +99 -0
  303. package/scripts/audit/build-audit-packet.mjs +242 -0
  304. package/scripts/backup-pg.sh +45 -0
  305. package/scripts/backup-restore/README.md +18 -0
  306. package/scripts/backup-restore/capture-state.mjs +130 -0
  307. package/scripts/backup-restore/client.mjs +97 -0
  308. package/scripts/backup-restore/seed-workload.mjs +235 -0
  309. package/scripts/backup-restore/verify-state.mjs +139 -0
  310. package/scripts/backup-restore-test.sh +217 -0
  311. package/scripts/chaos.js +221 -0
  312. package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
  313. package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
  314. package/scripts/ci/changelog-guard.mjs +145 -0
  315. package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
  316. package/scripts/ci/check-secret-hygiene.mjs +78 -0
  317. package/scripts/ci/check-version-consistency.mjs +42 -0
  318. package/scripts/ci/cli-pack-smoke.mjs +160 -0
  319. package/scripts/ci/flake-budget-guard.mjs +68 -0
  320. package/scripts/ci/generate-error-codes.mjs +54 -0
  321. package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
  322. package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
  323. package/scripts/ci/npm-pack-smoke.mjs +454 -0
  324. package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
  325. package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
  326. package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
  327. package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
  328. package/scripts/ci/run-go-live-gate.mjs +150 -0
  329. package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
  330. package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
  331. package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
  332. package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
  333. package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
  334. package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
  335. package/scripts/ci/run-production-cutover-gate.mjs +540 -0
  336. package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
  337. package/scripts/ci/run-release-promotion-guard.mjs +756 -0
  338. package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
  339. package/scripts/ci/runtime-import-smoke.mjs +58 -0
  340. package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
  341. package/scripts/closepack/lib.mjs +286 -0
  342. package/scripts/collect-debug.sh +263 -0
  343. package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
  344. package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
  345. package/scripts/demo/delivery-robot/generate.mjs +377 -0
  346. package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
  347. package/scripts/demo/magic-link-first-green.mjs +118 -0
  348. package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
  349. package/scripts/demo/mcp-paid-exa.mjs +1110 -0
  350. package/scripts/dev/billing-doctor.sh +145 -0
  351. package/scripts/dev/billing-smoke-prod.sh +219 -0
  352. package/scripts/dev/billing-webhook-replay.sh +161 -0
  353. package/scripts/dev/env.dev.example +29 -0
  354. package/scripts/dev/env.sh +37 -0
  355. package/scripts/dev/new-sdk-key.sh +81 -0
  356. package/scripts/dev/sdk-first-run.sh +21 -0
  357. package/scripts/dev/smoke-x402-gateway.sh +115 -0
  358. package/scripts/dev/start-api.sh +24 -0
  359. package/scripts/doctor/mcp-host.mjs +120 -0
  360. package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
  361. package/scripts/examples/sdk-first-paid-rfq.py +105 -0
  362. package/scripts/examples/sdk-first-verified-run.mjs +85 -0
  363. package/scripts/examples/sdk-first-verified-run.py +99 -0
  364. package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
  365. package/scripts/examples/sdk-tenant-analytics.py +118 -0
  366. package/scripts/finance-pack/bundle.mjs +284 -0
  367. package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
  368. package/scripts/governance/export.mjs +169 -0
  369. package/scripts/load/delivery-stress.k6.js +183 -0
  370. package/scripts/load/ingest-burst.k6.js +236 -0
  371. package/scripts/load/run-delivery-load.js +66 -0
  372. package/scripts/load/webhook-receiver.js +131 -0
  373. package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
  374. package/scripts/mcp/probe.mjs +238 -0
  375. package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
  376. package/scripts/mcp/settld-mcp-server.mjs +1511 -0
  377. package/scripts/openapi/write.mjs +13 -0
  378. package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
  379. package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
  380. package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
  381. package/scripts/ops/design-partner-run-packet.mjs +466 -0
  382. package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
  383. package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
  384. package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
  385. package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
  386. package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
  387. package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
  388. package/scripts/pilot/finance-pack.mjs +495 -0
  389. package/scripts/pilot/fixtures/robot-keypair.json +4 -0
  390. package/scripts/pilot/fixtures/server-signer.json +4 -0
  391. package/scripts/policy/cli.mjs +600 -0
  392. package/scripts/profile/cli.mjs +1324 -0
  393. package/scripts/proof-bundle/job.mjs +109 -0
  394. package/scripts/proof-bundle/lib.mjs +92 -0
  395. package/scripts/proof-bundle/month.mjs +103 -0
  396. package/scripts/provider/conformance-run.mjs +159 -0
  397. package/scripts/provider/keys-generate.mjs +135 -0
  398. package/scripts/provider/publish.mjs +420 -0
  399. package/scripts/quickstart/x402.mjs +334 -0
  400. package/scripts/register-entity-secret.mjs +102 -0
  401. package/scripts/release/build-artifacts.mjs +181 -0
  402. package/scripts/release/generate-release-index.mjs +112 -0
  403. package/scripts/release/release-index-lib.mjs +232 -0
  404. package/scripts/release/sign-release-index.mjs +85 -0
  405. package/scripts/release/validate-release-assets.mjs +170 -0
  406. package/scripts/release/verify-release.mjs +261 -0
  407. package/scripts/restore-pg.sh +34 -0
  408. package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
  409. package/scripts/sdk/smoke-python.py +30 -0
  410. package/scripts/sdk/smoke.mjs +16 -0
  411. package/scripts/settlement/x402-batch-worker.mjs +1091 -0
  412. package/scripts/setup/circle-bootstrap.mjs +310 -0
  413. package/scripts/setup/host-config.mjs +617 -0
  414. package/scripts/setup/onboard.mjs +1337 -0
  415. package/scripts/setup/openclaw-onboard.mjs +423 -0
  416. package/scripts/setup/wizard.mjs +986 -0
  417. package/scripts/slo/check.mjs +239 -0
  418. package/scripts/smoke/k8s-smoke.mjs +214 -0
  419. package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
  420. package/scripts/test/check-no-generated-artifacts.sh +12 -0
  421. package/scripts/test/run.sh +59 -0
  422. package/scripts/trust/validate-trust-file.mjs +57 -0
  423. package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
  424. package/scripts/trust-config/wizard.mjs +161 -0
  425. package/scripts/vendor-contract-test-lib.mjs +182 -0
  426. package/scripts/vendor-contract-test.mjs +55 -0
  427. package/scripts/vercel/build-mkdocs.sh +9 -0
  428. package/scripts/vercel/ignore-mkdocs.sh +25 -0
  429. package/scripts/vercel/install-mkdocs.sh +6 -0
  430. package/scripts/verify-pg.js +217 -0
  431. package/scripts/x402/receipt-verify.mjs +289 -0
  432. package/services/finance-sink/src/dedupe-store.js +29 -6
  433. package/services/receiver/src/dedupe-store.js +29 -5
  434. package/services/x402-gateway/Dockerfile +13 -0
  435. package/services/x402-gateway/README.md +58 -0
  436. package/services/x402-gateway/examples/upstream-mock.js +337 -0
  437. package/services/x402-gateway/src/server.js +1058 -0
  438. package/src/api/app.js +34658 -16940
  439. package/src/api/maintenance.js +70 -0
  440. package/src/api/middleware/trust-kernel.js +114 -0
  441. package/src/api/openapi.js +1778 -70
  442. package/src/api/persistence.js +456 -0
  443. package/src/api/server.js +81 -5
  444. package/src/api/store.js +1581 -62
  445. package/src/api/workers/deliveries.js +99 -4
  446. package/src/api/workers/insolvency-sweep.js +159 -0
  447. package/src/core/agent-card.js +69 -0
  448. package/src/core/agent-wallets.js +231 -0
  449. package/src/core/agreement-delegation.js +549 -0
  450. package/src/core/billing-plans.js +40 -6
  451. package/src/core/circle-reserve-adapter.js +845 -0
  452. package/src/core/event-policy.js +21 -2
  453. package/src/core/maintenance-locks.js +1 -0
  454. package/src/core/operator-action.js +303 -0
  455. package/src/core/paid-tool-manifest.js +318 -0
  456. package/src/core/policy-decision.js +322 -0
  457. package/src/core/policy-packs.js +207 -0
  458. package/src/core/profile-fingerprint.js +27 -0
  459. package/src/core/profile-simulation-reasons.js +84 -0
  460. package/src/core/profile-templates.js +242 -0
  461. package/src/core/provider-publish-conformance.js +525 -0
  462. package/src/core/provider-publish-proof.js +396 -0
  463. package/src/core/provider-quote-signature.js +170 -0
  464. package/src/core/settld-keys.js +112 -0
  465. package/src/core/settld-pay-token.js +344 -0
  466. package/src/core/settlement-kernel.js +239 -2
  467. package/src/core/settlement-verifier.js +335 -0
  468. package/src/core/tool-call-agreement.js +112 -0
  469. package/src/core/tool-call-evidence.js +144 -0
  470. package/src/core/tool-provider-signature.js +98 -0
  471. package/src/core/wallet-assignment-resolver.js +129 -0
  472. package/src/core/wallet-provider-bootstrap.js +365 -0
  473. package/src/core/x402-escalation-override.js +258 -0
  474. package/src/core/x402-gate.js +118 -0
  475. package/src/core/x402-provider-refund-decision.js +220 -0
  476. package/src/core/x402-receipt-verifier.js +708 -0
  477. package/src/core/x402-reversal-command.js +251 -0
  478. package/src/core/x402-wallet-issuer-decision.js +252 -0
  479. package/src/core/zk-verifier.js +300 -0
  480. package/src/db/migrations/029_reputation_event_index.sql +54 -0
  481. package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
  482. package/src/db/pg.js +18 -7
  483. package/src/db/store-pg.js +1508 -111
@@ -0,0 +1,1324 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { createInterface } from "node:readline/promises";
5
+
6
+ import { canonicalJsonStringify, normalizeForCanonicalJson } from "../../src/core/canonical-json.js";
7
+ import { sha256Hex } from "../../src/core/crypto.js";
8
+ import { computeProfileFingerprint } from "../../src/core/profile-fingerprint.js";
9
+ import {
10
+ PROFILE_SIMULATION_REASON_REGISTRY_VERSION,
11
+ mapProfileSimulationReasons
12
+ } from "../../src/core/profile-simulation-reasons.js";
13
+ import {
14
+ PROFILE_TEMPLATE_CATALOG_VERSION,
15
+ PROFILE_SCHEMA_VERSION,
16
+ createStarterProfile,
17
+ listProfileTemplates
18
+ } from "../../src/core/profile-templates.js";
19
+
20
+ const VALIDATION_REPORT_SCHEMA_VERSION = "SettldProfileValidationReport.v1";
21
+ const SIMULATION_REPORT_SCHEMA_VERSION = "SettldProfileSimulationReport.v1";
22
+ const APPLY_REPORT_SCHEMA_VERSION = "SettldProfileApplyReport.v1";
23
+ const DEFAULT_WIZARD_TEMPLATE_ID = "engineering-spend";
24
+
25
+ function usage() {
26
+ const lines = [
27
+ "usage:",
28
+ " settld profile list [--format json|text] [--json-out <path>]",
29
+ " settld profile init <profile-id> [--out <path>] [--force] [--format json|text] [--json-out <path>]",
30
+ " settld profile wizard [--template <profile-id>] [--non-interactive] [--profile-id <id>] [--name <text>] [--vertical <text>] [--description <text>] [--currency <code>] [--per-request-usd-cents <int>] [--monthly-usd-cents <int>] [--providers <csv>] [--tools <csv>] [--out <path>] [--force] [--format json|text] [--json-out <path>]",
31
+ " settld profile validate <profile.json|-> [--format json|text] [--json-out <path>]",
32
+ " settld profile simulate <profile.json|-> [--scenario <scenario.json|->|--scenario-json <json>] [--format json|text] [--json-out <path>]",
33
+ " settld profile apply <profile.json|-> [--base-url <url>] [--tenant-id <id>] [--api-key <key>] [--sponsor-ref <id>] [--sponsor-wallet-ref <id>] [--policy-ref <id>] [--policy-version <int>] [--idempotency-prefix <prefix>] [--dry-run] [--format json|text] [--json-out <path>]"
34
+ ];
35
+ process.stderr.write(`${lines.join("\n")}\n`);
36
+ }
37
+
38
+ function fail(message) {
39
+ throw new Error(String(message));
40
+ }
41
+
42
+ function parseArgs(argv) {
43
+ const out = {
44
+ command: String(argv[0] ?? "").trim() || null,
45
+ profileId: null,
46
+ inputPath: null,
47
+ scenarioPath: null,
48
+ scenarioJson: null,
49
+ outPath: null,
50
+ jsonOut: null,
51
+ format: "text",
52
+ force: false,
53
+ help: false,
54
+ baseUrl: null,
55
+ tenantId: null,
56
+ apiKey: null,
57
+ sponsorRef: null,
58
+ sponsorWalletRef: null,
59
+ policyRef: null,
60
+ policyVersion: null,
61
+ idempotencyPrefix: null,
62
+ dryRun: false,
63
+ templateId: null,
64
+ nonInteractive: false,
65
+ wizardProfileId: null,
66
+ metadataName: null,
67
+ metadataVertical: null,
68
+ metadataDescription: null,
69
+ currency: null,
70
+ perRequestUsdCents: null,
71
+ monthlyUsdCents: null,
72
+ providersCsv: null,
73
+ toolsCsv: null
74
+ };
75
+
76
+ for (let i = 1; i < argv.length; i += 1) {
77
+ const arg = String(argv[i] ?? "").trim();
78
+ if (!arg) continue;
79
+
80
+ if (arg === "--help" || arg === "-h") {
81
+ out.help = true;
82
+ continue;
83
+ }
84
+ if (arg === "--format") {
85
+ out.format = String(argv[i + 1] ?? "").trim().toLowerCase();
86
+ i += 1;
87
+ continue;
88
+ }
89
+ if (arg === "--json-out") {
90
+ out.jsonOut = String(argv[i + 1] ?? "").trim();
91
+ i += 1;
92
+ continue;
93
+ }
94
+ if (arg === "--out") {
95
+ out.outPath = String(argv[i + 1] ?? "").trim();
96
+ i += 1;
97
+ continue;
98
+ }
99
+ if (arg === "--force") {
100
+ out.force = true;
101
+ continue;
102
+ }
103
+ if (arg === "--profile") {
104
+ out.profileId = String(argv[i + 1] ?? "").trim();
105
+ i += 1;
106
+ continue;
107
+ }
108
+ if (arg === "--profile-id") {
109
+ out.wizardProfileId = String(argv[i + 1] ?? "").trim();
110
+ i += 1;
111
+ continue;
112
+ }
113
+ if (arg === "--template") {
114
+ out.templateId = String(argv[i + 1] ?? "").trim();
115
+ i += 1;
116
+ continue;
117
+ }
118
+ if (arg === "--non-interactive" || arg === "--yes") {
119
+ out.nonInteractive = true;
120
+ continue;
121
+ }
122
+ if (arg === "--name") {
123
+ out.metadataName = String(argv[i + 1] ?? "").trim();
124
+ i += 1;
125
+ continue;
126
+ }
127
+ if (arg === "--vertical") {
128
+ out.metadataVertical = String(argv[i + 1] ?? "").trim();
129
+ i += 1;
130
+ continue;
131
+ }
132
+ if (arg === "--description") {
133
+ out.metadataDescription = String(argv[i + 1] ?? "").trim();
134
+ i += 1;
135
+ continue;
136
+ }
137
+ if (arg === "--currency") {
138
+ out.currency = String(argv[i + 1] ?? "").trim();
139
+ i += 1;
140
+ continue;
141
+ }
142
+ if (arg === "--per-request-usd-cents") {
143
+ out.perRequestUsdCents = String(argv[i + 1] ?? "").trim();
144
+ i += 1;
145
+ continue;
146
+ }
147
+ if (arg === "--monthly-usd-cents") {
148
+ out.monthlyUsdCents = String(argv[i + 1] ?? "").trim();
149
+ i += 1;
150
+ continue;
151
+ }
152
+ if (arg === "--providers") {
153
+ out.providersCsv = String(argv[i + 1] ?? "").trim();
154
+ i += 1;
155
+ continue;
156
+ }
157
+ if (arg === "--tools") {
158
+ out.toolsCsv = String(argv[i + 1] ?? "").trim();
159
+ i += 1;
160
+ continue;
161
+ }
162
+ if (arg === "--in") {
163
+ out.inputPath = String(argv[i + 1] ?? "").trim();
164
+ i += 1;
165
+ continue;
166
+ }
167
+ if (arg === "--scenario") {
168
+ out.scenarioPath = String(argv[i + 1] ?? "").trim();
169
+ i += 1;
170
+ continue;
171
+ }
172
+ if (arg === "--scenario-json") {
173
+ out.scenarioJson = String(argv[i + 1] ?? "").trim();
174
+ i += 1;
175
+ continue;
176
+ }
177
+ if (arg === "--base-url") {
178
+ out.baseUrl = String(argv[i + 1] ?? "").trim();
179
+ i += 1;
180
+ continue;
181
+ }
182
+ if (arg === "--tenant-id") {
183
+ out.tenantId = String(argv[i + 1] ?? "").trim();
184
+ i += 1;
185
+ continue;
186
+ }
187
+ if (arg === "--api-key") {
188
+ out.apiKey = String(argv[i + 1] ?? "").trim();
189
+ i += 1;
190
+ continue;
191
+ }
192
+ if (arg === "--sponsor-ref") {
193
+ out.sponsorRef = String(argv[i + 1] ?? "").trim();
194
+ i += 1;
195
+ continue;
196
+ }
197
+ if (arg === "--sponsor-wallet-ref") {
198
+ out.sponsorWalletRef = String(argv[i + 1] ?? "").trim();
199
+ i += 1;
200
+ continue;
201
+ }
202
+ if (arg === "--policy-ref") {
203
+ out.policyRef = String(argv[i + 1] ?? "").trim();
204
+ i += 1;
205
+ continue;
206
+ }
207
+ if (arg === "--policy-version") {
208
+ out.policyVersion = String(argv[i + 1] ?? "").trim();
209
+ i += 1;
210
+ continue;
211
+ }
212
+ if (arg === "--idempotency-prefix") {
213
+ out.idempotencyPrefix = String(argv[i + 1] ?? "").trim();
214
+ i += 1;
215
+ continue;
216
+ }
217
+ if (arg === "--dry-run") {
218
+ out.dryRun = true;
219
+ continue;
220
+ }
221
+
222
+ if (arg !== "-" && arg.startsWith("-")) fail(`unknown argument: ${arg}`);
223
+
224
+ if (out.command === "init" && !out.profileId) {
225
+ out.profileId = arg;
226
+ continue;
227
+ }
228
+ if ((out.command === "validate" || out.command === "simulate" || out.command === "apply") && !out.inputPath) {
229
+ out.inputPath = arg;
230
+ continue;
231
+ }
232
+ fail(`unexpected positional argument: ${arg}`);
233
+ }
234
+
235
+ if (!out.command || out.command === "--help" || out.command === "-h") {
236
+ out.help = true;
237
+ out.command = out.command && out.command.startsWith("-") ? "list" : out.command;
238
+ return out;
239
+ }
240
+ if (!["list", "init", "wizard", "validate", "simulate", "apply"].includes(out.command)) fail(`unsupported command: ${out.command}`);
241
+ if (out.format !== "json" && out.format !== "text") fail("--format must be json or text");
242
+ if (out.command === "init" && !out.profileId) fail("profile id is required for init");
243
+ if ((out.command === "validate" || out.command === "simulate" || out.command === "apply") && !out.inputPath) {
244
+ fail("profile input is required");
245
+ }
246
+ if (out.command !== "simulate" && (out.scenarioPath || out.scenarioJson)) fail("--scenario/--scenario-json only apply to simulate");
247
+ if (out.scenarioPath && out.scenarioJson) fail("choose one of --scenario or --scenario-json");
248
+ const applyOptionUsed =
249
+ out.baseUrl !== null ||
250
+ out.tenantId !== null ||
251
+ out.apiKey !== null ||
252
+ out.sponsorRef !== null ||
253
+ out.sponsorWalletRef !== null ||
254
+ out.policyRef !== null ||
255
+ out.policyVersion !== null ||
256
+ out.idempotencyPrefix !== null ||
257
+ out.dryRun;
258
+ if (out.command !== "apply" && applyOptionUsed) {
259
+ fail("--base-url/--tenant-id/--api-key/--sponsor-ref/--sponsor-wallet-ref/--policy-ref/--policy-version/--idempotency-prefix/--dry-run only apply to apply");
260
+ }
261
+ const wizardOptionUsed =
262
+ out.templateId !== null ||
263
+ out.nonInteractive === true ||
264
+ out.wizardProfileId !== null ||
265
+ out.metadataName !== null ||
266
+ out.metadataVertical !== null ||
267
+ out.metadataDescription !== null ||
268
+ out.currency !== null ||
269
+ out.perRequestUsdCents !== null ||
270
+ out.monthlyUsdCents !== null ||
271
+ out.providersCsv !== null ||
272
+ out.toolsCsv !== null;
273
+ if (out.command !== "wizard" && wizardOptionUsed) {
274
+ fail("--template/--non-interactive/--profile-id/--name/--vertical/--description/--currency/--per-request-usd-cents/--monthly-usd-cents/--providers/--tools only apply to wizard");
275
+ }
276
+ if (out.command === "apply") {
277
+ if (out.policyVersion === null || out.policyVersion === undefined || out.policyVersion === "") {
278
+ out.policyVersion = 1;
279
+ } else {
280
+ const parsedVersion = Number(out.policyVersion);
281
+ if (!Number.isSafeInteger(parsedVersion) || parsedVersion < 1) fail("--policy-version must be an integer >= 1");
282
+ out.policyVersion = parsedVersion;
283
+ }
284
+ }
285
+ return out;
286
+ }
287
+
288
+ function isPlainObject(value) {
289
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
290
+ const proto = Object.getPrototypeOf(value);
291
+ return proto === Object.prototype || proto === null;
292
+ }
293
+
294
+ function addValidationError(report, { code, path: errorPath, message }) {
295
+ report.errors.push({ code: String(code), path: String(errorPath), message: String(message) });
296
+ }
297
+
298
+ function validateString(report, value, fieldPath) {
299
+ if (typeof value !== "string" || !value.trim()) {
300
+ addValidationError(report, { code: "invalid_string", path: fieldPath, message: "must be a non-empty string" });
301
+ return false;
302
+ }
303
+ return true;
304
+ }
305
+
306
+ function validateSafeInt(report, value, fieldPath, { min = 0 } = {}) {
307
+ if (!Number.isSafeInteger(value) || value < min) {
308
+ addValidationError(report, { code: "invalid_integer", path: fieldPath, message: `must be a safe integer >= ${min}` });
309
+ return false;
310
+ }
311
+ return true;
312
+ }
313
+
314
+ function validateStringArray(report, value, fieldPath) {
315
+ if (!Array.isArray(value)) {
316
+ addValidationError(report, { code: "invalid_array", path: fieldPath, message: "must be an array of strings" });
317
+ return false;
318
+ }
319
+ let ok = true;
320
+ const seen = new Set();
321
+ for (let i = 0; i < value.length; i += 1) {
322
+ const item = value[i];
323
+ const itemPath = `${fieldPath}[${i}]`;
324
+ if (!validateString(report, item, itemPath)) {
325
+ ok = false;
326
+ continue;
327
+ }
328
+ if (seen.has(item)) {
329
+ addValidationError(report, { code: "duplicate_value", path: itemPath, message: "must not contain duplicates" });
330
+ ok = false;
331
+ continue;
332
+ }
333
+ seen.add(item);
334
+ }
335
+ return ok;
336
+ }
337
+
338
+ function validateProfileDocument(profile) {
339
+ const report = {
340
+ schemaVersion: VALIDATION_REPORT_SCHEMA_VERSION,
341
+ ok: false,
342
+ profileId: typeof profile?.profileId === "string" ? profile.profileId : null,
343
+ errors: [],
344
+ warnings: []
345
+ };
346
+
347
+ if (!isPlainObject(profile)) {
348
+ addValidationError(report, { code: "invalid_profile", path: "$", message: "profile must be an object" });
349
+ return report;
350
+ }
351
+
352
+ if (profile.schemaVersion !== PROFILE_SCHEMA_VERSION) {
353
+ addValidationError(report, { code: "unsupported_schema_version", path: "$.schemaVersion", message: `must be ${PROFILE_SCHEMA_VERSION}` });
354
+ }
355
+
356
+ validateString(report, profile.profileId, "$.profileId");
357
+
358
+ if (!isPlainObject(profile.metadata)) {
359
+ addValidationError(report, { code: "invalid_metadata", path: "$.metadata", message: "metadata must be an object" });
360
+ } else {
361
+ validateString(report, profile.metadata.name, "$.metadata.name");
362
+ validateString(report, profile.metadata.vertical, "$.metadata.vertical");
363
+ validateString(report, profile.metadata.description, "$.metadata.description");
364
+ }
365
+
366
+ if (!isPlainObject(profile.policy)) {
367
+ addValidationError(report, { code: "invalid_policy", path: "$.policy", message: "policy must be an object" });
368
+ return report;
369
+ }
370
+
371
+ validateString(report, profile.policy.currency, "$.policy.currency");
372
+
373
+ if (!isPlainObject(profile.policy.limits)) {
374
+ addValidationError(report, { code: "invalid_limits", path: "$.policy.limits", message: "limits must be an object" });
375
+ } else {
376
+ validateSafeInt(report, profile.policy.limits.perRequestUsdCents, "$.policy.limits.perRequestUsdCents", { min: 1 });
377
+ validateSafeInt(report, profile.policy.limits.monthlyUsdCents, "$.policy.limits.monthlyUsdCents", { min: 1 });
378
+ if (
379
+ Number.isSafeInteger(profile.policy.limits.perRequestUsdCents) &&
380
+ Number.isSafeInteger(profile.policy.limits.monthlyUsdCents) &&
381
+ profile.policy.limits.perRequestUsdCents > profile.policy.limits.monthlyUsdCents
382
+ ) {
383
+ addValidationError(report, {
384
+ code: "limits_inconsistent",
385
+ path: "$.policy.limits",
386
+ message: "perRequestUsdCents must be <= monthlyUsdCents"
387
+ });
388
+ }
389
+ }
390
+
391
+ if (!isPlainObject(profile.policy.allowlists)) {
392
+ addValidationError(report, { code: "invalid_allowlists", path: "$.policy.allowlists", message: "allowlists must be an object" });
393
+ } else {
394
+ validateStringArray(report, profile.policy.allowlists.providers, "$.policy.allowlists.providers");
395
+ validateStringArray(report, profile.policy.allowlists.tools, "$.policy.allowlists.tools");
396
+ }
397
+
398
+ if (!Array.isArray(profile.policy.approvalTiers) || profile.policy.approvalTiers.length === 0) {
399
+ addValidationError(report, {
400
+ code: "invalid_approval_tiers",
401
+ path: "$.policy.approvalTiers",
402
+ message: "approvalTiers must be a non-empty array"
403
+ });
404
+ } else {
405
+ let previousMax = -1;
406
+ for (let i = 0; i < profile.policy.approvalTiers.length; i += 1) {
407
+ const tier = profile.policy.approvalTiers[i];
408
+ const tierPath = `$.policy.approvalTiers[${i}]`;
409
+ if (!isPlainObject(tier)) {
410
+ addValidationError(report, { code: "invalid_tier", path: tierPath, message: "tier must be an object" });
411
+ continue;
412
+ }
413
+ validateString(report, tier.tierId, `${tierPath}.tierId`);
414
+ validateSafeInt(report, tier.maxAmountUsdCents, `${tierPath}.maxAmountUsdCents`, { min: 0 });
415
+ validateSafeInt(report, tier.requiredApprovers, `${tierPath}.requiredApprovers`, { min: 0 });
416
+ validateString(report, tier.approverRole, `${tierPath}.approverRole`);
417
+ if (Number.isSafeInteger(tier.maxAmountUsdCents) && tier.maxAmountUsdCents <= previousMax) {
418
+ addValidationError(report, {
419
+ code: "tier_order_invalid",
420
+ path: `${tierPath}.maxAmountUsdCents`,
421
+ message: "maxAmountUsdCents must increase monotonically"
422
+ });
423
+ }
424
+ if (Number.isSafeInteger(tier.maxAmountUsdCents)) previousMax = tier.maxAmountUsdCents;
425
+ }
426
+ }
427
+
428
+ if (!isPlainObject(profile.policy.disputeDefaults)) {
429
+ addValidationError(report, {
430
+ code: "invalid_dispute_defaults",
431
+ path: "$.policy.disputeDefaults",
432
+ message: "disputeDefaults must be an object"
433
+ });
434
+ } else {
435
+ validateSafeInt(report, profile.policy.disputeDefaults.responseWindowHours, "$.policy.disputeDefaults.responseWindowHours", { min: 1 });
436
+ if (typeof profile.policy.disputeDefaults.autoOpenIfReceiptMissing !== "boolean") {
437
+ addValidationError(report, {
438
+ code: "invalid_boolean",
439
+ path: "$.policy.disputeDefaults.autoOpenIfReceiptMissing",
440
+ message: "must be a boolean"
441
+ });
442
+ }
443
+ validateStringArray(report, profile.policy.disputeDefaults.evidenceChecklist, "$.policy.disputeDefaults.evidenceChecklist");
444
+ }
445
+
446
+ if (!isPlainObject(profile.policy.compliance)) {
447
+ addValidationError(report, { code: "invalid_compliance", path: "$.policy.compliance", message: "compliance must be an object" });
448
+ } else {
449
+ const boolKeys = ["enforceVendorAllowlist", "requireReceiptSignature", "requireToolManifestHash", "allowUnknownToolVersion"];
450
+ for (const key of boolKeys) {
451
+ if (typeof profile.policy.compliance[key] !== "boolean") {
452
+ addValidationError(report, { code: "invalid_boolean", path: `$.policy.compliance.${key}`, message: "must be a boolean" });
453
+ }
454
+ }
455
+ }
456
+
457
+ report.ok = report.errors.length === 0;
458
+ return report;
459
+ }
460
+
461
+ function buildDefaultScenario(profile) {
462
+ const providers = Array.isArray(profile?.policy?.allowlists?.providers) ? profile.policy.allowlists.providers : [];
463
+ const tools = Array.isArray(profile?.policy?.allowlists?.tools) ? profile.policy.allowlists.tools : [];
464
+ return {
465
+ providerId: providers[0] ?? "",
466
+ toolId: tools[0] ?? "",
467
+ amountUsdCents: 0,
468
+ monthToDateSpendUsdCents: 0,
469
+ approvalsProvided: 0,
470
+ receiptSigned: true,
471
+ toolManifestHashPresent: true,
472
+ toolVersionKnown: true
473
+ };
474
+ }
475
+
476
+ function normalizeScenario(raw, { profile }) {
477
+ if (!isPlainObject(raw)) fail("scenario must be a JSON object");
478
+ const fallback = buildDefaultScenario(profile);
479
+ const out = {
480
+ providerId: String(raw.providerId ?? fallback.providerId).trim(),
481
+ toolId: String(raw.toolId ?? fallback.toolId).trim(),
482
+ amountUsdCents: Number(raw.amountUsdCents ?? fallback.amountUsdCents),
483
+ monthToDateSpendUsdCents: Number(raw.monthToDateSpendUsdCents ?? fallback.monthToDateSpendUsdCents),
484
+ approvalsProvided: Number(raw.approvalsProvided ?? fallback.approvalsProvided),
485
+ receiptSigned: raw.receiptSigned === undefined ? fallback.receiptSigned : Boolean(raw.receiptSigned),
486
+ toolManifestHashPresent: raw.toolManifestHashPresent === undefined ? fallback.toolManifestHashPresent : Boolean(raw.toolManifestHashPresent),
487
+ toolVersionKnown: raw.toolVersionKnown === undefined ? fallback.toolVersionKnown : Boolean(raw.toolVersionKnown)
488
+ };
489
+ if (!out.providerId) fail("scenario.providerId is required");
490
+ if (!out.toolId) fail("scenario.toolId is required");
491
+ if (!Number.isSafeInteger(out.amountUsdCents) || out.amountUsdCents < 0) fail("scenario.amountUsdCents must be a safe integer >= 0");
492
+ if (!Number.isSafeInteger(out.monthToDateSpendUsdCents) || out.monthToDateSpendUsdCents < 0) {
493
+ fail("scenario.monthToDateSpendUsdCents must be a safe integer >= 0");
494
+ }
495
+ if (!Number.isSafeInteger(out.approvalsProvided) || out.approvalsProvided < 0) fail("scenario.approvalsProvided must be a safe integer >= 0");
496
+ return out;
497
+ }
498
+
499
+ function findApprovalTier(profile, amountUsdCents) {
500
+ const tiers = profile.policy.approvalTiers;
501
+ for (const tier of tiers) {
502
+ if (amountUsdCents <= tier.maxAmountUsdCents) return tier;
503
+ }
504
+ return tiers[tiers.length - 1];
505
+ }
506
+
507
+ function simulateProfile({ profile, scenario }) {
508
+ const allowlists = profile.policy.allowlists;
509
+ const limits = profile.policy.limits;
510
+ const compliance = profile.policy.compliance;
511
+ const tier = findApprovalTier(profile, scenario.amountUsdCents);
512
+
513
+ const checks = [
514
+ { id: "provider_allowlisted", ok: compliance.enforceVendorAllowlist ? allowlists.providers.includes(scenario.providerId) : true },
515
+ { id: "tool_allowlisted", ok: allowlists.tools.includes(scenario.toolId) },
516
+ { id: "per_request_limit", ok: scenario.amountUsdCents <= limits.perRequestUsdCents },
517
+ { id: "monthly_limit", ok: scenario.amountUsdCents + scenario.monthToDateSpendUsdCents <= limits.monthlyUsdCents },
518
+ { id: "receipt_signature", ok: compliance.requireReceiptSignature ? scenario.receiptSigned : true },
519
+ { id: "tool_manifest_hash", ok: compliance.requireToolManifestHash ? scenario.toolManifestHashPresent : true },
520
+ { id: "tool_version_known", ok: compliance.allowUnknownToolVersion ? true : scenario.toolVersionKnown }
521
+ ];
522
+
523
+ const checksOk = checks.every((item) => item.ok === true);
524
+ const requiredApprovers = tier.requiredApprovers;
525
+ const approvalsSatisfied = scenario.approvalsProvided >= requiredApprovers;
526
+ const reasons = [];
527
+
528
+ for (const check of checks) {
529
+ if (check.ok !== true) reasons.push(check.id);
530
+ }
531
+ if (checksOk && !approvalsSatisfied) reasons.push("approval_required");
532
+
533
+ const decision = !checksOk ? "deny" : approvalsSatisfied ? "allow" : "challenge";
534
+ const reasonDetails = mapProfileSimulationReasons(reasons, { failOnUnknown: true });
535
+
536
+ return {
537
+ schemaVersion: SIMULATION_REPORT_SCHEMA_VERSION,
538
+ ok: true,
539
+ profileId: profile.profileId,
540
+ decision,
541
+ requiredApprovers,
542
+ approvalsProvided: scenario.approvalsProvided,
543
+ selectedApprovalTier: tier.tierId,
544
+ reasons,
545
+ reasonCodes: reasonDetails.map((item) => item.code),
546
+ reasonDetails,
547
+ reasonRegistryVersion: PROFILE_SIMULATION_REASON_REGISTRY_VERSION,
548
+ checks,
549
+ scenario
550
+ };
551
+ }
552
+
553
+ async function exists(filePath) {
554
+ try {
555
+ await fs.stat(filePath);
556
+ return true;
557
+ } catch {
558
+ return false;
559
+ }
560
+ }
561
+
562
+ async function readJsonPath(pathLike) {
563
+ if (pathLike === "-") {
564
+ const chunks = [];
565
+ for await (const chunk of process.stdin) {
566
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), "utf8"));
567
+ }
568
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
569
+ }
570
+ return JSON.parse(await fs.readFile(pathLike, "utf8"));
571
+ }
572
+
573
+ async function writeOutput({ format, payload, text, jsonOut }) {
574
+ const jsonBody = `${JSON.stringify(payload, null, 2)}\n`;
575
+ if (jsonOut) {
576
+ const target = path.resolve(process.cwd(), jsonOut);
577
+ await fs.mkdir(path.dirname(target), { recursive: true });
578
+ await fs.writeFile(target, jsonBody, "utf8");
579
+ }
580
+ if (format === "json") {
581
+ process.stdout.write(jsonBody);
582
+ return;
583
+ }
584
+ process.stdout.write(text);
585
+ }
586
+
587
+ function computeTemplateFingerprint(template) {
588
+ const normalized = normalizeForCanonicalJson(template, { path: "$" });
589
+ return sha256Hex(canonicalJsonStringify(normalized));
590
+ }
591
+
592
+ function renderListText(templates) {
593
+ const lines = [];
594
+ for (const profile of templates) {
595
+ lines.push(`${profile.profileId}\t${profile.metadata.vertical}\t${profile.metadata.name}\t${computeTemplateFingerprint(profile)}`);
596
+ }
597
+ return `${lines.join("\n")}${lines.length ? "\n" : ""}`;
598
+ }
599
+
600
+ function renderValidationText(report) {
601
+ if (report.ok) return "ok\n";
602
+ return report.errors.map((row) => `${row.code}\t${row.path}\t${row.message}`).join("\n") + "\n";
603
+ }
604
+
605
+ function renderSimulationText(report) {
606
+ const reasonText = report.reasons.length ? report.reasons.join(",") : "none";
607
+ return [
608
+ `decision: ${report.decision}`,
609
+ `requiredApprovers: ${report.requiredApprovers}`,
610
+ `approvalsProvided: ${report.approvalsProvided}`,
611
+ `reasons: ${reasonText}`
612
+ ].join("\n") + "\n";
613
+ }
614
+
615
+ function normalizeApplyBaseUrl(rawValue) {
616
+ const value = String(rawValue ?? "").trim();
617
+ if (!value) fail("--base-url or SETTLD_BASE_URL is required");
618
+ let parsed;
619
+ try {
620
+ parsed = new URL(value);
621
+ } catch {
622
+ fail("--base-url must be a valid absolute URL");
623
+ }
624
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
625
+ fail("--base-url must use http or https");
626
+ }
627
+ return parsed.toString().replace(/\/+$/, "");
628
+ }
629
+
630
+ function joinBaseUrl(baseUrl, routePath) {
631
+ const suffix = String(routePath ?? "").trim().replace(/^\/+/, "");
632
+ return `${baseUrl}/${suffix}`;
633
+ }
634
+
635
+ function toTrimmedOrNull(value) {
636
+ if (value === null || value === undefined) return null;
637
+ const normalized = String(value).trim();
638
+ return normalized || null;
639
+ }
640
+
641
+ function resolveApplyConfig(parsed, profile) {
642
+ const profileId = toTrimmedOrNull(profile?.profileId);
643
+ if (!profileId) fail("profile.profileId must be a non-empty string");
644
+ const profileSchemaVersion = toTrimmedOrNull(profile?.schemaVersion);
645
+ if (!profileSchemaVersion) fail("profile.schemaVersion must be a non-empty string");
646
+ const baseUrl = normalizeApplyBaseUrl(
647
+ parsed.baseUrl ??
648
+ process.env.SETTLD_BASE_URL ??
649
+ process.env.SETTLD_RUNTIME_BASE_URL ??
650
+ process.env.SETTLD_RUNTIME_URL ??
651
+ process.env.SETTLD_API_URL ??
652
+ ""
653
+ );
654
+ const tenantId = toTrimmedOrNull(parsed.tenantId ?? process.env.SETTLD_TENANT_ID ?? process.env.SETTLD_RUNTIME_TENANT_ID ?? "");
655
+ const apiKey = toTrimmedOrNull(
656
+ parsed.apiKey ??
657
+ process.env.SETTLD_API_KEY ??
658
+ process.env.SETTLD_RUNTIME_BEARER_TOKEN ??
659
+ process.env.SETTLD_BEARER_TOKEN ??
660
+ process.env.SETTLD_TOKEN ??
661
+ ""
662
+ );
663
+ const sponsorRef = toTrimmedOrNull(parsed.sponsorRef ?? process.env.SETTLD_SPONSOR_REF ?? "sponsor_default");
664
+ const sponsorWalletRef = toTrimmedOrNull(
665
+ parsed.sponsorWalletRef ?? process.env.SETTLD_SPONSOR_WALLET_REF ?? process.env.SETTLD_RUNTIME_WALLET_REF ?? `wallet_${profileId}`
666
+ );
667
+ const policyRef = toTrimmedOrNull(parsed.policyRef ?? process.env.SETTLD_POLICY_REF ?? profileId);
668
+ const policyVersion = Number(parsed.policyVersion ?? process.env.SETTLD_POLICY_VERSION ?? 1);
669
+ const idempotencyPrefix = toTrimmedOrNull(parsed.idempotencyPrefix ?? process.env.SETTLD_IDEMPOTENCY_PREFIX ?? "settld_profile_apply");
670
+
671
+ if (!sponsorRef) fail("--sponsor-ref must be a non-empty string");
672
+ if (!sponsorWalletRef) fail("--sponsor-wallet-ref must be a non-empty string");
673
+ if (!policyRef) fail("--policy-ref must be a non-empty string");
674
+ if (!idempotencyPrefix) fail("--idempotency-prefix must be a non-empty string");
675
+ if (!Number.isSafeInteger(policyVersion) || policyVersion < 1) fail("--policy-version must be an integer >= 1");
676
+ if (!parsed.dryRun) {
677
+ if (!tenantId) fail("--tenant-id or SETTLD_TENANT_ID is required");
678
+ if (!apiKey) fail("--api-key or SETTLD_API_KEY is required");
679
+ }
680
+
681
+ return {
682
+ profileId,
683
+ profileSchemaVersion,
684
+ baseUrl,
685
+ tenantId,
686
+ apiKey,
687
+ sponsorRef,
688
+ sponsorWalletRef,
689
+ policyRef,
690
+ policyVersion,
691
+ idempotencyPrefix
692
+ };
693
+ }
694
+
695
+ function mapProfileToX402WalletPolicy({ profile, config }) {
696
+ const currency = String(profile.policy.currency ?? "").trim().toUpperCase();
697
+ return {
698
+ schemaVersion: "X402WalletPolicy.v1",
699
+ sponsorRef: config.sponsorRef,
700
+ sponsorWalletRef: config.sponsorWalletRef,
701
+ policyRef: config.policyRef,
702
+ policyVersion: config.policyVersion,
703
+ status: "active",
704
+ maxAmountCents: Number(profile.policy.limits.perRequestUsdCents),
705
+ maxDailyAuthorizationCents: Number(profile.policy.limits.monthlyUsdCents),
706
+ allowedProviderIds: Array.isArray(profile.policy.allowlists.providers) ? [...profile.policy.allowlists.providers] : [],
707
+ allowedToolIds: Array.isArray(profile.policy.allowlists.tools) ? [...profile.policy.allowlists.tools] : [],
708
+ allowedCurrencies: currency ? [currency] : [],
709
+ requireQuote: true,
710
+ requireStrictRequestBinding: true,
711
+ requireAgentKeyMatch: profile.policy.compliance.allowUnknownToolVersion !== true,
712
+ requiresZkProof: false,
713
+ description: profile.metadata.description,
714
+ metadata: {
715
+ profileId: config.profileId,
716
+ profileSchemaVersion: config.profileSchemaVersion
717
+ }
718
+ };
719
+ }
720
+
721
+ function findMaxAutoReleaseAmountCents(profile) {
722
+ if (!Array.isArray(profile?.policy?.approvalTiers)) return null;
723
+ let maxAmount = null;
724
+ for (const tier of profile.policy.approvalTiers) {
725
+ if (!isPlainObject(tier)) continue;
726
+ if (Number(tier.requiredApprovers) !== 0) continue;
727
+ const amount = Number(tier.maxAmountUsdCents);
728
+ if (!Number.isSafeInteger(amount) || amount < 0) continue;
729
+ if (maxAmount === null || amount > maxAmount) maxAmount = amount;
730
+ }
731
+ return maxAmount;
732
+ }
733
+
734
+ function mapProfileToSettlementPolicyUpsert({ profile, config }) {
735
+ const maxAutoReleaseAmountCents = findMaxAutoReleaseAmountCents(profile);
736
+ return {
737
+ policyId: config.policyRef,
738
+ policyVersion: config.policyVersion,
739
+ policy: {
740
+ schemaVersion: "SettlementPolicy.v1",
741
+ policyVersion: config.policyVersion,
742
+ mode: "automatic",
743
+ rules: {
744
+ maxAutoReleaseAmountCents,
745
+ disputeWindowHours: Number(profile.policy.disputeDefaults.responseWindowHours),
746
+ requireDeterministicVerification: profile.policy.compliance.requireToolManifestHash === true,
747
+ autoReleaseOnGreen: true,
748
+ autoReleaseOnAmber: false,
749
+ autoReleaseOnRed: false
750
+ }
751
+ }
752
+ };
753
+ }
754
+
755
+ function createApplyIdempotencyKey({ prefix, runToken, step }) {
756
+ return `${prefix}_${runToken}_${step}`;
757
+ }
758
+
759
+ function summarizeApplyResponseBody(body) {
760
+ if (body === null || body === undefined) return null;
761
+ if (typeof body === "string") return body.slice(0, 500);
762
+ if (!isPlainObject(body)) return body;
763
+ const summary = {};
764
+ if (typeof body.code === "string") summary.code = body.code;
765
+ if (typeof body.message === "string") summary.message = body.message;
766
+ if (typeof body.ok === "boolean") summary.ok = body.ok;
767
+ if (typeof body.created === "boolean") summary.created = body.created;
768
+ if (isPlainObject(body.policy)) {
769
+ summary.policy = {
770
+ policyId: body.policy.policyId ?? null,
771
+ policyVersion: body.policy.policyVersion ?? null,
772
+ schemaVersion: body.policy.schemaVersion ?? null
773
+ };
774
+ } else if (body.policyId || body.policyVersion) {
775
+ summary.policy = {
776
+ policyId: body.policyId ?? null,
777
+ policyVersion: body.policyVersion ?? null
778
+ };
779
+ }
780
+ if (summary.code || summary.message || summary.ok !== undefined || summary.created !== undefined || summary.policy) {
781
+ return summary;
782
+ }
783
+ return { keys: Object.keys(body).slice(0, 20) };
784
+ }
785
+
786
+ async function runApplyRequest({ method, url, headers, payload }) {
787
+ let response;
788
+ try {
789
+ response = await fetch(url, { method, headers, body: JSON.stringify(payload) });
790
+ } catch (err) {
791
+ return {
792
+ ok: false,
793
+ statusCode: 0,
794
+ statusText: "network_error",
795
+ summary: { message: err?.message ?? "network request failed" },
796
+ body: null
797
+ };
798
+ }
799
+
800
+ const contentType = String(response.headers.get("content-type") ?? "").toLowerCase();
801
+ const rawText = await response.text();
802
+ let body = null;
803
+ if (rawText) {
804
+ if (contentType.includes("application/json") || contentType.includes("+json")) {
805
+ try {
806
+ body = JSON.parse(rawText);
807
+ } catch {
808
+ body = rawText;
809
+ }
810
+ } else {
811
+ body = rawText;
812
+ }
813
+ }
814
+ return {
815
+ ok: response.ok,
816
+ statusCode: response.status,
817
+ statusText: response.statusText || null,
818
+ summary: summarizeApplyResponseBody(body),
819
+ body
820
+ };
821
+ }
822
+
823
+ function renderApplyText(report) {
824
+ const lines = [
825
+ `ok: ${report.ok ? "true" : "false"}`,
826
+ `dryRun: ${report.dryRun ? "true" : "false"}`,
827
+ `profileId: ${report.profileId}`
828
+ ];
829
+ for (const step of report.steps ?? []) {
830
+ const status = step.response ? `${step.response.statusCode} ${step.response.ok ? "ok" : "error"}` : "dry-run";
831
+ lines.push(`${step.step}\t${status}\t${step.method}\t${step.url}`);
832
+ }
833
+ return `${lines.join("\n")}\n`;
834
+ }
835
+
836
+ function parseCsvList(rawValue) {
837
+ const parts = String(rawValue ?? "")
838
+ .split(",")
839
+ .map((value) => value.trim())
840
+ .filter(Boolean);
841
+ const seen = new Set();
842
+ const out = [];
843
+ for (const part of parts) {
844
+ if (seen.has(part)) continue;
845
+ seen.add(part);
846
+ out.push(part);
847
+ }
848
+ return out;
849
+ }
850
+
851
+ function parseWizardInteger(rawValue, flagName, { min = 0 } = {}) {
852
+ const value = Number(rawValue);
853
+ if (!Number.isSafeInteger(value) || value < min) {
854
+ fail(`${flagName} must be an integer >= ${min}`);
855
+ }
856
+ return value;
857
+ }
858
+
859
+ async function promptLine(rl, label, { required = true, defaultValue = null } = {}) {
860
+ const suffix = defaultValue !== null && defaultValue !== undefined && String(defaultValue).trim() ? ` [${String(defaultValue).trim()}]` : "";
861
+ const answer = await rl.question(`${label}${suffix}: `);
862
+ const value = answer.trim() || (defaultValue === null || defaultValue === undefined ? "" : String(defaultValue).trim());
863
+ if (!required || value) return value;
864
+ throw new Error(`${label} is required`);
865
+ }
866
+
867
+ async function createPromptAdapter({ stdin = process.stdin, stdout = process.stdout } = {}) {
868
+ if (stdin.isTTY && stdout.isTTY) {
869
+ const rl = createInterface({ input: stdin, output: stdout });
870
+ return {
871
+ question: async (query) => await rl.question(query),
872
+ close: () => rl.close()
873
+ };
874
+ }
875
+
876
+ const chunks = [];
877
+ for await (const chunk of stdin) {
878
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), "utf8"));
879
+ }
880
+ const answers = Buffer.concat(chunks).toString("utf8").split(/\r?\n/u);
881
+ let answerIndex = 0;
882
+ return {
883
+ async question(query) {
884
+ stdout.write(String(query ?? ""));
885
+ const answer = answers[answerIndex] ?? "";
886
+ answerIndex += 1;
887
+ return answer;
888
+ },
889
+ close: () => {}
890
+ };
891
+ }
892
+
893
+ function parseYesNo(value, { defaultValue = false } = {}) {
894
+ const normalized = String(value ?? "").trim().toLowerCase();
895
+ if (!normalized) return defaultValue;
896
+ if (["y", "yes", "true", "1"].includes(normalized)) return true;
897
+ if (["n", "no", "false", "0"].includes(normalized)) return false;
898
+ return defaultValue;
899
+ }
900
+
901
+ async function promptWizardInteger(rl, label, { defaultValue, min = 0 }) {
902
+ // Keep prompting until we get a valid integer so interactive runs don't fail on a typo.
903
+ while (true) {
904
+ const raw = await promptLine(rl, label, { defaultValue: String(defaultValue) });
905
+ const value = Number(raw);
906
+ if (Number.isSafeInteger(value) && value >= min) return value;
907
+ process.stderr.write(`${label} must be an integer >= ${min}\n`);
908
+ }
909
+ }
910
+
911
+ function resolveWizardTemplateId(parsed, templates) {
912
+ const availableIds = templates.map((template) => template.profileId);
913
+ const fallback = availableIds.includes(DEFAULT_WIZARD_TEMPLATE_ID) ? DEFAULT_WIZARD_TEMPLATE_ID : availableIds[0] ?? null;
914
+ const templateId = String(parsed.templateId || "").trim() || fallback;
915
+ if (!templateId) fail("no profile templates available for wizard");
916
+ return templateId;
917
+ }
918
+
919
+ async function buildWizardProfile(parsed, { stdin = process.stdin, stdout = process.stdout } = {}) {
920
+ const templates = listProfileTemplates();
921
+ const interactive = parsed.nonInteractive !== true;
922
+ let templateId = resolveWizardTemplateId(parsed, templates);
923
+ let prompt = null;
924
+
925
+ try {
926
+ if (interactive) {
927
+ prompt = await createPromptAdapter({ stdin, stdout });
928
+ if (!parsed.templateId) {
929
+ const templatePrompt = templates.length
930
+ ? `Template profile (${templates.map((template) => template.profileId).join("/")})`
931
+ : "Template profile";
932
+ templateId = await promptLine(prompt, templatePrompt, { defaultValue: templateId });
933
+ }
934
+ }
935
+
936
+ const profile = createStarterProfile({ profileId: templateId });
937
+ if (!profile) fail(`unknown profile template: ${templateId}`);
938
+
939
+ let profileId = String(parsed.wizardProfileId || parsed.profileId || "").trim();
940
+ let metadataName = String(parsed.metadataName || "").trim();
941
+ let metadataVertical = String(parsed.metadataVertical || "").trim();
942
+ let metadataDescription = String(parsed.metadataDescription || "").trim();
943
+ let currency = String(parsed.currency || "").trim().toUpperCase();
944
+ let perRequestUsdCents = parsed.perRequestUsdCents;
945
+ let monthlyUsdCents = parsed.monthlyUsdCents;
946
+ let providers = parsed.providersCsv !== null ? parseCsvList(parsed.providersCsv) : null;
947
+ let tools = parsed.toolsCsv !== null ? parseCsvList(parsed.toolsCsv) : null;
948
+
949
+ if (interactive) {
950
+ profileId = profileId || (await promptLine(prompt, "Profile ID", { defaultValue: profile.profileId }));
951
+ metadataName = metadataName || (await promptLine(prompt, "Policy name", { defaultValue: profile.metadata.name }));
952
+ metadataVertical = metadataVertical || (await promptLine(prompt, "Vertical", { defaultValue: profile.metadata.vertical }));
953
+ metadataDescription = metadataDescription || (await promptLine(prompt, "Description", { defaultValue: profile.metadata.description }));
954
+ currency = currency || (await promptLine(prompt, "Currency", { defaultValue: profile.policy.currency })).toUpperCase();
955
+
956
+ perRequestUsdCents =
957
+ perRequestUsdCents !== null
958
+ ? parseWizardInteger(perRequestUsdCents, "--per-request-usd-cents", { min: 1 })
959
+ : await promptWizardInteger(prompt, "Per-request limit (USD cents)", {
960
+ defaultValue: profile.policy.limits.perRequestUsdCents,
961
+ min: 1
962
+ });
963
+ monthlyUsdCents =
964
+ monthlyUsdCents !== null
965
+ ? parseWizardInteger(monthlyUsdCents, "--monthly-usd-cents", { min: 1 })
966
+ : await promptWizardInteger(prompt, "Monthly limit (USD cents)", {
967
+ defaultValue: profile.policy.limits.monthlyUsdCents,
968
+ min: 1
969
+ });
970
+
971
+ providers =
972
+ providers ??
973
+ parseCsvList(
974
+ await promptLine(prompt, "Allowed providers CSV", {
975
+ required: false,
976
+ defaultValue: profile.policy.allowlists.providers.join(",")
977
+ })
978
+ );
979
+ tools =
980
+ tools ??
981
+ parseCsvList(
982
+ await promptLine(prompt, "Allowed tools CSV", {
983
+ required: false,
984
+ defaultValue: profile.policy.allowlists.tools.join(",")
985
+ })
986
+ );
987
+ } else {
988
+ profileId = profileId || profile.profileId;
989
+ metadataName = metadataName || profile.metadata.name;
990
+ metadataVertical = metadataVertical || profile.metadata.vertical;
991
+ metadataDescription = metadataDescription || profile.metadata.description;
992
+ currency = currency || String(profile.policy.currency).toUpperCase();
993
+ perRequestUsdCents =
994
+ perRequestUsdCents === null
995
+ ? profile.policy.limits.perRequestUsdCents
996
+ : parseWizardInteger(perRequestUsdCents, "--per-request-usd-cents", { min: 1 });
997
+ monthlyUsdCents =
998
+ monthlyUsdCents === null
999
+ ? profile.policy.limits.monthlyUsdCents
1000
+ : parseWizardInteger(monthlyUsdCents, "--monthly-usd-cents", { min: 1 });
1001
+ providers = providers ?? [...profile.policy.allowlists.providers];
1002
+ tools = tools ?? [...profile.policy.allowlists.tools];
1003
+ }
1004
+
1005
+ if (!profileId) fail("profile id is required");
1006
+ if (!metadataName) fail("metadata.name is required");
1007
+ if (!metadataVertical) fail("metadata.vertical is required");
1008
+ if (!metadataDescription) fail("metadata.description is required");
1009
+ if (!currency) fail("policy.currency is required");
1010
+ if (perRequestUsdCents > monthlyUsdCents) fail("per-request limit must be <= monthly limit");
1011
+
1012
+ profile.profileId = profileId;
1013
+ profile.metadata.name = metadataName;
1014
+ profile.metadata.vertical = metadataVertical;
1015
+ profile.metadata.description = metadataDescription;
1016
+ profile.policy.currency = currency;
1017
+ profile.policy.limits.perRequestUsdCents = perRequestUsdCents;
1018
+ profile.policy.limits.monthlyUsdCents = monthlyUsdCents;
1019
+ profile.policy.allowlists.providers = providers;
1020
+ profile.policy.allowlists.tools = tools;
1021
+
1022
+ const targetPathDefault = `${profile.profileId}.profile.json`;
1023
+ const outPathInput =
1024
+ parsed.outPath || (interactive ? await promptLine(prompt, "Output path", { defaultValue: targetPathDefault }) : targetPathDefault);
1025
+ const targetPath = path.resolve(process.cwd(), outPathInput);
1026
+ let forceWrite = parsed.force;
1027
+ if (!forceWrite && (await exists(targetPath))) {
1028
+ if (!interactive) fail(`output path exists: ${targetPath}`);
1029
+ const overwrite = parseYesNo(await promptLine(prompt, "Output exists. Overwrite? (y/n)", { required: false, defaultValue: "n" }), {
1030
+ defaultValue: false
1031
+ });
1032
+ if (!overwrite) fail(`output path exists: ${targetPath}`);
1033
+ forceWrite = true;
1034
+ }
1035
+ if (!forceWrite && (await exists(targetPath))) fail(`output path exists: ${targetPath}`);
1036
+
1037
+ return {
1038
+ profile,
1039
+ templateId,
1040
+ targetPath,
1041
+ mode: interactive ? "interactive" : "non_interactive"
1042
+ };
1043
+ } finally {
1044
+ if (prompt) prompt.close();
1045
+ }
1046
+ }
1047
+
1048
+ async function handleWizard(parsed) {
1049
+ const generated = await buildWizardProfile(parsed);
1050
+ const validation = validateProfileDocument(generated.profile);
1051
+ if (!validation.ok) {
1052
+ const firstError = validation.errors[0];
1053
+ fail(
1054
+ `wizard generated an invalid profile at ${firstError?.path ?? "$"}: ${firstError?.message ?? "validation failed"}`
1055
+ );
1056
+ }
1057
+ const profileFingerprint = computeProfileFingerprint(generated.profile);
1058
+ await fs.mkdir(path.dirname(generated.targetPath), { recursive: true });
1059
+ await fs.writeFile(generated.targetPath, `${JSON.stringify(generated.profile, null, 2)}\n`, "utf8");
1060
+ const payload = {
1061
+ ok: true,
1062
+ command: "wizard",
1063
+ mode: generated.mode,
1064
+ templateId: generated.templateId,
1065
+ profileId: generated.profile.profileId,
1066
+ outPath: generated.targetPath,
1067
+ profileFingerprintVersion: profileFingerprint.schemaVersion,
1068
+ profileFingerprint: profileFingerprint.profileFingerprint
1069
+ };
1070
+ await writeOutput({
1071
+ format: parsed.format,
1072
+ payload,
1073
+ text: `ok\t${generated.profile.profileId}\t${generated.targetPath}\n`,
1074
+ jsonOut: parsed.jsonOut
1075
+ });
1076
+ return 0;
1077
+ }
1078
+
1079
+ async function handleList(parsed) {
1080
+ const templates = listProfileTemplates();
1081
+ const profilesWithFingerprints = templates.map((profile) => ({
1082
+ profileId: profile.profileId,
1083
+ metadata: profile.metadata,
1084
+ policySummary: {
1085
+ currency: profile.policyDefaults?.currency ?? null,
1086
+ perRequestUsdCents: profile.policyDefaults?.limits?.perRequestUsdCents ?? null,
1087
+ monthlyUsdCents: profile.policyDefaults?.limits?.monthlyUsdCents ?? null,
1088
+ providerCount: Array.isArray(profile.policyDefaults?.allowlists?.providers) ? profile.policyDefaults.allowlists.providers.length : 0,
1089
+ toolCount: Array.isArray(profile.policyDefaults?.allowlists?.tools) ? profile.policyDefaults.allowlists.tools.length : 0
1090
+ },
1091
+ profileFingerprint: computeTemplateFingerprint(profile)
1092
+ }));
1093
+ const payload = {
1094
+ schemaVersion: PROFILE_TEMPLATE_CATALOG_VERSION,
1095
+ profileFingerprintAlgorithm: "sha256",
1096
+ profiles: profilesWithFingerprints
1097
+ };
1098
+ await writeOutput({
1099
+ format: parsed.format,
1100
+ payload,
1101
+ text: renderListText(profilesWithFingerprints),
1102
+ jsonOut: parsed.jsonOut
1103
+ });
1104
+ return 0;
1105
+ }
1106
+
1107
+ async function handleInit(parsed) {
1108
+ const profile = createStarterProfile({ profileId: parsed.profileId });
1109
+ if (!profile) fail(`unknown profile template: ${parsed.profileId}`);
1110
+ const profileFingerprint = computeProfileFingerprint(profile);
1111
+ const targetPath = path.resolve(process.cwd(), parsed.outPath || `${parsed.profileId}.profile.json`);
1112
+ if (!parsed.force && (await exists(targetPath))) fail(`output path exists: ${targetPath}`);
1113
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
1114
+ await fs.writeFile(targetPath, `${JSON.stringify(profile, null, 2)}\n`, "utf8");
1115
+
1116
+ const payload = {
1117
+ ok: true,
1118
+ command: "init",
1119
+ profileId: parsed.profileId,
1120
+ outPath: targetPath,
1121
+ profileFingerprintVersion: profileFingerprint.schemaVersion,
1122
+ profileFingerprint: profileFingerprint.profileFingerprint
1123
+ };
1124
+ await writeOutput({
1125
+ format: parsed.format,
1126
+ payload,
1127
+ text: `ok\t${parsed.profileId}\t${targetPath}\n`,
1128
+ jsonOut: parsed.jsonOut
1129
+ });
1130
+ return 0;
1131
+ }
1132
+
1133
+ async function handleValidate(parsed) {
1134
+ const profile = await readJsonPath(parsed.inputPath);
1135
+ const report = validateProfileDocument(profile);
1136
+ if (report.ok) {
1137
+ const fingerprint = computeProfileFingerprint(profile);
1138
+ report.profileFingerprintVersion = fingerprint.schemaVersion;
1139
+ report.profileFingerprint = fingerprint.profileFingerprint;
1140
+ }
1141
+ await writeOutput({
1142
+ format: parsed.format,
1143
+ payload: report,
1144
+ text: renderValidationText(report),
1145
+ jsonOut: parsed.jsonOut
1146
+ });
1147
+ return report.ok ? 0 : 1;
1148
+ }
1149
+
1150
+ async function loadScenario(parsed, profile) {
1151
+ if (parsed.scenarioJson) return normalizeScenario(JSON.parse(parsed.scenarioJson), { profile });
1152
+ if (parsed.scenarioPath) return normalizeScenario(await readJsonPath(parsed.scenarioPath), { profile });
1153
+ return normalizeScenario(buildDefaultScenario(profile), { profile });
1154
+ }
1155
+
1156
+ async function handleSimulate(parsed) {
1157
+ const profile = await readJsonPath(parsed.inputPath);
1158
+ const validation = validateProfileDocument(profile);
1159
+ if (!validation.ok) {
1160
+ await writeOutput({
1161
+ format: parsed.format,
1162
+ payload: validation,
1163
+ text: renderValidationText(validation),
1164
+ jsonOut: parsed.jsonOut
1165
+ });
1166
+ return 1;
1167
+ }
1168
+ const scenario = await loadScenario(parsed, profile);
1169
+ const report = simulateProfile({ profile, scenario });
1170
+ const fingerprint = computeProfileFingerprint(profile);
1171
+ report.profileFingerprintVersion = fingerprint.schemaVersion;
1172
+ report.profileFingerprint = fingerprint.profileFingerprint;
1173
+ await writeOutput({
1174
+ format: parsed.format,
1175
+ payload: report,
1176
+ text: renderSimulationText(report),
1177
+ jsonOut: parsed.jsonOut
1178
+ });
1179
+ return 0;
1180
+ }
1181
+
1182
+ async function handleApply(parsed) {
1183
+ const profile = await readJsonPath(parsed.inputPath);
1184
+ const validation = validateProfileDocument(profile);
1185
+ if (!validation.ok) {
1186
+ await writeOutput({
1187
+ format: parsed.format,
1188
+ payload: validation,
1189
+ text: renderValidationText(validation),
1190
+ jsonOut: parsed.jsonOut
1191
+ });
1192
+ return 1;
1193
+ }
1194
+
1195
+ const config = resolveApplyConfig(parsed, profile);
1196
+ const profileFingerprint = computeProfileFingerprint(profile);
1197
+ const x402PolicyPayload = mapProfileToX402WalletPolicy({ profile, config });
1198
+ const settlementPolicyUpsertPayload = mapProfileToSettlementPolicyUpsert({ profile, config });
1199
+ const x402PolicyUrl = joinBaseUrl(config.baseUrl, `/x402/wallets/${encodeURIComponent(config.sponsorWalletRef)}/policy`);
1200
+ const settlementPolicyUrl = joinBaseUrl(config.baseUrl, "/marketplace/settlement-policies");
1201
+ const runToken = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1202
+ const x402IdempotencyKey = createApplyIdempotencyKey({
1203
+ prefix: config.idempotencyPrefix,
1204
+ runToken,
1205
+ step: "x402_wallet_policy"
1206
+ });
1207
+ const settlementIdempotencyKey = createApplyIdempotencyKey({
1208
+ prefix: config.idempotencyPrefix,
1209
+ runToken,
1210
+ step: "settlement_policy"
1211
+ });
1212
+ const report = {
1213
+ schemaVersion: APPLY_REPORT_SCHEMA_VERSION,
1214
+ ok: true,
1215
+ dryRun: parsed.dryRun === true,
1216
+ profileId: config.profileId,
1217
+ profileSchemaVersion: config.profileSchemaVersion,
1218
+ profileFingerprintVersion: profileFingerprint.schemaVersion,
1219
+ profileFingerprint: profileFingerprint.profileFingerprint,
1220
+ appliedAt: new Date().toISOString(),
1221
+ target: {
1222
+ baseUrl: config.baseUrl,
1223
+ tenantId: config.tenantId,
1224
+ sponsorRef: config.sponsorRef,
1225
+ sponsorWalletRef: config.sponsorWalletRef,
1226
+ policyRef: config.policyRef,
1227
+ policyVersion: config.policyVersion
1228
+ },
1229
+ steps: [
1230
+ {
1231
+ step: "x402_wallet_policy_upsert",
1232
+ method: "PUT",
1233
+ url: x402PolicyUrl,
1234
+ idempotencyKey: x402IdempotencyKey,
1235
+ requestPayload: x402PolicyPayload,
1236
+ response: null
1237
+ },
1238
+ {
1239
+ step: "settlement_policy_upsert",
1240
+ method: "POST",
1241
+ url: settlementPolicyUrl,
1242
+ idempotencyKey: settlementIdempotencyKey,
1243
+ requestPayload: settlementPolicyUpsertPayload,
1244
+ response: null
1245
+ }
1246
+ ]
1247
+ };
1248
+
1249
+ if (!parsed.dryRun) {
1250
+ const headersBase = {
1251
+ authorization: `Bearer ${config.apiKey}`,
1252
+ "x-proxy-tenant-id": config.tenantId,
1253
+ "content-type": "application/json",
1254
+ "x-settld-protocol": "1.0"
1255
+ };
1256
+ const x402Response = await runApplyRequest({
1257
+ method: "PUT",
1258
+ url: x402PolicyUrl,
1259
+ headers: {
1260
+ ...headersBase,
1261
+ "x-idempotency-key": x402IdempotencyKey
1262
+ },
1263
+ payload: x402PolicyPayload
1264
+ });
1265
+ report.steps[0].response = x402Response;
1266
+ if (x402Response.ok) {
1267
+ const settlementResponse = await runApplyRequest({
1268
+ method: "POST",
1269
+ url: settlementPolicyUrl,
1270
+ headers: {
1271
+ ...headersBase,
1272
+ "x-idempotency-key": settlementIdempotencyKey
1273
+ },
1274
+ payload: settlementPolicyUpsertPayload
1275
+ });
1276
+ report.steps[1].response = settlementResponse;
1277
+ report.ok = settlementResponse.ok;
1278
+ } else {
1279
+ report.ok = false;
1280
+ }
1281
+ }
1282
+
1283
+ await writeOutput({
1284
+ format: parsed.format,
1285
+ payload: report,
1286
+ text: renderApplyText(report),
1287
+ jsonOut: parsed.jsonOut
1288
+ });
1289
+ return report.ok ? 0 : 1;
1290
+ }
1291
+
1292
+ async function main() {
1293
+ let parsed;
1294
+ try {
1295
+ parsed = parseArgs(process.argv.slice(2));
1296
+ } catch (err) {
1297
+ usage();
1298
+ process.stderr.write(`${err?.message ?? "invalid arguments"}\n`);
1299
+ process.exit(2);
1300
+ return;
1301
+ }
1302
+
1303
+ if (parsed.help) {
1304
+ usage();
1305
+ process.exit(0);
1306
+ return;
1307
+ }
1308
+
1309
+ try {
1310
+ let code = 1;
1311
+ if (parsed.command === "list") code = await handleList(parsed);
1312
+ if (parsed.command === "init") code = await handleInit(parsed);
1313
+ if (parsed.command === "wizard") code = await handleWizard(parsed);
1314
+ if (parsed.command === "validate") code = await handleValidate(parsed);
1315
+ if (parsed.command === "simulate") code = await handleSimulate(parsed);
1316
+ if (parsed.command === "apply") code = await handleApply(parsed);
1317
+ process.exit(code);
1318
+ } catch (err) {
1319
+ process.stderr.write(`${err?.message ?? "command failed"}\n`);
1320
+ process.exit(1);
1321
+ }
1322
+ }
1323
+
1324
+ main();