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,1337 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs/promises";
4
+ import fsNative from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import process from "node:process";
8
+ import { createInterface } from "node:readline/promises";
9
+ import { spawnSync } from "node:child_process";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ import { bootstrapWalletProvider } from "../../src/core/wallet-provider-bootstrap.js";
13
+ import { loadHostConfigHelper, runWizard } from "./wizard.mjs";
14
+ import { SUPPORTED_HOSTS } from "./host-config.mjs";
15
+
16
+ const WALLET_MODES = new Set(["managed", "byo", "none"]);
17
+ const WALLET_PROVIDERS = new Set(["circle"]);
18
+ const WALLET_BOOTSTRAP_MODES = new Set(["auto", "local", "remote"]);
19
+ const FORMAT_OPTIONS = new Set(["text", "json"]);
20
+ const HOST_BINARY_HINTS = Object.freeze({
21
+ codex: "codex",
22
+ claude: "claude",
23
+ cursor: "cursor",
24
+ openclaw: "openclaw"
25
+ });
26
+ const HOST_SELECTION_ORDER = Object.freeze(["openclaw", "codex", "claude", "cursor"]);
27
+ const CIRCLE_BYO_REQUIRED_KEYS = Object.freeze([
28
+ "CIRCLE_BASE_URL",
29
+ "CIRCLE_BLOCKCHAIN",
30
+ "CIRCLE_WALLET_ID_SPEND",
31
+ "CIRCLE_WALLET_ID_ESCROW",
32
+ "CIRCLE_TOKEN_ID_USDC",
33
+ "CIRCLE_ENTITY_SECRET_HEX"
34
+ ]);
35
+ const ONBOARDING_DOCS_PATH = "docs/QUICKSTART_MCP_HOSTS.md";
36
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
37
+ const REPO_ROOT = path.resolve(SCRIPT_DIR, "..", "..");
38
+ const SETTLD_BIN = path.join(REPO_ROOT, "bin", "settld.js");
39
+ const PROFILE_FINGERPRINT_REGEX = /^[0-9a-f]{64}$/;
40
+
41
+ function usage() {
42
+ const text = [
43
+ "usage:",
44
+ " settld setup [flags]",
45
+ " settld onboard [flags]",
46
+ " node scripts/setup/onboard.mjs [flags]",
47
+ "",
48
+ "flags:",
49
+ " --non-interactive Disable prompts; require explicit flags",
50
+ ` --host <${SUPPORTED_HOSTS.join("|")}> Host target (default: auto-detect, fallback openclaw)`,
51
+ " --base-url <url> Settld API base URL (or SETTLD_BASE_URL)",
52
+ " --tenant-id <id> Settld tenant ID (or SETTLD_TENANT_ID)",
53
+ " --settld-api-key <key> Settld tenant API key (or SETTLD_API_KEY)",
54
+ " --wallet-mode <managed|byo|none> Wallet setup mode (default: managed)",
55
+ " --wallet-provider <name> Wallet provider (circle; default: circle)",
56
+ " --wallet-bootstrap <auto|local|remote> Managed wallet setup path (default: auto)",
57
+ " --wallet-env <KEY=VALUE> BYO wallet env row (repeatable)",
58
+ " --circle-api-key <key> Circle API key (or CIRCLE_API_KEY)",
59
+ " --circle-mode <auto|sandbox|production> Circle host selection (default: auto)",
60
+ " --circle-base-url <url> Force Circle API URL",
61
+ " --circle-blockchain <name> Force Circle blockchain",
62
+ " --profile-id <id> Starter profile id (default: engineering-spend)",
63
+ " --skip-profile-apply Skip profile apply",
64
+ " --preflight Run connectivity/auth/path preflight checks (default: on)",
65
+ " --no-preflight Skip preflight checks",
66
+ " --preflight-only Run only preflight checks, then exit",
67
+ " --smoke Run MCP smoke check (default: on)",
68
+ " --no-smoke Disable MCP smoke check",
69
+ " --dry-run Dry-run host config write",
70
+ " --out-env <path> Write combined env file (KEY=VALUE)",
71
+ " --report-path <path> Write JSON report payload to disk",
72
+ " --format <text|json> Output format (default: text)",
73
+ " --help Show this help"
74
+ ].join("\n");
75
+ process.stderr.write(`${text}\n`);
76
+ }
77
+
78
+ function readArgValue(argv, index, rawArg) {
79
+ const arg = String(rawArg ?? "");
80
+ const eq = arg.indexOf("=");
81
+ if (eq >= 0) return { value: arg.slice(eq + 1), nextIndex: index };
82
+ return { value: String(argv[index + 1] ?? ""), nextIndex: index + 1 };
83
+ }
84
+
85
+ function parseWalletEnvAssignment(raw) {
86
+ const text = String(raw ?? "").trim();
87
+ const eq = text.indexOf("=");
88
+ if (eq <= 0) throw new Error("--wallet-env requires KEY=VALUE");
89
+ const key = text.slice(0, eq).trim();
90
+ const value = text.slice(eq + 1);
91
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
92
+ throw new Error(`invalid --wallet-env key: ${key}`);
93
+ }
94
+ return { key, value };
95
+ }
96
+
97
+ function parseArgs(argv) {
98
+ const out = {
99
+ nonInteractive: false,
100
+ host: null,
101
+ baseUrl: null,
102
+ tenantId: null,
103
+ settldApiKey: null,
104
+ walletMode: "managed",
105
+ walletProvider: "circle",
106
+ walletBootstrap: "auto",
107
+ walletEnvRows: [],
108
+ circleApiKey: null,
109
+ circleMode: "auto",
110
+ circleBaseUrl: null,
111
+ circleBlockchain: null,
112
+ profileId: "engineering-spend",
113
+ skipProfileApply: false,
114
+ preflight: true,
115
+ preflightOnly: false,
116
+ smoke: true,
117
+ dryRun: false,
118
+ outEnv: null,
119
+ reportPath: null,
120
+ format: "text",
121
+ help: false
122
+ };
123
+
124
+ for (let i = 0; i < argv.length; i += 1) {
125
+ const arg = String(argv[i] ?? "");
126
+ if (!arg) continue;
127
+
128
+ if (arg === "--help" || arg === "-h") {
129
+ out.help = true;
130
+ continue;
131
+ }
132
+ if (arg === "--non-interactive" || arg === "--yes") {
133
+ out.nonInteractive = true;
134
+ continue;
135
+ }
136
+ if (arg === "--skip-profile-apply") {
137
+ out.skipProfileApply = true;
138
+ continue;
139
+ }
140
+ if (arg === "--preflight") {
141
+ out.preflight = true;
142
+ continue;
143
+ }
144
+ if (arg === "--no-preflight") {
145
+ out.preflight = false;
146
+ continue;
147
+ }
148
+ if (arg === "--preflight-only") {
149
+ out.preflightOnly = true;
150
+ continue;
151
+ }
152
+ if (arg === "--smoke") {
153
+ out.smoke = true;
154
+ continue;
155
+ }
156
+ if (arg === "--no-smoke") {
157
+ out.smoke = false;
158
+ continue;
159
+ }
160
+ if (arg === "--dry-run") {
161
+ out.dryRun = true;
162
+ continue;
163
+ }
164
+
165
+ if (arg === "--host" || arg.startsWith("--host=")) {
166
+ const parsed = readArgValue(argv, i, arg);
167
+ out.host = String(parsed.value ?? "").trim().toLowerCase();
168
+ i = parsed.nextIndex;
169
+ continue;
170
+ }
171
+ if (arg === "--base-url" || arg.startsWith("--base-url=")) {
172
+ const parsed = readArgValue(argv, i, arg);
173
+ out.baseUrl = parsed.value;
174
+ i = parsed.nextIndex;
175
+ continue;
176
+ }
177
+ if (arg === "--tenant-id" || arg.startsWith("--tenant-id=")) {
178
+ const parsed = readArgValue(argv, i, arg);
179
+ out.tenantId = parsed.value;
180
+ i = parsed.nextIndex;
181
+ continue;
182
+ }
183
+ if (arg === "--settld-api-key" || arg.startsWith("--settld-api-key=")) {
184
+ const parsed = readArgValue(argv, i, arg);
185
+ out.settldApiKey = parsed.value;
186
+ i = parsed.nextIndex;
187
+ continue;
188
+ }
189
+ if (arg === "--wallet-mode" || arg.startsWith("--wallet-mode=")) {
190
+ const parsed = readArgValue(argv, i, arg);
191
+ out.walletMode = String(parsed.value ?? "").trim().toLowerCase();
192
+ i = parsed.nextIndex;
193
+ continue;
194
+ }
195
+ if (arg === "--wallet-provider" || arg.startsWith("--wallet-provider=")) {
196
+ const parsed = readArgValue(argv, i, arg);
197
+ out.walletProvider = String(parsed.value ?? "").trim().toLowerCase();
198
+ i = parsed.nextIndex;
199
+ continue;
200
+ }
201
+ if (arg === "--wallet-bootstrap" || arg.startsWith("--wallet-bootstrap=")) {
202
+ const parsed = readArgValue(argv, i, arg);
203
+ out.walletBootstrap = String(parsed.value ?? "").trim().toLowerCase();
204
+ i = parsed.nextIndex;
205
+ continue;
206
+ }
207
+ if (arg === "--wallet-env" || arg.startsWith("--wallet-env=")) {
208
+ const parsed = readArgValue(argv, i, arg);
209
+ out.walletEnvRows.push(parseWalletEnvAssignment(parsed.value));
210
+ i = parsed.nextIndex;
211
+ continue;
212
+ }
213
+ if (arg === "--circle-api-key" || arg.startsWith("--circle-api-key=")) {
214
+ const parsed = readArgValue(argv, i, arg);
215
+ out.circleApiKey = parsed.value;
216
+ i = parsed.nextIndex;
217
+ continue;
218
+ }
219
+ if (arg === "--circle-mode" || arg.startsWith("--circle-mode=")) {
220
+ const parsed = readArgValue(argv, i, arg);
221
+ out.circleMode = String(parsed.value ?? "").trim().toLowerCase();
222
+ i = parsed.nextIndex;
223
+ continue;
224
+ }
225
+ if (arg === "--circle-base-url" || arg.startsWith("--circle-base-url=")) {
226
+ const parsed = readArgValue(argv, i, arg);
227
+ out.circleBaseUrl = parsed.value;
228
+ i = parsed.nextIndex;
229
+ continue;
230
+ }
231
+ if (arg === "--circle-blockchain" || arg.startsWith("--circle-blockchain=")) {
232
+ const parsed = readArgValue(argv, i, arg);
233
+ out.circleBlockchain = parsed.value;
234
+ i = parsed.nextIndex;
235
+ continue;
236
+ }
237
+ if (arg === "--profile-id" || arg.startsWith("--profile-id=")) {
238
+ const parsed = readArgValue(argv, i, arg);
239
+ out.profileId = String(parsed.value ?? "").trim();
240
+ i = parsed.nextIndex;
241
+ continue;
242
+ }
243
+ if (arg === "--out-env" || arg.startsWith("--out-env=")) {
244
+ const parsed = readArgValue(argv, i, arg);
245
+ out.outEnv = parsed.value;
246
+ i = parsed.nextIndex;
247
+ continue;
248
+ }
249
+ if (arg === "--report-path" || arg.startsWith("--report-path=")) {
250
+ const parsed = readArgValue(argv, i, arg);
251
+ out.reportPath = parsed.value;
252
+ i = parsed.nextIndex;
253
+ continue;
254
+ }
255
+ if (arg === "--format" || arg.startsWith("--format=")) {
256
+ const parsed = readArgValue(argv, i, arg);
257
+ out.format = String(parsed.value ?? "").trim().toLowerCase();
258
+ i = parsed.nextIndex;
259
+ continue;
260
+ }
261
+
262
+ throw new Error(`unknown argument: ${arg}`);
263
+ }
264
+
265
+ if (out.host && !SUPPORTED_HOSTS.includes(out.host)) {
266
+ throw new Error(`--host must be one of: ${SUPPORTED_HOSTS.join(", ")}`);
267
+ }
268
+ if (!WALLET_MODES.has(out.walletMode)) throw new Error("--wallet-mode must be managed|byo|none");
269
+ if (!WALLET_PROVIDERS.has(out.walletProvider)) throw new Error(`--wallet-provider must be one of: ${[...WALLET_PROVIDERS].join(", ")}`);
270
+ if (!WALLET_BOOTSTRAP_MODES.has(out.walletBootstrap)) throw new Error("--wallet-bootstrap must be auto|local|remote");
271
+ if (!FORMAT_OPTIONS.has(out.format)) throw new Error("--format must be text|json");
272
+ if (out.preflightOnly && out.preflight === false) {
273
+ throw new Error("--preflight-only cannot be combined with --no-preflight");
274
+ }
275
+ if (out.outEnv) out.outEnv = path.resolve(process.cwd(), out.outEnv);
276
+ if (out.reportPath) out.reportPath = path.resolve(process.cwd(), out.reportPath);
277
+ return out;
278
+ }
279
+
280
+ function normalizeHttpUrl(value) {
281
+ const raw = String(value ?? "").trim();
282
+ if (!raw) return null;
283
+ let parsed;
284
+ try {
285
+ parsed = new URL(raw);
286
+ } catch {
287
+ return null;
288
+ }
289
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
290
+ return parsed.toString().replace(/\/+$/, "");
291
+ }
292
+
293
+ function commandExists(command, { platform = process.platform } = {}) {
294
+ const lookupCmd = platform === "win32" ? "where" : "which";
295
+ const probe = spawnSync(lookupCmd, [command], { stdio: "ignore" });
296
+ return probe.status === 0;
297
+ }
298
+
299
+ function detectInstalledHosts({ platform = process.platform } = {}) {
300
+ const out = [];
301
+ for (const host of HOST_SELECTION_ORDER) {
302
+ const bin = HOST_BINARY_HINTS[host];
303
+ if (!bin) continue;
304
+ if (commandExists(bin, { platform })) out.push(host);
305
+ }
306
+ return out;
307
+ }
308
+
309
+ function selectDefaultHost({ explicitHost, installedHosts }) {
310
+ if (explicitHost && SUPPORTED_HOSTS.includes(explicitHost)) return explicitHost;
311
+ if (Array.isArray(installedHosts) && installedHosts.length > 0) return installedHosts[0];
312
+ return "openclaw";
313
+ }
314
+
315
+ function healthUrlForBase(baseUrl) {
316
+ const normalized = normalizeHttpUrl(baseUrl);
317
+ if (!normalized) return null;
318
+ return `${normalized}/healthz`;
319
+ }
320
+
321
+ function parseJsonOrNull(text) {
322
+ try {
323
+ return JSON.parse(String(text ?? ""));
324
+ } catch {
325
+ return null;
326
+ }
327
+ }
328
+
329
+ function runSettldProfileListProbe({ baseUrl, tenantId, apiKey, timeoutMs = 12000 } = {}) {
330
+ const args = [
331
+ SETTLD_BIN,
332
+ "profile",
333
+ "list",
334
+ "--format",
335
+ "json"
336
+ ];
337
+ return spawnSync(process.execPath, args, {
338
+ cwd: REPO_ROOT,
339
+ env: {
340
+ ...process.env,
341
+ SETTLD_BASE_URL: String(baseUrl ?? ""),
342
+ SETTLD_TENANT_ID: String(tenantId ?? ""),
343
+ SETTLD_API_KEY: String(apiKey ?? "")
344
+ },
345
+ encoding: "utf8",
346
+ timeout: timeoutMs,
347
+ maxBuffer: 1_048_576,
348
+ stdio: ["ignore", "pipe", "pipe"]
349
+ });
350
+ }
351
+
352
+ function runSettldProfileInitProbe({ profileId, outPath, timeoutMs = 12000 } = {}) {
353
+ const args = [SETTLD_BIN, "profile", "init", String(profileId ?? ""), "--out", String(outPath ?? ""), "--force", "--format", "json"];
354
+ return spawnSync(process.execPath, args, {
355
+ cwd: REPO_ROOT,
356
+ env: process.env,
357
+ encoding: "utf8",
358
+ timeout: timeoutMs,
359
+ maxBuffer: 1_048_576,
360
+ stdio: ["ignore", "pipe", "pipe"]
361
+ });
362
+ }
363
+
364
+ function runSettldProfileSimulateProbe({ profilePath, timeoutMs = 12000 } = {}) {
365
+ const args = [SETTLD_BIN, "profile", "simulate", String(profilePath ?? ""), "--format", "json"];
366
+ return spawnSync(process.execPath, args, {
367
+ cwd: REPO_ROOT,
368
+ env: process.env,
369
+ encoding: "utf8",
370
+ timeout: timeoutMs,
371
+ maxBuffer: 1_048_576,
372
+ stdio: ["ignore", "pipe", "pipe"]
373
+ });
374
+ }
375
+
376
+ export async function runProfileSimulationPreflight({ profileId, timeoutMs = 12000 } = {}) {
377
+ const resolvedProfileId = String(profileId ?? "").trim() || "engineering-spend";
378
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "settld-profile-preflight-"));
379
+ const profilePath = path.join(tmpDir, `${resolvedProfileId}.profile.json`);
380
+ try {
381
+ const initProbe = runSettldProfileInitProbe({ profileId: resolvedProfileId, outPath: profilePath, timeoutMs });
382
+ if (initProbe.error?.code === "ETIMEDOUT") {
383
+ return { ok: false, detail: "profile init timed out" };
384
+ }
385
+ if (initProbe.status !== 0) {
386
+ const detail = String(initProbe.stderr || initProbe.stdout || "").trim() || `exit ${initProbe.status}`;
387
+ return { ok: false, detail: `profile init failed: ${detail}` };
388
+ }
389
+ const initJson = parseJsonOrNull(initProbe.stdout);
390
+ const initFingerprint = typeof initJson?.profileFingerprint === "string" ? initJson.profileFingerprint.trim().toLowerCase() : "";
391
+ if (!PROFILE_FINGERPRINT_REGEX.test(initFingerprint)) {
392
+ return { ok: false, detail: "profile init output missing valid profileFingerprint" };
393
+ }
394
+
395
+ const simulateProbe = runSettldProfileSimulateProbe({ profilePath, timeoutMs });
396
+ if (simulateProbe.error?.code === "ETIMEDOUT") {
397
+ return { ok: false, detail: "profile simulate timed out" };
398
+ }
399
+ if (simulateProbe.status !== 0) {
400
+ const detail = String(simulateProbe.stderr || simulateProbe.stdout || "").trim() || `exit ${simulateProbe.status}`;
401
+ return { ok: false, detail: `profile simulate failed: ${detail}` };
402
+ }
403
+
404
+ const simulateJson = parseJsonOrNull(simulateProbe.stdout);
405
+ if (!simulateJson || typeof simulateJson !== "object") {
406
+ return { ok: false, detail: "profile simulate did not return valid JSON output" };
407
+ }
408
+
409
+ const decision = typeof simulateJson.decision === "string" ? simulateJson.decision.trim().toLowerCase() : "";
410
+ if (decision === "allow") {
411
+ return {
412
+ ok: true,
413
+ detail: `profile ${resolvedProfileId} baseline simulation decision=allow (fingerprint=${initFingerprint})`
414
+ };
415
+ }
416
+
417
+ const reasonCodes = Array.isArray(simulateJson.reasonCodes) ? simulateJson.reasonCodes.map((value) => String(value)).filter(Boolean) : [];
418
+ const firstHint = Array.isArray(simulateJson.reasonDetails)
419
+ ? simulateJson.reasonDetails.find((row) => typeof row?.remediationHint === "string" && row.remediationHint.trim())
420
+ : null;
421
+ const hintText = firstHint ? `; remediation=${String(firstHint.remediationHint).trim()}` : "";
422
+ const reasonText = reasonCodes.length ? reasonCodes.join(",") : "none";
423
+ return {
424
+ ok: false,
425
+ detail: `profile ${resolvedProfileId} baseline simulation decision=${decision || "unknown"} reasonCodes=${reasonText}${hintText}`
426
+ };
427
+ } finally {
428
+ await fs.rm(tmpDir, { recursive: true, force: true });
429
+ }
430
+ }
431
+
432
+ async function nearestExistingDirectory(startPath) {
433
+ let current = path.resolve(startPath);
434
+ while (true) {
435
+ try {
436
+ const stat = await fs.stat(current);
437
+ if (stat.isDirectory()) return current;
438
+ current = path.dirname(current);
439
+ } catch (err) {
440
+ if (err?.code !== "ENOENT") throw err;
441
+ const next = path.dirname(current);
442
+ if (next === current) return null;
443
+ current = next;
444
+ }
445
+ }
446
+ }
447
+
448
+ async function assertPathLikelyWritable(configPath) {
449
+ const dir = path.dirname(path.resolve(String(configPath ?? "")));
450
+ const existingDir = await nearestExistingDirectory(dir);
451
+ if (!existingDir) {
452
+ throw new Error(`could not locate an existing parent directory for host config path: ${configPath}`);
453
+ }
454
+ await fs.access(existingDir, fsNative.constants.W_OK);
455
+ }
456
+
457
+ async function runPreflightChecks({
458
+ config,
459
+ normalizedBaseUrl,
460
+ tenantId,
461
+ settldApiKey,
462
+ hostHelper,
463
+ walletEnv,
464
+ fetchImpl = fetch,
465
+ stdout = process.stdout,
466
+ verbose = true
467
+ } = {}) {
468
+ const checks = [];
469
+ const ok = (name, detail) => checks.push({ name, ok: true, detail });
470
+ const fail = (name, detail) => {
471
+ const error = new Error(`${name} preflight failed: ${detail}`);
472
+ error.preflightChecks = checks;
473
+ throw error;
474
+ };
475
+
476
+ if (verbose) stdout.write("Preflight checks...\n");
477
+ const healthUrl = healthUrlForBase(normalizedBaseUrl);
478
+ if (!healthUrl) fail("api_health", `invalid Settld base URL: ${normalizedBaseUrl}`);
479
+ let healthRes;
480
+ try {
481
+ healthRes = await fetchImpl(healthUrl, { method: "GET" });
482
+ } catch (err) {
483
+ fail("api_health", `cannot reach ${healthUrl}: ${err?.message ?? String(err)}`);
484
+ }
485
+ if (!healthRes?.ok) {
486
+ fail("api_health", `GET ${healthUrl} returned HTTP ${healthRes?.status ?? "unknown"}`);
487
+ }
488
+ ok("api_health", `reachable (${healthUrl})`);
489
+
490
+ const probe = runSettldProfileListProbe({
491
+ baseUrl: normalizedBaseUrl,
492
+ tenantId,
493
+ apiKey: settldApiKey
494
+ });
495
+ if (probe.error && probe.error.code === "ETIMEDOUT") {
496
+ fail("tenant_auth", "profile list probe timed out");
497
+ }
498
+ if (probe.status !== 0) {
499
+ const message = String(probe.stderr || probe.stdout || "").trim() || `exit ${probe.status}`;
500
+ fail("tenant_auth", message);
501
+ }
502
+ const probeJson = parseJsonOrNull(probe.stdout);
503
+ if (!probeJson || typeof probeJson !== "object" || probeJson.schemaVersion !== "SettldProfileTemplateCatalog.v1") {
504
+ fail("tenant_auth", "profile list probe returned invalid JSON schema");
505
+ }
506
+ const catalogProfiles = Array.isArray(probeJson.profiles) ? probeJson.profiles : [];
507
+ if (!catalogProfiles.length) {
508
+ fail("tenant_auth", "profile list probe returned empty profile catalog");
509
+ }
510
+ const missingFingerprint = catalogProfiles.some((row) => {
511
+ const fingerprint = typeof row?.profileFingerprint === "string" ? row.profileFingerprint.trim().toLowerCase() : "";
512
+ return !PROFILE_FINGERPRINT_REGEX.test(fingerprint);
513
+ });
514
+ if (missingFingerprint) {
515
+ fail("tenant_auth", "profile list probe returned profile rows without valid profileFingerprint");
516
+ }
517
+ ok("tenant_auth", "tenant + API key accepted");
518
+
519
+ if (config.skipProfileApply === true) {
520
+ ok("profile_policy", "skipped (skip-profile-apply enabled)");
521
+ } else {
522
+ const profilePolicy = await runProfileSimulationPreflight({ profileId: config.profileId });
523
+ if (!profilePolicy.ok) {
524
+ fail("profile_policy", profilePolicy.detail);
525
+ }
526
+ ok("profile_policy", profilePolicy.detail);
527
+ }
528
+
529
+ const hostConfigProbe = await hostHelper.applyHostConfig({
530
+ host: config.host,
531
+ env: {
532
+ SETTLD_BASE_URL: normalizedBaseUrl,
533
+ SETTLD_TENANT_ID: tenantId,
534
+ SETTLD_API_KEY: settldApiKey,
535
+ ...(walletEnv ?? {})
536
+ },
537
+ configPath: null,
538
+ dryRun: true
539
+ });
540
+ if (!hostConfigProbe?.configPath) {
541
+ fail("host_config", "host config path could not be resolved");
542
+ }
543
+ try {
544
+ await assertPathLikelyWritable(hostConfigProbe.configPath);
545
+ } catch (err) {
546
+ fail("host_config", `path not writable (${hostConfigProbe.configPath}): ${err?.message ?? String(err)}`);
547
+ }
548
+ ok("host_config", `${hostConfigProbe.configPath}`);
549
+ if (verbose) stdout.write("Preflight checks passed.\n");
550
+ return {
551
+ ok: true,
552
+ checks,
553
+ hostConfigPreview: hostConfigProbe
554
+ };
555
+ }
556
+
557
+ function mustString(value, name) {
558
+ if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
559
+ return value.trim();
560
+ }
561
+
562
+ async function promptLine(rl, label, { required = true, defaultValue = null } = {}) {
563
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
564
+ const answer = await rl.question(`${label}${suffix}: `);
565
+ const value = answer.trim() || (defaultValue ? String(defaultValue).trim() : "");
566
+ if (!required) return value;
567
+ if (value) return value;
568
+ throw new Error(`${label} is required`);
569
+ }
570
+
571
+ function findOptionIndex(options, value, fallback = 0) {
572
+ const normalized = String(value ?? "").trim().toLowerCase();
573
+ if (!normalized) return fallback;
574
+ const idx = options.findIndex((option) => String(option?.value ?? "").trim().toLowerCase() === normalized);
575
+ if (idx < 0) return fallback;
576
+ return idx;
577
+ }
578
+
579
+ async function promptSelect(
580
+ rl,
581
+ stdin,
582
+ stdout,
583
+ label,
584
+ options,
585
+ { defaultValue = null, hint = null } = {}
586
+ ) {
587
+ if (!Array.isArray(options) || options.length === 0) {
588
+ throw new Error(`${label} requires at least one option`);
589
+ }
590
+ const normalizedOptions = options.map((option) => {
591
+ const value = String(option?.value ?? "").trim();
592
+ const display = String(option?.label ?? value).trim() || value;
593
+ const detail = typeof option?.hint === "string" ? option.hint.trim() : "";
594
+ return { value, label: display, hint: detail };
595
+ });
596
+ if (!stdin?.isTTY || typeof stdin.setRawMode !== "function") {
597
+ const fallbackValue = defaultValue ?? normalizedOptions[0].value;
598
+ const joined = normalizedOptions.map((option) => option.value).join("/");
599
+ const picked = (
600
+ await promptLine(rl, `${label} (${joined})`, {
601
+ defaultValue: fallbackValue
602
+ })
603
+ ).toLowerCase();
604
+ const idx = findOptionIndex(normalizedOptions, picked, -1);
605
+ if (idx < 0) {
606
+ throw new Error(`${label} must be one of: ${normalizedOptions.map((option) => option.value).join(", ")}`);
607
+ }
608
+ return normalizedOptions[idx].value;
609
+ }
610
+
611
+ let index = findOptionIndex(normalizedOptions, defaultValue, 0);
612
+ const wasRaw = stdin.isRaw === true;
613
+ let renderedLines = 0;
614
+
615
+ const render = () => {
616
+ const lines = [];
617
+ lines.push(`${label} (arrow keys + Enter)`);
618
+ for (let i = 0; i < normalizedOptions.length; i += 1) {
619
+ const option = normalizedOptions[i];
620
+ const prefix = i === index ? ">" : " ";
621
+ const detail = option.hint ? ` - ${option.hint}` : "";
622
+ lines.push(` ${prefix} ${option.label}${detail}`);
623
+ }
624
+ if (hint) lines.push(` ${hint}`);
625
+ if (renderedLines > 0) {
626
+ stdout.write(`\u001b[${renderedLines}A`);
627
+ }
628
+ for (const line of lines) {
629
+ stdout.write(`\u001b[2K\r${line}\n`);
630
+ }
631
+ renderedLines = lines.length;
632
+ };
633
+
634
+ if (typeof rl?.pause === "function") rl.pause();
635
+ if (!wasRaw) stdin.setRawMode(true);
636
+ stdin.resume();
637
+ stdin.setEncoding("utf8");
638
+
639
+ render();
640
+
641
+ return await new Promise((resolve, reject) => {
642
+ const cleanup = () => {
643
+ stdin.off("data", onData);
644
+ if (!wasRaw) stdin.setRawMode(false);
645
+ if (typeof rl?.resume === "function") rl.resume();
646
+ };
647
+
648
+ const resolveWithSelection = () => {
649
+ const selected = normalizedOptions[index];
650
+ cleanup();
651
+ stdout.write(`\u001b[2K\r${label}: ${selected.label}\n`);
652
+ resolve(selected.value);
653
+ };
654
+
655
+ const onData = (chunk) => {
656
+ const key = String(chunk ?? "");
657
+ if (!key) return;
658
+ if (key === "\u0003") {
659
+ cleanup();
660
+ reject(new Error("setup cancelled by user"));
661
+ return;
662
+ }
663
+ if (key === "\r" || key === "\n") {
664
+ resolveWithSelection();
665
+ return;
666
+ }
667
+ if (key === "\u001b[A" || key === "k" || key === "K") {
668
+ index = (index - 1 + normalizedOptions.length) % normalizedOptions.length;
669
+ render();
670
+ return;
671
+ }
672
+ if (key === "\u001b[B" || key === "j" || key === "J") {
673
+ index = (index + 1) % normalizedOptions.length;
674
+ render();
675
+ }
676
+ };
677
+
678
+ stdin.on("data", onData);
679
+ });
680
+ }
681
+
682
+ async function promptBooleanChoice(rl, stdin, stdout, label, defaultValue, { trueLabel = "Yes", falseLabel = "No", hint = null } = {}) {
683
+ const selected = await promptSelect(
684
+ rl,
685
+ stdin,
686
+ stdout,
687
+ label,
688
+ [
689
+ { value: "yes", label: trueLabel },
690
+ { value: "no", label: falseLabel }
691
+ ],
692
+ { defaultValue: defaultValue ? "yes" : "no", hint }
693
+ );
694
+ return selected === "yes";
695
+ }
696
+
697
+ function upsertWalletEnvRow(rows, key, value) {
698
+ const normalizedKey = String(key ?? "").trim();
699
+ if (!normalizedKey) return;
700
+ const normalizedValue = String(value ?? "");
701
+ const idx = rows.findIndex((row) => String(row?.key ?? "").trim() === normalizedKey);
702
+ if (idx >= 0) {
703
+ rows[idx] = { key: normalizedKey, value: normalizedValue };
704
+ return;
705
+ }
706
+ rows.push({ key: normalizedKey, value: normalizedValue });
707
+ }
708
+
709
+ function createMutableOutput(output) {
710
+ return {
711
+ muted: false,
712
+ write(chunk, encoding, callback) {
713
+ if (this.muted) {
714
+ if (typeof callback === "function") callback();
715
+ return true;
716
+ }
717
+ return output.write(chunk, encoding, callback);
718
+ }
719
+ };
720
+ }
721
+
722
+ async function promptSecretLine(rl, outputProxy, stdout, label, { required = true } = {}) {
723
+ stdout.write(`${label}: `);
724
+ outputProxy.muted = true;
725
+ let answer = "";
726
+ try {
727
+ answer = await rl.question("");
728
+ } finally {
729
+ outputProxy.muted = false;
730
+ stdout.write("\n");
731
+ }
732
+ const value = String(answer ?? "").trim();
733
+ if (value || !required) return value;
734
+ throw new Error(`${label} is required`);
735
+ }
736
+
737
+ function toEnvFileText(env) {
738
+ const keys = Object.keys(env).sort();
739
+ return `${keys.map((k) => `${k}=${String(env[k] ?? "")}`).join("\n")}\n`;
740
+ }
741
+
742
+ function shellQuote(value) {
743
+ const s = String(value ?? "");
744
+ if (!s) return "''";
745
+ return `'${s.replace(/'/g, `'\\''`)}'`;
746
+ }
747
+
748
+ function toExportText(env) {
749
+ const keys = Object.keys(env).sort();
750
+ return keys.map((k) => `export ${k}=${shellQuote(env[k])}`).join("\n");
751
+ }
752
+
753
+ function printStep(stdout, index, total, label) {
754
+ stdout.write(`[${index}/${total}] ${label}\n`);
755
+ }
756
+
757
+ async function writeJsonReport(reportPath, payload) {
758
+ if (!reportPath) return;
759
+ await fs.mkdir(path.dirname(reportPath), { recursive: true });
760
+ await fs.writeFile(reportPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
761
+ }
762
+
763
+ function inferCircleReserveMode(baseUrl) {
764
+ const normalized = normalizeHttpUrl(baseUrl);
765
+ if (!normalized) return null;
766
+ if (normalized.includes("api-sandbox.circle.com")) return "sandbox";
767
+ return "production";
768
+ }
769
+
770
+ function buildHostNextSteps({ host, installedHosts }) {
771
+ const steps = [];
772
+ const installed = Array.isArray(installedHosts) && installedHosts.includes(host);
773
+ if (!installed) {
774
+ const bin = HOST_BINARY_HINTS[host] ?? host;
775
+ steps.push(`Install ${host} CLI/app so MCP config can be consumed (expected binary: ${bin}).`);
776
+ }
777
+ if (host === "openclaw") {
778
+ steps.push("Run `openclaw doctor` and ensure OpenClaw itself is onboarded (`openclaw onboard --install-daemon`).");
779
+ steps.push("Run `openclaw tui`.");
780
+ steps.push("In OpenClaw, ask for a Settld tool call (for example: `run settld.about`).");
781
+ return steps;
782
+ }
783
+ if (host === "codex") {
784
+ steps.push("Restart Codex so it reloads MCP config.");
785
+ steps.push("Run a Settld tool call (for example: `settld.about`).");
786
+ return steps;
787
+ }
788
+ if (host === "claude") {
789
+ steps.push("Restart Claude Desktop so it reloads MCP config.");
790
+ steps.push("Run a Settld tool call (for example: `settld.about`).");
791
+ return steps;
792
+ }
793
+ if (host === "cursor") {
794
+ steps.push("Restart Cursor so it reloads MCP config.");
795
+ steps.push("Run a Settld tool call (for example: `settld.about`).");
796
+ return steps;
797
+ }
798
+ steps.push("Run a Settld MCP tool call (for example: `settld.about`).");
799
+ return steps;
800
+ }
801
+
802
+ function resolveByoWalletEnv({ walletProvider, walletEnvRows, runtimeEnv }) {
803
+ const env = {};
804
+ for (const row of walletEnvRows ?? []) env[row.key] = row.value;
805
+ if (walletProvider === "circle") {
806
+ for (const key of CIRCLE_BYO_REQUIRED_KEYS) {
807
+ if (typeof env[key] === "string" && env[key].trim()) continue;
808
+ const fromProcess = String(runtimeEnv[key] ?? "").trim();
809
+ if (fromProcess) env[key] = fromProcess;
810
+ }
811
+ const missing = CIRCLE_BYO_REQUIRED_KEYS.filter((key) => !(typeof env[key] === "string" && String(env[key]).trim()));
812
+ if (missing.length) {
813
+ throw new Error(
814
+ `BYO wallet mode missing required env keys: ${missing.join(", ")} (set --wallet-env KEY=VALUE or shell env; see ${ONBOARDING_DOCS_PATH}#3-wallet-modes-managed-vs-byo)`
815
+ );
816
+ }
817
+ if (!(typeof env.X402_CIRCLE_RESERVE_MODE === "string" && String(env.X402_CIRCLE_RESERVE_MODE).trim())) {
818
+ env.X402_CIRCLE_RESERVE_MODE = inferCircleReserveMode(env.CIRCLE_BASE_URL) ?? "production";
819
+ }
820
+ if (!(typeof env.X402_REQUIRE_EXTERNAL_RESERVE === "string" && String(env.X402_REQUIRE_EXTERNAL_RESERVE).trim())) {
821
+ env.X402_REQUIRE_EXTERNAL_RESERVE = "1";
822
+ }
823
+ }
824
+ return env;
825
+ }
826
+
827
+ async function requestRemoteWalletBootstrap({
828
+ baseUrl,
829
+ tenantId,
830
+ settldApiKey,
831
+ walletProvider,
832
+ circleMode,
833
+ circleBaseUrl,
834
+ circleBlockchain,
835
+ fetchImpl = fetch
836
+ } = {}) {
837
+ const normalizedBaseUrl = normalizeHttpUrl(baseUrl);
838
+ if (!normalizedBaseUrl) throw new Error(`invalid wallet bootstrap base URL: ${baseUrl}`);
839
+
840
+ const body = {
841
+ provider: walletProvider
842
+ };
843
+ if (walletProvider === "circle") {
844
+ const circle = {};
845
+ if (typeof circleMode === "string" && circleMode.trim()) circle.mode = circleMode.trim();
846
+ if (typeof circleBaseUrl === "string" && circleBaseUrl.trim()) circle.baseUrl = circleBaseUrl.trim();
847
+ if (typeof circleBlockchain === "string" && circleBlockchain.trim()) circle.blockchain = circleBlockchain.trim();
848
+ if (Object.keys(circle).length > 0) body.circle = circle;
849
+ }
850
+
851
+ const url = new URL(`/v1/tenants/${encodeURIComponent(String(tenantId ?? ""))}/onboarding/wallet-bootstrap`, normalizedBaseUrl);
852
+ const res = await fetchImpl(url.toString(), {
853
+ method: "POST",
854
+ headers: {
855
+ "content-type": "application/json",
856
+ "x-api-key": String(settldApiKey ?? "")
857
+ },
858
+ body: JSON.stringify(body)
859
+ });
860
+
861
+ const text = await res.text();
862
+ let json = null;
863
+ try {
864
+ json = text ? JSON.parse(text) : null;
865
+ } catch {
866
+ json = null;
867
+ }
868
+ if (!res.ok) {
869
+ const message =
870
+ json && typeof json === "object"
871
+ ? json?.message ?? json?.error ?? `HTTP ${res.status}`
872
+ : text || `HTTP ${res.status}`;
873
+ throw new Error(`remote wallet bootstrap failed (${res.status}): ${String(message)}`);
874
+ }
875
+ const bootstrap = json?.walletBootstrap;
876
+ if (!bootstrap || typeof bootstrap !== "object" || Array.isArray(bootstrap)) {
877
+ throw new Error("remote wallet bootstrap response missing walletBootstrap object");
878
+ }
879
+ if (!bootstrap.env || typeof bootstrap.env !== "object" || Array.isArray(bootstrap.env)) {
880
+ throw new Error("remote wallet bootstrap response missing walletBootstrap.env");
881
+ }
882
+ return bootstrap;
883
+ }
884
+
885
+ async function resolveRuntimeConfig({
886
+ args,
887
+ runtimeEnv,
888
+ stdin = process.stdin,
889
+ stdout = process.stdout,
890
+ detectInstalledHostsImpl = detectInstalledHosts
891
+ }) {
892
+ const installedHosts = detectInstalledHostsImpl();
893
+ const defaultHost = selectDefaultHost({
894
+ explicitHost: args.host ? String(args.host).toLowerCase() : "",
895
+ installedHosts
896
+ });
897
+ const out = {
898
+ host: args.host ?? defaultHost,
899
+ walletMode: args.walletMode,
900
+ baseUrl: String(args.baseUrl ?? runtimeEnv.SETTLD_BASE_URL ?? "").trim(),
901
+ tenantId: String(args.tenantId ?? runtimeEnv.SETTLD_TENANT_ID ?? "").trim(),
902
+ settldApiKey: String(args.settldApiKey ?? runtimeEnv.SETTLD_API_KEY ?? "").trim(),
903
+ walletProvider: args.walletProvider,
904
+ walletBootstrap: args.walletBootstrap,
905
+ circleApiKey: String(args.circleApiKey ?? runtimeEnv.CIRCLE_API_KEY ?? "").trim(),
906
+ circleMode: args.circleMode,
907
+ circleBaseUrl: String(args.circleBaseUrl ?? runtimeEnv.CIRCLE_BASE_URL ?? "").trim(),
908
+ circleBlockchain: String(args.circleBlockchain ?? runtimeEnv.CIRCLE_BLOCKCHAIN ?? "").trim(),
909
+ walletEnvRows: Array.isArray(args.walletEnvRows) ? args.walletEnvRows.map((row) => ({ ...row })) : [],
910
+ profileId: args.profileId,
911
+ skipProfileApply: Boolean(args.skipProfileApply),
912
+ preflight: Boolean(args.preflight),
913
+ smoke: Boolean(args.smoke),
914
+ dryRun: Boolean(args.dryRun),
915
+ installedHosts
916
+ };
917
+
918
+ if (args.nonInteractive) {
919
+ if (!SUPPORTED_HOSTS.includes(out.host)) throw new Error(`--host must be one of: ${SUPPORTED_HOSTS.join(", ")}`);
920
+ if (!out.baseUrl) throw new Error("--base-url is required");
921
+ if (!out.tenantId) throw new Error("--tenant-id is required");
922
+ if (!out.settldApiKey) throw new Error("--settld-api-key is required");
923
+ if (out.walletMode === "managed" && out.walletBootstrap === "local" && !out.circleApiKey) {
924
+ throw new Error("--circle-api-key is required for --wallet-mode managed --wallet-bootstrap local");
925
+ }
926
+ return out;
927
+ }
928
+
929
+ if (!stdin.isTTY || !stdout.isTTY) {
930
+ throw new Error("interactive mode requires a TTY. Re-run with --non-interactive and explicit flags.");
931
+ }
932
+ const mutableOutput = createMutableOutput(stdout);
933
+ const rl = createInterface({ input: stdin, output: mutableOutput });
934
+ try {
935
+ stdout.write("Settld guided setup\n");
936
+ stdout.write("===================\n");
937
+ if (installedHosts.length > 0) {
938
+ stdout.write(`Detected hosts: ${installedHosts.join(", ")}\n`);
939
+ } else {
940
+ stdout.write("Detected hosts: none (will still write config files)\n");
941
+ }
942
+ stdout.write("\n");
943
+
944
+ const hostPromptDefault = out.host && SUPPORTED_HOSTS.includes(out.host) ? out.host : defaultHost;
945
+ const hostOptions = SUPPORTED_HOSTS.map((host) => ({
946
+ value: host,
947
+ label: installedHosts.includes(host) ? `${host} (detected)` : host
948
+ }));
949
+ out.host = await promptSelect(
950
+ rl,
951
+ stdin,
952
+ stdout,
953
+ "Select host",
954
+ hostOptions,
955
+ { defaultValue: hostPromptDefault, hint: "Up/Down arrows change selection" }
956
+ );
957
+
958
+ if (!out.walletMode) out.walletMode = "managed";
959
+ out.walletMode = await promptSelect(
960
+ rl,
961
+ stdin,
962
+ stdout,
963
+ "Select wallet mode",
964
+ [
965
+ { value: "managed", label: "managed", hint: "Settld bootstraps wallet env for you" },
966
+ { value: "byo", label: "byo", hint: "Use your existing wallet IDs and secrets" },
967
+ { value: "none", label: "none", hint: "No payment rail wiring during setup" }
968
+ ],
969
+ { defaultValue: out.walletMode }
970
+ );
971
+
972
+ if (!out.baseUrl) {
973
+ out.baseUrl = await promptLine(rl, "Settld base URL", { defaultValue: "https://api.settld.work" });
974
+ }
975
+ if (!out.tenantId) {
976
+ out.tenantId = await promptLine(rl, "Tenant ID", { defaultValue: "tenant_default" });
977
+ }
978
+ if (!out.settldApiKey) out.settldApiKey = await promptSecretLine(rl, mutableOutput, stdout, "Settld API key");
979
+
980
+ if (out.walletMode === "managed") {
981
+ out.walletBootstrap = await promptSelect(
982
+ rl,
983
+ stdin,
984
+ stdout,
985
+ "Managed wallet bootstrap",
986
+ [
987
+ { value: "auto", label: "auto", hint: "Use local Circle key when present, else remote bootstrap" },
988
+ { value: "local", label: "local", hint: "Always use local Circle API key flow" },
989
+ { value: "remote", label: "remote", hint: "Always use tenant onboarding endpoint" }
990
+ ],
991
+ { defaultValue: out.walletBootstrap || "auto" }
992
+ );
993
+ if (out.walletBootstrap === "local" && !out.circleApiKey) {
994
+ out.circleApiKey = await promptSecretLine(rl, mutableOutput, stdout, "Circle API key");
995
+ }
996
+ } else if (out.walletMode === "byo" && out.walletProvider === "circle") {
997
+ for (const key of CIRCLE_BYO_REQUIRED_KEYS) {
998
+ const alreadySet = out.walletEnvRows.find((row) => row?.key === key && String(row?.value ?? "").trim() !== "");
999
+ if (alreadySet) continue;
1000
+ const inheritedValue = String(runtimeEnv[key] ?? "").trim();
1001
+ if (inheritedValue) {
1002
+ upsertWalletEnvRow(out.walletEnvRows, key, inheritedValue);
1003
+ continue;
1004
+ }
1005
+ const isSecret = key === "CIRCLE_ENTITY_SECRET_HEX";
1006
+ const value = isSecret
1007
+ ? await promptSecretLine(rl, mutableOutput, stdout, key, { required: true })
1008
+ : await promptLine(rl, key, { required: true });
1009
+ upsertWalletEnvRow(out.walletEnvRows, key, value);
1010
+ }
1011
+ }
1012
+
1013
+ if (args.preflightOnly) {
1014
+ out.preflight = true;
1015
+ out.smoke = false;
1016
+ out.skipProfileApply = true;
1017
+ out.dryRun = true;
1018
+ } else {
1019
+ out.preflight = await promptBooleanChoice(
1020
+ rl,
1021
+ stdin,
1022
+ stdout,
1023
+ "Run preflight checks?",
1024
+ out.preflight,
1025
+ {
1026
+ trueLabel: "Yes - validate API/auth/paths",
1027
+ falseLabel: "No - skip preflight"
1028
+ }
1029
+ );
1030
+ out.smoke = await promptBooleanChoice(
1031
+ rl,
1032
+ stdin,
1033
+ stdout,
1034
+ "Run MCP smoke test?",
1035
+ out.smoke,
1036
+ {
1037
+ trueLabel: "Yes - run settld.about probe",
1038
+ falseLabel: "No - skip smoke"
1039
+ }
1040
+ );
1041
+
1042
+ const applyProfile = await promptBooleanChoice(
1043
+ rl,
1044
+ stdin,
1045
+ stdout,
1046
+ "Apply starter policy profile?",
1047
+ !out.skipProfileApply,
1048
+ {
1049
+ trueLabel: "Yes - apply profile now",
1050
+ falseLabel: "No - skip profile apply"
1051
+ }
1052
+ );
1053
+ out.skipProfileApply = !applyProfile;
1054
+ if (applyProfile) {
1055
+ out.profileId = await promptLine(rl, "Starter profile ID", {
1056
+ defaultValue: out.profileId || "engineering-spend"
1057
+ });
1058
+ }
1059
+
1060
+ out.dryRun = await promptBooleanChoice(
1061
+ rl,
1062
+ stdin,
1063
+ stdout,
1064
+ "Dry run host config write?",
1065
+ out.dryRun,
1066
+ {
1067
+ trueLabel: "Yes - preview only",
1068
+ falseLabel: "No - write config"
1069
+ }
1070
+ );
1071
+ }
1072
+ return out;
1073
+ } finally {
1074
+ rl.close();
1075
+ }
1076
+ }
1077
+
1078
+ export async function runOnboard({
1079
+ argv = process.argv.slice(2),
1080
+ fetchImpl = fetch,
1081
+ stdin = process.stdin,
1082
+ stdout = process.stdout,
1083
+ runtimeEnv = process.env,
1084
+ runWizardImpl = runWizard,
1085
+ loadHostConfigHelperImpl = loadHostConfigHelper,
1086
+ bootstrapWalletProviderImpl = bootstrapWalletProvider,
1087
+ requestRemoteWalletBootstrapImpl = requestRemoteWalletBootstrap,
1088
+ runPreflightChecksImpl = runPreflightChecks,
1089
+ detectInstalledHostsImpl = detectInstalledHosts
1090
+ } = {}) {
1091
+ const args = parseArgs(argv);
1092
+ if (args.help) {
1093
+ usage();
1094
+ return { ok: true, code: 0 };
1095
+ }
1096
+
1097
+ const showSteps = args.format !== "json";
1098
+ const totalSteps = args.preflightOnly ? 4 : 5;
1099
+ let step = 1;
1100
+
1101
+ if (showSteps) printStep(stdout, step, totalSteps, "Resolve setup configuration");
1102
+ const config = await resolveRuntimeConfig({
1103
+ args,
1104
+ runtimeEnv,
1105
+ stdin,
1106
+ stdout,
1107
+ detectInstalledHostsImpl
1108
+ });
1109
+ step += 1;
1110
+ const normalizedBaseUrl = normalizeHttpUrl(mustString(config.baseUrl, "SETTLD_BASE_URL / --base-url"));
1111
+ if (!normalizedBaseUrl) throw new Error(`invalid Settld base URL: ${config.baseUrl}`);
1112
+ const tenantId = mustString(config.tenantId, "SETTLD_TENANT_ID / --tenant-id");
1113
+ const settldApiKey = mustString(config.settldApiKey, "SETTLD_API_KEY / --settld-api-key");
1114
+
1115
+ if (showSteps) printStep(stdout, step, totalSteps, "Resolve wallet configuration");
1116
+ let walletBootstrapMode = "none";
1117
+ let wallet = null;
1118
+ let walletEnv = {};
1119
+ if (config.walletMode === "managed") {
1120
+ walletBootstrapMode =
1121
+ config.walletBootstrap === "auto"
1122
+ ? (config.circleApiKey ? "local" : "remote")
1123
+ : config.walletBootstrap;
1124
+ if (walletBootstrapMode === "local") {
1125
+ if (!config.circleApiKey) throw new Error("Circle API key is required for local managed wallet bootstrap");
1126
+ wallet = await bootstrapWalletProviderImpl({
1127
+ provider: config.walletProvider,
1128
+ apiKey: config.circleApiKey,
1129
+ mode: config.circleMode,
1130
+ baseUrl: config.circleBaseUrl || null,
1131
+ blockchain: config.circleBlockchain || null,
1132
+ includeApiKey: false,
1133
+ fetchImpl
1134
+ });
1135
+ } else {
1136
+ wallet = await requestRemoteWalletBootstrapImpl({
1137
+ baseUrl: normalizedBaseUrl,
1138
+ tenantId,
1139
+ settldApiKey,
1140
+ walletProvider: config.walletProvider,
1141
+ circleMode: config.circleMode,
1142
+ circleBaseUrl: config.circleBaseUrl || null,
1143
+ circleBlockchain: config.circleBlockchain || null,
1144
+ fetchImpl
1145
+ });
1146
+ }
1147
+ walletEnv = wallet?.env && typeof wallet.env === "object" ? { ...wallet.env } : {};
1148
+ } else if (config.walletMode === "byo") {
1149
+ walletBootstrapMode = "byo";
1150
+ walletEnv = resolveByoWalletEnv({
1151
+ walletProvider: config.walletProvider,
1152
+ walletEnvRows: config.walletEnvRows,
1153
+ runtimeEnv
1154
+ });
1155
+ }
1156
+ step += 1;
1157
+
1158
+ let preflight = { ok: false, skipped: true, checks: [] };
1159
+ if (config.preflight) {
1160
+ if (showSteps) printStep(stdout, step, totalSteps, "Run preflight checks");
1161
+ const hostHelper = await loadHostConfigHelperImpl();
1162
+ preflight = await runPreflightChecksImpl({
1163
+ config,
1164
+ normalizedBaseUrl,
1165
+ tenantId,
1166
+ settldApiKey,
1167
+ hostHelper,
1168
+ walletEnv,
1169
+ fetchImpl,
1170
+ stdout,
1171
+ verbose: showSteps
1172
+ });
1173
+ } else {
1174
+ if (showSteps) printStep(stdout, step, totalSteps, "Skip preflight checks");
1175
+ }
1176
+ step += 1;
1177
+
1178
+ if (args.preflightOnly) {
1179
+ if (showSteps) printStep(stdout, step, totalSteps, "Finalize preflight-only output");
1180
+ const payload = {
1181
+ ok: true,
1182
+ preflightOnly: true,
1183
+ host: config.host,
1184
+ wallet: {
1185
+ mode: config.walletMode,
1186
+ bootstrapMode: walletBootstrapMode,
1187
+ provider: config.walletProvider,
1188
+ details: wallet && typeof wallet === "object" ? wallet : null
1189
+ },
1190
+ settld: {
1191
+ baseUrl: normalizedBaseUrl,
1192
+ tenantId,
1193
+ preflight: Boolean(config.preflight),
1194
+ smoke: false,
1195
+ profileApplied: false,
1196
+ profileId: null
1197
+ },
1198
+ preflight,
1199
+ hostInstallDetected: Array.isArray(config.installedHosts) && config.installedHosts.includes(config.host),
1200
+ installedHosts: config.installedHosts,
1201
+ env: walletEnv,
1202
+ outEnv: args.outEnv ?? null,
1203
+ reportPath: args.reportPath ?? null
1204
+ };
1205
+ if (args.outEnv) {
1206
+ await fs.mkdir(path.dirname(args.outEnv), { recursive: true });
1207
+ await fs.writeFile(args.outEnv, toEnvFileText(walletEnv), "utf8");
1208
+ }
1209
+ await writeJsonReport(args.reportPath, payload);
1210
+ if (args.format === "json") {
1211
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
1212
+ } else {
1213
+ const lines = [];
1214
+ lines.push("Settld preflight completed.");
1215
+ lines.push(`Host: ${config.host}`);
1216
+ lines.push(`Settld: ${normalizedBaseUrl} (tenant=${tenantId})`);
1217
+ lines.push(`Wallet mode: ${config.walletMode}`);
1218
+ lines.push(`Wallet bootstrap mode: ${walletBootstrapMode}`);
1219
+ if (args.outEnv) lines.push(`Wrote env file: ${args.outEnv}`);
1220
+ if (args.reportPath) lines.push(`Wrote report: ${args.reportPath}`);
1221
+ lines.push("");
1222
+ lines.push("Preflight checks:");
1223
+ for (const row of preflight.checks ?? []) {
1224
+ lines.push(`- ${row.name}: ${row.detail}`);
1225
+ }
1226
+ stdout.write(`${lines.join("\n")}\n`);
1227
+ }
1228
+ return payload;
1229
+ }
1230
+
1231
+ if (showSteps) printStep(stdout, step, totalSteps, "Write host config + apply policy/smoke");
1232
+ const wizardArgv = [
1233
+ "--non-interactive",
1234
+ "--mode",
1235
+ "manual",
1236
+ "--host",
1237
+ config.host,
1238
+ "--base-url",
1239
+ normalizedBaseUrl,
1240
+ "--tenant-id",
1241
+ tenantId,
1242
+ "--api-key",
1243
+ settldApiKey
1244
+ ];
1245
+ if (config.skipProfileApply) wizardArgv.push("--skip-profile-apply");
1246
+ else wizardArgv.push("--profile-id", config.profileId || "engineering-spend");
1247
+ if (config.smoke) wizardArgv.push("--smoke");
1248
+ if (config.dryRun) wizardArgv.push("--dry-run");
1249
+
1250
+ const wizardResult = await runWizardImpl({
1251
+ argv: wizardArgv,
1252
+ fetchImpl,
1253
+ stdout,
1254
+ extraEnv: walletEnv
1255
+ });
1256
+ step += 1;
1257
+
1258
+ const mergedEnv = {
1259
+ ...(walletEnv ?? {}),
1260
+ ...(wizardResult?.env && typeof wizardResult.env === "object" ? wizardResult.env : {})
1261
+ };
1262
+
1263
+ if (args.outEnv) {
1264
+ await fs.mkdir(path.dirname(args.outEnv), { recursive: true });
1265
+ await fs.writeFile(args.outEnv, toEnvFileText(mergedEnv), "utf8");
1266
+ }
1267
+
1268
+ if (showSteps) printStep(stdout, step, totalSteps, "Finalize output");
1269
+ const payload = {
1270
+ ok: true,
1271
+ host: config.host,
1272
+ wallet: {
1273
+ mode: config.walletMode,
1274
+ bootstrapMode: walletBootstrapMode,
1275
+ provider: config.walletProvider,
1276
+ details: wallet && typeof wallet === "object" ? wallet : null
1277
+ },
1278
+ settld: {
1279
+ baseUrl: normalizedBaseUrl,
1280
+ tenantId,
1281
+ preflight: Boolean(config.preflight),
1282
+ smoke: Boolean(config.smoke),
1283
+ profileApplied: !config.skipProfileApply,
1284
+ profileId: config.skipProfileApply ? null : (config.profileId || "engineering-spend")
1285
+ },
1286
+ preflight,
1287
+ hostInstallDetected: Array.isArray(config.installedHosts) && config.installedHosts.includes(config.host),
1288
+ installedHosts: config.installedHosts,
1289
+ env: mergedEnv,
1290
+ outEnv: args.outEnv ?? null,
1291
+ reportPath: args.reportPath ?? null
1292
+ };
1293
+ await writeJsonReport(args.reportPath, payload);
1294
+
1295
+ if (args.format === "json") {
1296
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
1297
+ } else {
1298
+ const lines = [];
1299
+ lines.push("Settld onboard complete.");
1300
+ lines.push(`Host: ${config.host}`);
1301
+ lines.push(`Settld: ${normalizedBaseUrl} (tenant=${tenantId})`);
1302
+ lines.push(`Preflight: ${config.preflight ? "passed" : "skipped"}`);
1303
+ lines.push(`Wallet mode: ${config.walletMode}`);
1304
+ lines.push(`Wallet bootstrap mode: ${walletBootstrapMode}`);
1305
+ if (wallet?.wallets?.spend?.walletId) lines.push(`Spend wallet: ${wallet.wallets.spend.walletId}`);
1306
+ if (wallet?.wallets?.escrow?.walletId) lines.push(`Escrow wallet: ${wallet.wallets.escrow.walletId}`);
1307
+ if (wallet?.tokenIdUsdc) lines.push(`USDC token id: ${wallet.tokenIdUsdc}`);
1308
+ if (args.outEnv) lines.push(`Wrote env file: ${args.outEnv}`);
1309
+ lines.push("");
1310
+ lines.push("Combined exports:");
1311
+ lines.push(toExportText(mergedEnv));
1312
+ lines.push("");
1313
+ lines.push("Next:");
1314
+ let step = 1;
1315
+ for (const row of buildHostNextSteps({ host: config.host, installedHosts: config.installedHosts })) {
1316
+ lines.push(`${step}. ${row}`);
1317
+ step += 1;
1318
+ }
1319
+ lines.push(`${step}. Run \`npm run mcp:probe\` for an immediate health check.`);
1320
+ stdout.write(`${lines.join("\n")}\n`);
1321
+ }
1322
+
1323
+ return payload;
1324
+ }
1325
+
1326
+ async function main(argv = process.argv.slice(2)) {
1327
+ try {
1328
+ await runOnboard({ argv });
1329
+ } catch (err) {
1330
+ process.stderr.write(`${err?.message ?? String(err)}\n`);
1331
+ process.exit(1);
1332
+ }
1333
+ }
1334
+
1335
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
1336
+ main();
1337
+ }