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.
- package/README.md +93 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +71 -0
- package/conformance/kernel-v0/README.md +7 -0
- package/conformance/kernel-v0/run.mjs +292 -4
- package/docs/ACCESS.md +57 -0
- package/docs/ADOPTION_CHECKLIST.md +44 -0
- package/docs/ALERTS.md +198 -0
- package/docs/ARCHITECTURE.md +69 -0
- package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
- package/docs/ARTIFACTS.md +60 -0
- package/docs/CERTIFICATION_CHECKLIST.md +33 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
- package/docs/CONFIG.md +297 -0
- package/docs/CONTRACTS_APIS.md +23 -0
- package/docs/DEPRECATION.md +31 -0
- package/docs/DOMAIN_MODEL.md +92 -0
- package/docs/EVENT_ENVELOPE.md +53 -0
- package/docs/FINANCE_PACK_FORMAT.md +53 -0
- package/docs/INCIDENT_TAXONOMY.md +30 -0
- package/docs/JOB_STATE_MACHINE.md +66 -0
- package/docs/KERNEL_COMPATIBLE.md +60 -0
- package/docs/KERNEL_V0.md +40 -0
- package/docs/KEY_ROTATION.md +80 -0
- package/docs/LEDGER.md +82 -0
- package/docs/LIVENESS.md +76 -0
- package/docs/MVP_BUILD_ORDER.md +36 -0
- package/docs/ONCALL_PLAYBOOK.md +39 -0
- package/docs/OPERATIONS_SIGNING.md +20 -0
- package/docs/OVERVIEW.md +190 -0
- package/docs/PERF_BASELINE.md +85 -0
- package/docs/PRD.md +77 -0
- package/docs/QUICKSTART_KERNEL_V0.md +96 -0
- package/docs/QUICKSTART_MCP.md +377 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
- package/docs/QUICKSTART_SDK.md +125 -0
- package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
- package/docs/QUICKSTART_VERIFY.md +54 -0
- package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
- package/docs/README.md +33 -0
- package/docs/RELEASE_CHECKLIST.md +182 -0
- package/docs/RELEASING.md +82 -0
- package/docs/REPO_SETTINGS.md +37 -0
- package/docs/RUNBOOK.md +86 -0
- package/docs/SKILLS.md +42 -0
- package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
- package/docs/SLO.md +131 -0
- package/docs/SUMMARY.md +17 -0
- package/docs/SUPPORT.md +31 -0
- package/docs/THREAT_MODEL.md +36 -0
- package/docs/TRUST.md +59 -0
- package/docs/WORKFLOW.md +35 -0
- package/docs/X402_BATCH_SETTLEMENT.md +126 -0
- package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
- package/docs/examples/x402-provider-payout-registry.example.json +14 -0
- package/docs/gitbook/README.md +64 -0
- package/docs/gitbook/SETUP.md +25 -0
- package/docs/gitbook/SUMMARY.md +15 -0
- package/docs/gitbook/api-reference.md +73 -0
- package/docs/gitbook/closepacks.md +55 -0
- package/docs/gitbook/conformance.md +59 -0
- package/docs/gitbook/core-primitives.md +85 -0
- package/docs/gitbook/dispute-lifecycle.md +33 -0
- package/docs/gitbook/faq.md +21 -0
- package/docs/gitbook/guides.md +49 -0
- package/docs/gitbook/operations-runbook.md +36 -0
- package/docs/gitbook/quickstart.md +103 -0
- package/docs/gitbook/replay-and-audit.md +30 -0
- package/docs/gitbook/sdk-reference.md +35 -0
- package/docs/gitbook/security-model.md +58 -0
- package/docs/integrations/README.md +15 -0
- package/docs/integrations/github-actions-verify.yml +31 -0
- package/docs/integrations/github-actions.md +34 -0
- package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
- package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
- package/docs/kernel-compatible/capabilities.json +36 -0
- package/docs/marketing/agent-commerce-substrate.md +78 -0
- package/docs/marketing/hn-repost-2026-02-17.md +102 -0
- package/docs/marketing/show-hn-post.md +45 -0
- package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
- package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
- package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
- package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
- package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
- package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
- package/docs/ops/R1_SLOS.md +66 -0
- package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
- package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
- package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
- package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
- package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
- package/docs/ops/tool-call-disputes-holdback.md +52 -0
- package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
- package/docs/pilot-kit/README.md +29 -0
- package/docs/pilot-kit/architecture-one-pager.md +48 -0
- package/docs/pilot-kit/buyer-email.txt +19 -0
- package/docs/pilot-kit/buyer-one-pager.md +31 -0
- package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
- package/docs/pilot-kit/offline-verify.md +33 -0
- package/docs/pilot-kit/procurement-one-pager.md +50 -0
- package/docs/pilot-kit/rfp-clause.md +46 -0
- package/docs/pilot-kit/roi-calculator-template.csv +2 -0
- package/docs/pilot-kit/security-qa.md +153 -0
- package/docs/pilot-kit/security-summary.md +35 -0
- package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/AcceptanceCriteria.v1.md +17 -0
- package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
- package/docs/spec/AgentEvent.v1.md +47 -0
- package/docs/spec/AgentIdentity.v1.md +62 -0
- package/docs/spec/AgentPassport.v1.md +95 -0
- package/docs/spec/AgentReputation.v1.md +59 -0
- package/docs/spec/AgentReputation.v2.md +52 -0
- package/docs/spec/AgentRun.v1.md +47 -0
- package/docs/spec/AgentRunSettlement.v1.md +52 -0
- package/docs/spec/AgentWallet.v1.md +43 -0
- package/docs/spec/AgreementDelegation.v1.md +109 -0
- package/docs/spec/ArbitrationCase.v1.md +67 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/ArbitrationVerdict.v1.md +60 -0
- package/docs/spec/BundleHeadAttestation.v1.md +32 -0
- package/docs/spec/CANONICAL_JSON.md +31 -0
- package/docs/spec/CRYPTOGRAPHY.md +61 -0
- package/docs/spec/ClosePack.v1.md +49 -0
- package/docs/spec/ClosePackManifest.v1.md +24 -0
- package/docs/spec/DelegationGrant.v1.md +90 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
- package/docs/spec/ERRORS.md +76 -0
- package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
- package/docs/spec/EvidenceIndex.v1.md +20 -0
- package/docs/spec/ExecutionIntent.v1.md +90 -0
- package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
- package/docs/spec/FundingHold.v1.md +60 -0
- package/docs/spec/GovernancePolicy.v1.md +34 -0
- package/docs/spec/GovernancePolicy.v2.md +30 -0
- package/docs/spec/INVARIANTS.md +389 -0
- package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
- package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
- package/docs/spec/InvoiceClaim.v1.md +11 -0
- package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
- package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
- package/docs/spec/MarketplaceOffer.v2.md +54 -0
- package/docs/spec/MeteringReport.v1.md +18 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PRODUCER_ERRORS.md +42 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/PricingMatrix.v1.md +20 -0
- package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
- package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
- package/docs/spec/ProduceCliOutput.v1.md +46 -0
- package/docs/spec/ProofBundleManifest.v1.md +24 -0
- package/docs/spec/README.md +109 -0
- package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
- package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
- package/docs/spec/REMOTE_SIGNER.md +66 -0
- package/docs/spec/ReleaseIndex.v1.md +32 -0
- package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
- package/docs/spec/ReleaseTrust.v1.md +13 -0
- package/docs/spec/ReleaseTrust.v2.md +26 -0
- package/docs/spec/RemoteSignerRequest.v1.md +21 -0
- package/docs/spec/RemoteSignerResponse.v1.md +16 -0
- package/docs/spec/ReputationEvent.v1.md +63 -0
- package/docs/spec/RevocationList.v1.md +28 -0
- package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
- package/docs/spec/STRICTNESS.md +68 -0
- package/docs/spec/SUPPLY_CHAIN.md +33 -0
- package/docs/spec/SettlementAdjustment.v1.md +45 -0
- package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
- package/docs/spec/SettlementDecisionReport.v1.md +44 -0
- package/docs/spec/SettlementKernel.v1.md +59 -0
- package/docs/spec/SettlementReceipt.v1.md +63 -0
- package/docs/spec/SlaDefinition.v1.md +24 -0
- package/docs/spec/SlaEvaluation.v1.md +12 -0
- package/docs/spec/THREAT_MODEL.md +113 -0
- package/docs/spec/TOOL_PROVENANCE.md +30 -0
- package/docs/spec/TRUST_ANCHORS.md +84 -0
- package/docs/spec/TenantSettings.v1.md +90 -0
- package/docs/spec/TenantSettings.v2.md +99 -0
- package/docs/spec/TimestampProof.v1.md +25 -0
- package/docs/spec/ToolCallAgreement.v1.md +34 -0
- package/docs/spec/ToolCallEvidence.v1.md +47 -0
- package/docs/spec/ToolManifest.v1.md +47 -0
- package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
- package/docs/spec/VERSIONING.md +107 -0
- package/docs/spec/VerificationReport.v1.md +50 -0
- package/docs/spec/VerifyAboutOutput.v1.md +10 -0
- package/docs/spec/VerifyCliOutput.v1.md +28 -0
- package/docs/spec/WARNINGS.md +83 -0
- package/docs/spec/error-codes.v1.txt +285 -0
- package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
- package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
- package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
- package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
- package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
- package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
- package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
- package/docs/spec/examples/release_index_v1.example.json +15 -0
- package/docs/spec/examples/release_trust_v1.example.json +7 -0
- package/docs/spec/examples/release_trust_v2.example.json +22 -0
- package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
- package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
- package/docs/spec/examples/reputation_event_v1.example.json +29 -0
- package/docs/spec/examples/verification_report_v1.example.json +24 -0
- package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
- package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
- package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
- package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
- package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
- package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
- package/docs/spec/producer-error-codes.v1.txt +14 -0
- package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
- package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
- package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
- package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
- package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
- package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
- package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
- package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
- package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
- package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
- package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
- package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
- package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
- package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
- package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
- package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
- package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
- package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
- package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
- package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
- package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
- package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
- package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
- package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
- package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
- package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
- package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
- package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
- package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
- package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
- package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
- package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
- package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
- package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
- package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
- package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
- package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
- package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
- package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
- package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
- package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
- package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
- package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
- package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
- package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
- package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
- package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
- package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
- package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
- package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
- package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
- package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
- package/docs/spec/x402-error-codes.v1.txt +35 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +53 -6
- package/scripts/acceptance/full-stack.mjs +734 -0
- package/scripts/acceptance/full-stack.sh +99 -0
- package/scripts/audit/build-audit-packet.mjs +242 -0
- package/scripts/backup-pg.sh +45 -0
- package/scripts/backup-restore/README.md +18 -0
- package/scripts/backup-restore/capture-state.mjs +130 -0
- package/scripts/backup-restore/client.mjs +97 -0
- package/scripts/backup-restore/seed-workload.mjs +235 -0
- package/scripts/backup-restore/verify-state.mjs +139 -0
- package/scripts/backup-restore-test.sh +217 -0
- package/scripts/chaos.js +221 -0
- package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
- package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
- package/scripts/ci/changelog-guard.mjs +145 -0
- package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
- package/scripts/ci/check-secret-hygiene.mjs +78 -0
- package/scripts/ci/check-version-consistency.mjs +42 -0
- package/scripts/ci/cli-pack-smoke.mjs +160 -0
- package/scripts/ci/flake-budget-guard.mjs +68 -0
- package/scripts/ci/generate-error-codes.mjs +54 -0
- package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
- package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
- package/scripts/ci/npm-pack-smoke.mjs +454 -0
- package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
- package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
- package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
- package/scripts/ci/run-go-live-gate.mjs +150 -0
- package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
- package/scripts/ci/runtime-import-smoke.mjs +58 -0
- package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
- package/scripts/closepack/lib.mjs +286 -0
- package/scripts/collect-debug.sh +263 -0
- package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
- package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
- package/scripts/demo/delivery-robot/generate.mjs +377 -0
- package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
- package/scripts/demo/magic-link-first-green.mjs +118 -0
- package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
- package/scripts/demo/mcp-paid-exa.mjs +1110 -0
- package/scripts/dev/billing-doctor.sh +145 -0
- package/scripts/dev/billing-smoke-prod.sh +219 -0
- package/scripts/dev/billing-webhook-replay.sh +161 -0
- package/scripts/dev/env.dev.example +29 -0
- package/scripts/dev/env.sh +37 -0
- package/scripts/dev/new-sdk-key.sh +81 -0
- package/scripts/dev/sdk-first-run.sh +21 -0
- package/scripts/dev/smoke-x402-gateway.sh +115 -0
- package/scripts/dev/start-api.sh +24 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
- package/scripts/examples/sdk-first-paid-rfq.py +105 -0
- package/scripts/examples/sdk-first-verified-run.mjs +85 -0
- package/scripts/examples/sdk-first-verified-run.py +99 -0
- package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
- package/scripts/examples/sdk-tenant-analytics.py +118 -0
- package/scripts/finance-pack/bundle.mjs +284 -0
- package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
- package/scripts/governance/export.mjs +169 -0
- package/scripts/load/delivery-stress.k6.js +183 -0
- package/scripts/load/ingest-burst.k6.js +236 -0
- package/scripts/load/run-delivery-load.js +66 -0
- package/scripts/load/webhook-receiver.js +131 -0
- package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
- package/scripts/mcp/probe.mjs +238 -0
- package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
- package/scripts/mcp/settld-mcp-server.mjs +1511 -0
- package/scripts/openapi/write.mjs +13 -0
- package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
- package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
- package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
- package/scripts/ops/design-partner-run-packet.mjs +466 -0
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
- package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
- package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
- package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/pilot/finance-pack.mjs +495 -0
- package/scripts/pilot/fixtures/robot-keypair.json +4 -0
- package/scripts/pilot/fixtures/server-signer.json +4 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/proof-bundle/job.mjs +109 -0
- package/scripts/proof-bundle/lib.mjs +92 -0
- package/scripts/proof-bundle/month.mjs +103 -0
- package/scripts/provider/conformance-run.mjs +159 -0
- package/scripts/provider/keys-generate.mjs +135 -0
- package/scripts/provider/publish.mjs +420 -0
- package/scripts/quickstart/x402.mjs +334 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/release/build-artifacts.mjs +181 -0
- package/scripts/release/generate-release-index.mjs +112 -0
- package/scripts/release/release-index-lib.mjs +232 -0
- package/scripts/release/sign-release-index.mjs +85 -0
- package/scripts/release/validate-release-assets.mjs +170 -0
- package/scripts/release/verify-release.mjs +261 -0
- package/scripts/restore-pg.sh +34 -0
- package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
- package/scripts/sdk/smoke-python.py +30 -0
- package/scripts/sdk/smoke.mjs +16 -0
- package/scripts/settlement/x402-batch-worker.mjs +1091 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +239 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +59 -0
- package/scripts/trust/validate-trust-file.mjs +57 -0
- package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
- package/scripts/trust-config/wizard.mjs +161 -0
- package/scripts/vendor-contract-test-lib.mjs +182 -0
- package/scripts/vendor-contract-test.mjs +55 -0
- package/scripts/vercel/build-mkdocs.sh +9 -0
- package/scripts/vercel/ignore-mkdocs.sh +25 -0
- package/scripts/vercel/install-mkdocs.sh +6 -0
- package/scripts/verify-pg.js +217 -0
- package/scripts/x402/receipt-verify.mjs +289 -0
- package/services/finance-sink/src/dedupe-store.js +29 -6
- package/services/receiver/src/dedupe-store.js +29 -5
- package/services/x402-gateway/Dockerfile +13 -0
- package/services/x402-gateway/README.md +58 -0
- package/services/x402-gateway/examples/upstream-mock.js +337 -0
- package/services/x402-gateway/src/server.js +1058 -0
- package/src/api/app.js +34658 -16940
- package/src/api/maintenance.js +70 -0
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +1778 -70
- package/src/api/persistence.js +456 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1581 -62
- package/src/api/workers/deliveries.js +99 -4
- package/src/api/workers/insolvency-sweep.js +159 -0
- package/src/core/agent-card.js +69 -0
- package/src/core/agent-wallets.js +231 -0
- package/src/core/agreement-delegation.js +549 -0
- package/src/core/billing-plans.js +40 -6
- package/src/core/circle-reserve-adapter.js +845 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/maintenance-locks.js +1 -0
- package/src/core/operator-action.js +303 -0
- package/src/core/paid-tool-manifest.js +318 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/provider-publish-conformance.js +525 -0
- package/src/core/provider-publish-proof.js +396 -0
- package/src/core/provider-quote-signature.js +170 -0
- package/src/core/settld-keys.js +112 -0
- package/src/core/settld-pay-token.js +344 -0
- package/src/core/settlement-kernel.js +239 -2
- package/src/core/settlement-verifier.js +335 -0
- package/src/core/tool-call-agreement.js +112 -0
- package/src/core/tool-call-evidence.js +144 -0
- package/src/core/tool-provider-signature.js +98 -0
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/core/x402-escalation-override.js +258 -0
- package/src/core/x402-gate.js +118 -0
- package/src/core/x402-provider-refund-decision.js +220 -0
- package/src/core/x402-receipt-verifier.js +708 -0
- package/src/core/x402-reversal-command.js +251 -0
- package/src/core/x402-wallet-issuer-decision.js +252 -0
- package/src/core/zk-verifier.js +300 -0
- package/src/db/migrations/029_reputation_event_index.sql +54 -0
- package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
- package/src/db/pg.js +18 -7
- package/src/db/store-pg.js +1508 -111
package/src/core/event-policy.js
CHANGED
|
@@ -33,13 +33,22 @@ const OPERATOR_SIGNED_TYPES = new Set([
|
|
|
33
33
|
"OPERATOR_SHIFT_CLOSED"
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
|
+
const OPERATOR_EMERGENCY_SIGNED_TYPES = new Set([
|
|
37
|
+
"OPERATOR_EMERGENCY_PAUSE",
|
|
38
|
+
"OPERATOR_EMERGENCY_QUARANTINE",
|
|
39
|
+
"OPERATOR_EMERGENCY_REVOKE",
|
|
40
|
+
"OPERATOR_EMERGENCY_REVOKE_DELEGATION",
|
|
41
|
+
"OPERATOR_EMERGENCY_KILL_SWITCH",
|
|
42
|
+
"OPERATOR_EMERGENCY_RESUME"
|
|
43
|
+
]);
|
|
44
|
+
|
|
36
45
|
const ROBOT_OR_OPERATOR_SIGNED_TYPES = new Set(["ACCESS_GRANTED", "ACCESS_DENIED", "SKILL_USED", "ZONE_COVERAGE_REPORTED"]);
|
|
37
46
|
|
|
38
47
|
const SERVER_OR_OPERATOR_SIGNED_TYPES = new Set(["INCIDENT_REPORTED", "CLAIM_TRIAGED"]);
|
|
39
48
|
|
|
40
49
|
const SERVER_OR_ROBOT_SIGNED_TYPES = new Set(["EVIDENCE_CAPTURED", "JOB_EXECUTION_ABORTED", "JOB_EXECUTION_RESUMED", "ROBOT_UNHEALTHY"]);
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
const SERVER_SIGNED_TYPES = new Set([
|
|
43
52
|
"JOB_CREATED",
|
|
44
53
|
"QUOTE_PROPOSED",
|
|
45
54
|
"RISK_SCORED",
|
|
@@ -104,6 +113,8 @@ const SERVER_OR_ROBOT_SIGNED_TYPES = new Set(["EVIDENCE_CAPTURED", "JOB_EXECUTIO
|
|
|
104
113
|
export function requiredSignerKindForEventType(eventType) {
|
|
105
114
|
if (ROBOT_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.ROBOT;
|
|
106
115
|
if (OPERATOR_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.OPERATOR;
|
|
116
|
+
if (OPERATOR_EMERGENCY_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.OPERATOR;
|
|
117
|
+
if (typeof eventType === "string" && eventType.startsWith("OPERATOR_EMERGENCY_")) return SIGNER_KIND.OPERATOR;
|
|
107
118
|
if (ROBOT_OR_OPERATOR_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.ROBOT_OR_OPERATOR;
|
|
108
119
|
if (SERVER_OR_OPERATOR_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.SERVER_OR_OPERATOR;
|
|
109
120
|
if (SERVER_OR_ROBOT_SIGNED_TYPES.has(eventType)) return SIGNER_KIND.SERVER_OR_ROBOT;
|
|
@@ -113,7 +124,15 @@ export function requiredSignerKindForEventType(eventType) {
|
|
|
113
124
|
|
|
114
125
|
export function listKnownEventTypes() {
|
|
115
126
|
const all = new Set();
|
|
116
|
-
for (const s of [
|
|
127
|
+
for (const s of [
|
|
128
|
+
ROBOT_SIGNED_TYPES,
|
|
129
|
+
OPERATOR_SIGNED_TYPES,
|
|
130
|
+
OPERATOR_EMERGENCY_SIGNED_TYPES,
|
|
131
|
+
ROBOT_OR_OPERATOR_SIGNED_TYPES,
|
|
132
|
+
SERVER_OR_OPERATOR_SIGNED_TYPES,
|
|
133
|
+
SERVER_OR_ROBOT_SIGNED_TYPES,
|
|
134
|
+
SERVER_SIGNED_TYPES
|
|
135
|
+
]) {
|
|
117
136
|
for (const t of s) all.add(t);
|
|
118
137
|
}
|
|
119
138
|
return Array.from(all).sort();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export const RETENTION_CLEANUP_ADVISORY_LOCK_KEY = "settld:maintenance:retention_cleanup:v1";
|
|
2
2
|
export const FINANCE_RECONCILE_ADVISORY_LOCK_KEY = "settld:maintenance:finance_reconcile:v1";
|
|
3
|
+
export const MONEY_RAIL_RECONCILE_ADVISORY_LOCK_KEY = "settld:maintenance:money_rail_reconcile:v1";
|
|
3
4
|
export const MIGRATIONS_ADVISORY_LOCK_KEY = "settld:migrations:v1";
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { canonicalJsonStringify, normalizeForCanonicalJson } from "./canonical-json.js";
|
|
2
|
+
import { keyIdFromPublicKeyPem, sha256Hex, signHashHexEd25519, verifyHashHexEd25519 } from "./crypto.js";
|
|
3
|
+
|
|
4
|
+
export const OPERATOR_ACTION_SCHEMA_VERSION = "OperatorAction.v1";
|
|
5
|
+
export const OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION = "OperatorActionSignature.v1";
|
|
6
|
+
|
|
7
|
+
const ALLOWED_CASE_KINDS = new Set(["challenge", "dispute", "escalation"]);
|
|
8
|
+
const ALLOWED_ACTIONS = new Set(["APPROVE", "REJECT", "REQUEST_INFO", "OVERRIDE_ALLOW", "OVERRIDE_DENY"]);
|
|
9
|
+
|
|
10
|
+
function assertPlainObject(value, name) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
|
|
12
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
13
|
+
throw new TypeError(`${name} must be a plain object`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function assertNonEmptyString(value, name) {
|
|
18
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
|
|
19
|
+
return String(value).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function assertIsoDateTime(value, name) {
|
|
23
|
+
const out = assertNonEmptyString(value, name);
|
|
24
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(out)) throw new TypeError(`${name} must be an ISO date-time`);
|
|
25
|
+
if (!Number.isFinite(Date.parse(out))) throw new TypeError(`${name} must be an ISO date-time`);
|
|
26
|
+
return new Date(out).toISOString();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function assertPemString(value, name) {
|
|
30
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty PEM string`);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function assertId(value, name, { max = 200 } = {}) {
|
|
35
|
+
const out = assertNonEmptyString(value, name);
|
|
36
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
37
|
+
if (!/^[A-Za-z0-9:._/-]+$/.test(out)) throw new TypeError(`${name} must match ^[A-Za-z0-9:._/-]+$`);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function assertOptionalId(value, name, { max = 200 } = {}) {
|
|
42
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
43
|
+
return assertId(value, name, { max });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertSha256Hex(value, name) {
|
|
47
|
+
const out = assertNonEmptyString(value, name).toLowerCase();
|
|
48
|
+
if (!/^[0-9a-f]{64}$/.test(out)) throw new TypeError(`${name} must be sha256 hex`);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function assertAction(value, name = "action.action") {
|
|
53
|
+
const out = assertNonEmptyString(value, name).toUpperCase();
|
|
54
|
+
if (!ALLOWED_ACTIONS.has(out)) {
|
|
55
|
+
throw new TypeError(`${name} must be APPROVE|REJECT|REQUEST_INFO|OVERRIDE_ALLOW|OVERRIDE_DENY`);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function assertJustificationCode(value, name = "action.justificationCode") {
|
|
61
|
+
const out = assertNonEmptyString(value, name).toUpperCase();
|
|
62
|
+
if (out.length > 128) throw new TypeError(`${name} must be <= 128 chars`);
|
|
63
|
+
if (!/^[A-Z][A-Z0-9._:-]*$/.test(out)) throw new TypeError(`${name} must match ^[A-Z][A-Z0-9._:-]*$`);
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function assertOptionalJustificationText(value, name = "action.justification", { max = 2000 } = {}) {
|
|
68
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
69
|
+
const out = String(value).trim();
|
|
70
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function assertOptionalToken(value, name, { max = 128 } = {}) {
|
|
75
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
76
|
+
const out = String(value).trim().toLowerCase();
|
|
77
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
78
|
+
if (!/^[a-z0-9._:-]+$/.test(out)) throw new TypeError(`${name} must match ^[a-z0-9._:-]+$`);
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeCaseRef(action) {
|
|
83
|
+
const caseRefRaw = action.caseRef;
|
|
84
|
+
if (!caseRefRaw || typeof caseRefRaw !== "object" || Array.isArray(caseRefRaw)) {
|
|
85
|
+
throw new TypeError("action.caseRef must be an object");
|
|
86
|
+
}
|
|
87
|
+
const caseKind = assertNonEmptyString(caseRefRaw.kind, "action.caseRef.kind").toLowerCase();
|
|
88
|
+
if (!ALLOWED_CASE_KINDS.has(caseKind)) {
|
|
89
|
+
throw new TypeError("action.caseRef.kind must be challenge|dispute|escalation");
|
|
90
|
+
}
|
|
91
|
+
return normalizeForCanonicalJson(
|
|
92
|
+
{
|
|
93
|
+
kind: caseKind,
|
|
94
|
+
caseId: assertId(caseRefRaw.caseId, "action.caseRef.caseId", { max: 240 })
|
|
95
|
+
},
|
|
96
|
+
{ path: "$.caseRef" }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizeActor(actor) {
|
|
101
|
+
assertPlainObject(actor, "action.actor");
|
|
102
|
+
const operatorId = assertOptionalId(actor.operatorId ?? actor.id, "action.actor.operatorId", { max: 200 });
|
|
103
|
+
if (!operatorId) throw new TypeError("action.actor.operatorId is required");
|
|
104
|
+
const role = assertOptionalToken(actor.role, "action.actor.role", { max: 128 });
|
|
105
|
+
const tenantId = assertOptionalId(actor.tenantId, "action.actor.tenantId", { max: 128 });
|
|
106
|
+
const sessionId = assertOptionalId(actor.sessionId, "action.actor.sessionId", { max: 256 });
|
|
107
|
+
let metadata = null;
|
|
108
|
+
if (Object.prototype.hasOwnProperty.call(actor, "metadata")) {
|
|
109
|
+
assertPlainObject(actor.metadata, "action.actor.metadata");
|
|
110
|
+
metadata = normalizeForCanonicalJson(actor.metadata, { path: "$.actor.metadata" });
|
|
111
|
+
}
|
|
112
|
+
return normalizeForCanonicalJson(
|
|
113
|
+
{
|
|
114
|
+
operatorId,
|
|
115
|
+
...(role ? { role } : {}),
|
|
116
|
+
...(tenantId ? { tenantId } : {}),
|
|
117
|
+
...(sessionId ? { sessionId } : {}),
|
|
118
|
+
...(metadata ? { metadata } : {})
|
|
119
|
+
},
|
|
120
|
+
{ path: "$.actor" }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeOperatorActionSignature(signature) {
|
|
125
|
+
assertPlainObject(signature, "action.signature");
|
|
126
|
+
return normalizeForCanonicalJson(
|
|
127
|
+
{
|
|
128
|
+
schemaVersion: (() => {
|
|
129
|
+
const schemaVersion = assertNonEmptyString(signature.schemaVersion, "action.signature.schemaVersion");
|
|
130
|
+
if (schemaVersion !== OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION) {
|
|
131
|
+
throw new TypeError(`action.signature.schemaVersion must be ${OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION}`);
|
|
132
|
+
}
|
|
133
|
+
return schemaVersion;
|
|
134
|
+
})(),
|
|
135
|
+
algorithm: (() => {
|
|
136
|
+
const algorithm = assertNonEmptyString(signature.algorithm, "action.signature.algorithm").toLowerCase();
|
|
137
|
+
if (algorithm !== "ed25519") throw new TypeError("action.signature.algorithm must be ed25519");
|
|
138
|
+
return "ed25519";
|
|
139
|
+
})(),
|
|
140
|
+
keyId: assertId(signature.keyId, "action.signature.keyId", { max: 256 }),
|
|
141
|
+
signedAt: assertIsoDateTime(signature.signedAt, "action.signature.signedAt"),
|
|
142
|
+
actionHash: assertSha256Hex(signature.actionHash, "action.signature.actionHash"),
|
|
143
|
+
signatureBase64: assertNonEmptyString(signature.signatureBase64, "action.signature.signatureBase64")
|
|
144
|
+
},
|
|
145
|
+
{ path: "$.signature" }
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function buildOperatorActionV1(action = {}) {
|
|
150
|
+
assertPlainObject(action, "action");
|
|
151
|
+
if (action.schemaVersion !== undefined && action.schemaVersion !== null) {
|
|
152
|
+
const schemaVersion = assertNonEmptyString(action.schemaVersion, "action.schemaVersion");
|
|
153
|
+
if (schemaVersion !== OPERATOR_ACTION_SCHEMA_VERSION) {
|
|
154
|
+
throw new TypeError(`action.schemaVersion must be ${OPERATOR_ACTION_SCHEMA_VERSION}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let metadata = null;
|
|
159
|
+
if (Object.prototype.hasOwnProperty.call(action, "metadata")) {
|
|
160
|
+
assertPlainObject(action.metadata, "action.metadata");
|
|
161
|
+
metadata = normalizeForCanonicalJson(action.metadata, { path: "$.metadata" });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const normalized = normalizeForCanonicalJson(
|
|
165
|
+
{
|
|
166
|
+
schemaVersion: OPERATOR_ACTION_SCHEMA_VERSION,
|
|
167
|
+
...(assertOptionalId(action.actionId, "action.actionId", { max: 240 })
|
|
168
|
+
? { actionId: assertOptionalId(action.actionId, "action.actionId", { max: 240 }) }
|
|
169
|
+
: {}),
|
|
170
|
+
caseRef: normalizeCaseRef(action),
|
|
171
|
+
action: assertAction(action.action),
|
|
172
|
+
justificationCode: assertJustificationCode(action.justificationCode),
|
|
173
|
+
...(assertOptionalJustificationText(action.justification) ? { justification: assertOptionalJustificationText(action.justification) } : {}),
|
|
174
|
+
actor: normalizeActor(action.actor),
|
|
175
|
+
actedAt: assertIsoDateTime(action.actedAt, "action.actedAt"),
|
|
176
|
+
...(metadata ? { metadata } : {})
|
|
177
|
+
},
|
|
178
|
+
{ path: "$" }
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return normalized;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeSignedOperatorActionV1(action = {}) {
|
|
185
|
+
const normalizedAction = buildOperatorActionV1(action);
|
|
186
|
+
const signature = normalizeOperatorActionSignature(action.signature);
|
|
187
|
+
return normalizeForCanonicalJson(
|
|
188
|
+
{
|
|
189
|
+
...normalizedAction,
|
|
190
|
+
signature
|
|
191
|
+
},
|
|
192
|
+
{ path: "$" }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function computeOperatorActionHashV1({ action } = {}) {
|
|
197
|
+
const normalizedAction = buildOperatorActionV1(action ?? {});
|
|
198
|
+
return sha256Hex(canonicalJsonStringify(normalizedAction));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function signOperatorActionV1({ action, signedAt, publicKeyPem, privateKeyPem } = {}) {
|
|
202
|
+
const normalizedAction = buildOperatorActionV1(action ?? {});
|
|
203
|
+
const signerPublicKeyPem = assertPemString(publicKeyPem, "publicKeyPem");
|
|
204
|
+
const signerPrivateKeyPem = assertPemString(privateKeyPem, "privateKeyPem");
|
|
205
|
+
const actionHash = computeOperatorActionHashV1({ action: normalizedAction });
|
|
206
|
+
const signatureBase64 = signHashHexEd25519(actionHash, signerPrivateKeyPem);
|
|
207
|
+
return normalizeForCanonicalJson(
|
|
208
|
+
{
|
|
209
|
+
...normalizedAction,
|
|
210
|
+
signature: {
|
|
211
|
+
schemaVersion: OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION,
|
|
212
|
+
algorithm: "ed25519",
|
|
213
|
+
keyId: keyIdFromPublicKeyPem(signerPublicKeyPem),
|
|
214
|
+
signedAt: assertIsoDateTime(signedAt ?? normalizedAction.actedAt, "signedAt"),
|
|
215
|
+
actionHash,
|
|
216
|
+
signatureBase64
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
{ path: "$" }
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function verifyOperatorActionV1({ action, publicKeyPem } = {}) {
|
|
224
|
+
try {
|
|
225
|
+
const signerPublicKeyPem = assertPemString(publicKeyPem, "publicKeyPem");
|
|
226
|
+
assertPlainObject(action, "action");
|
|
227
|
+
|
|
228
|
+
const schemaVersion = assertNonEmptyString(action.schemaVersion, "action.schemaVersion");
|
|
229
|
+
if (schemaVersion !== OPERATOR_ACTION_SCHEMA_VERSION) {
|
|
230
|
+
return {
|
|
231
|
+
ok: false,
|
|
232
|
+
code: "OPERATOR_ACTION_SCHEMA_MISMATCH",
|
|
233
|
+
error: `action.schemaVersion must be ${OPERATOR_ACTION_SCHEMA_VERSION}`
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!action.signature || typeof action.signature !== "object" || Array.isArray(action.signature)) {
|
|
238
|
+
return {
|
|
239
|
+
ok: false,
|
|
240
|
+
code: "OPERATOR_ACTION_SCHEMA_INVALID",
|
|
241
|
+
error: "action.signature must be an object"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (String(action.signature.schemaVersion ?? "") !== OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
code: "OPERATOR_ACTION_SIGNATURE_SCHEMA_MISMATCH",
|
|
249
|
+
error: `action.signature.schemaVersion must be ${OPERATOR_ACTION_SIGNATURE_SCHEMA_VERSION}`
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const normalized = normalizeSignedOperatorActionV1(action);
|
|
254
|
+
|
|
255
|
+
const expectedKeyId = keyIdFromPublicKeyPem(signerPublicKeyPem);
|
|
256
|
+
if (normalized.signature.keyId !== expectedKeyId) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
code: "OPERATOR_ACTION_KEY_ID_MISMATCH",
|
|
260
|
+
error: "signature keyId mismatch"
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const actionHash = computeOperatorActionHashV1({ action: normalized });
|
|
265
|
+
if (normalized.signature.actionHash !== actionHash) {
|
|
266
|
+
return {
|
|
267
|
+
ok: false,
|
|
268
|
+
code: "OPERATOR_ACTION_HASH_MISMATCH",
|
|
269
|
+
error: "action hash mismatch"
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const signatureValid = verifyHashHexEd25519({
|
|
274
|
+
hashHex: actionHash,
|
|
275
|
+
signatureBase64: normalized.signature.signatureBase64,
|
|
276
|
+
publicKeyPem: signerPublicKeyPem
|
|
277
|
+
});
|
|
278
|
+
if (!signatureValid) {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
code: "OPERATOR_ACTION_SIGNATURE_INVALID",
|
|
282
|
+
error: "signature invalid"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const normalizedAction = buildOperatorActionV1(normalized);
|
|
287
|
+
return {
|
|
288
|
+
ok: true,
|
|
289
|
+
code: null,
|
|
290
|
+
error: null,
|
|
291
|
+
actionHash,
|
|
292
|
+
keyId: normalized.signature.keyId,
|
|
293
|
+
action: normalizedAction,
|
|
294
|
+
signedAction: normalized
|
|
295
|
+
};
|
|
296
|
+
} catch (err) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
code: "OPERATOR_ACTION_SCHEMA_INVALID",
|
|
300
|
+
error: err?.message ?? String(err ?? "")
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { canonicalJsonStringify, normalizeForCanonicalJson } from "./canonical-json.js";
|
|
2
|
+
import { sha256Hex } from "./crypto.js";
|
|
3
|
+
|
|
4
|
+
export const PAID_TOOL_MANIFEST_SCHEMA_VERSION_V1 = "PaidToolManifest.v1";
|
|
5
|
+
export const PAID_TOOL_MANIFEST_SCHEMA_VERSION_V2 = "PaidToolManifest.v2";
|
|
6
|
+
export const PAID_TOOL_MANIFEST_SCHEMA_VERSION = PAID_TOOL_MANIFEST_SCHEMA_VERSION_V1;
|
|
7
|
+
|
|
8
|
+
function assertPlainObject(value, name) {
|
|
9
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
10
|
+
throw new TypeError(`${name} must be an object`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeNonEmptyString(value, name, { max = 256 } = {}) {
|
|
15
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
16
|
+
if (!text) throw new TypeError(`${name} is required`);
|
|
17
|
+
if (text.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeOptionalString(value, name, { max = 2048 } = {}) {
|
|
22
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
23
|
+
const text = String(value).trim();
|
|
24
|
+
if (text.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeOptionalAbsoluteHttpUrl(value, name) {
|
|
29
|
+
const raw = normalizeOptionalString(value, name, { max: 2048 });
|
|
30
|
+
if (!raw) return null;
|
|
31
|
+
let parsed = null;
|
|
32
|
+
try {
|
|
33
|
+
parsed = new URL(raw);
|
|
34
|
+
} catch {
|
|
35
|
+
throw new TypeError(`${name} must be an absolute URL`);
|
|
36
|
+
}
|
|
37
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
38
|
+
throw new TypeError(`${name} must use http or https`);
|
|
39
|
+
}
|
|
40
|
+
return parsed.toString();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeManifestSchemaVersion(value) {
|
|
44
|
+
const text = normalizeNonEmptyString(value, "manifest.schemaVersion", { max: 64 });
|
|
45
|
+
if (text === PAID_TOOL_MANIFEST_SCHEMA_VERSION_V1 || text === PAID_TOOL_MANIFEST_SCHEMA_VERSION_V2) return text;
|
|
46
|
+
throw new TypeError(
|
|
47
|
+
`manifest.schemaVersion must be ${PAID_TOOL_MANIFEST_SCHEMA_VERSION_V1} or ${PAID_TOOL_MANIFEST_SCHEMA_VERSION_V2}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeToolClass(value, name) {
|
|
52
|
+
const raw = normalizeOptionalString(value, name, { max: 32 });
|
|
53
|
+
const normalized = raw ? raw.toLowerCase() : null;
|
|
54
|
+
if (normalized === null) return null;
|
|
55
|
+
if (!["read", "compute", "action"].includes(normalized)) {
|
|
56
|
+
throw new TypeError(`${name} must be read|compute|action`);
|
|
57
|
+
}
|
|
58
|
+
return normalized;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeRiskLevel(value, name) {
|
|
62
|
+
const raw = normalizeOptionalString(value, name, { max: 32 });
|
|
63
|
+
if (!raw) return null;
|
|
64
|
+
const normalized = raw.toLowerCase() === "med" ? "medium" : raw.toLowerCase();
|
|
65
|
+
if (!["low", "medium", "high"].includes(normalized)) {
|
|
66
|
+
throw new TypeError(`${name} must be low|medium|high`);
|
|
67
|
+
}
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeStringArray(value, name, { maxItems = 32, maxLen = 64 } = {}) {
|
|
72
|
+
if (value === null || value === undefined) return [];
|
|
73
|
+
if (!Array.isArray(value)) throw new TypeError(`${name} must be an array`);
|
|
74
|
+
const out = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
77
|
+
const row = normalizeNonEmptyString(value[i], `${name}[${i}]`, { max: maxLen }).toLowerCase();
|
|
78
|
+
if (seen.has(row)) continue;
|
|
79
|
+
seen.add(row);
|
|
80
|
+
out.push(row);
|
|
81
|
+
if (out.length > maxItems) throw new TypeError(`${name} must contain <= ${maxItems} items`);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeRequiredSignatures(value, name) {
|
|
87
|
+
const allowed = new Set(["quote", "output", "refund_decision"]);
|
|
88
|
+
const out = normalizeStringArray(value, name, { maxItems: 8, maxLen: 32 });
|
|
89
|
+
for (const item of out) {
|
|
90
|
+
if (!allowed.has(item)) throw new TypeError(`${name} contains unsupported value: ${item}`);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizeRequestBinding(value, name) {
|
|
96
|
+
const raw = normalizeOptionalString(value, name, { max: 32 });
|
|
97
|
+
if (!raw) return null;
|
|
98
|
+
const normalized = raw.toLowerCase();
|
|
99
|
+
if (!["strict", "recommended", "none"].includes(normalized)) {
|
|
100
|
+
throw new TypeError(`${name} must be strict|recommended|none`);
|
|
101
|
+
}
|
|
102
|
+
return normalized;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normalizeHttpMethod(value, name) {
|
|
106
|
+
const method = normalizeNonEmptyString(value ?? "GET", name, { max: 16 }).toUpperCase();
|
|
107
|
+
if (!["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"].includes(method)) {
|
|
108
|
+
throw new TypeError(`${name} must be GET|POST|PUT|PATCH|DELETE|HEAD`);
|
|
109
|
+
}
|
|
110
|
+
return method;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizeCurrency(value, name) {
|
|
114
|
+
const raw = typeof value === "string" && value.trim() !== "" ? value.trim().toUpperCase() : "USD";
|
|
115
|
+
if (!/^[A-Z][A-Z0-9_]{2,11}$/.test(raw)) throw new TypeError(`${name} must match ^[A-Z][A-Z0-9_]{2,11}$`);
|
|
116
|
+
return raw;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizePositiveSafeInt(value, name) {
|
|
120
|
+
const n = Number(value);
|
|
121
|
+
if (!Number.isSafeInteger(n) || n <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
122
|
+
return n;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeRoutePath(value, name) {
|
|
126
|
+
const text = normalizeNonEmptyString(value, name, { max: 1024 });
|
|
127
|
+
if (!text.startsWith("/")) throw new TypeError(`${name} must start with /`);
|
|
128
|
+
if (text.includes("..")) throw new TypeError(`${name} must not contain path traversal`);
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizePricing(rawPricing, defaults, fieldPath) {
|
|
133
|
+
const pricing = rawPricing && typeof rawPricing === "object" && !Array.isArray(rawPricing) ? rawPricing : {};
|
|
134
|
+
const amountCents = normalizePositiveSafeInt(pricing.amountCents ?? defaults.amountCents ?? 500, `${fieldPath}.amountCents`);
|
|
135
|
+
const currency = normalizeCurrency(pricing.currency ?? defaults.currency ?? "USD", `${fieldPath}.currency`);
|
|
136
|
+
return normalizeForCanonicalJson({ amountCents, currency }, { path: "$" });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeToolSecurity(rawSecurity, defaults, fieldPath) {
|
|
140
|
+
const security = rawSecurity && typeof rawSecurity === "object" && !Array.isArray(rawSecurity) ? rawSecurity : {};
|
|
141
|
+
const requiredSignatures = normalizeRequiredSignatures(
|
|
142
|
+
security.requiredSignatures ?? security.required_signatures ?? defaults.requiredSignatures ?? [],
|
|
143
|
+
`${fieldPath}.requiredSignatures`
|
|
144
|
+
);
|
|
145
|
+
const requestBinding =
|
|
146
|
+
normalizeRequestBinding(security.requestBinding ?? security.request_binding ?? defaults.requestBinding, `${fieldPath}.requestBinding`) ??
|
|
147
|
+
"recommended";
|
|
148
|
+
return normalizeForCanonicalJson(
|
|
149
|
+
{
|
|
150
|
+
requiredSignatures,
|
|
151
|
+
requestBinding
|
|
152
|
+
},
|
|
153
|
+
{ path: "$" }
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function normalizeTool(rawTool, defaults, { index, schemaVersion }) {
|
|
158
|
+
assertPlainObject(rawTool, `tools[${index}]`);
|
|
159
|
+
const toolId = normalizeNonEmptyString(rawTool.toolId, `tools[${index}].toolId`, { max: 128 });
|
|
160
|
+
const mcpToolName = normalizeOptionalString(rawTool.mcpToolName, `tools[${index}].mcpToolName`, { max: 180 });
|
|
161
|
+
const description = normalizeOptionalString(rawTool.description, `tools[${index}].description`, { max: 1000 });
|
|
162
|
+
const method = normalizeHttpMethod(rawTool.method ?? "GET", `tools[${index}].method`);
|
|
163
|
+
const upstreamPath = normalizeOptionalString(rawTool.upstreamPath, `tools[${index}].upstreamPath`, { max: 1024 });
|
|
164
|
+
const paidPath = normalizeRoutePath(rawTool.paidPath, `tools[${index}].paidPath`);
|
|
165
|
+
const idempotency = normalizeOptionalString(rawTool.idempotency, `tools[${index}].idempotency`, { max: 64 }) ?? defaults.idempotency ?? "idempotent";
|
|
166
|
+
const signatureMode = normalizeOptionalString(rawTool.signatureMode, `tools[${index}].signatureMode`, { max: 64 }) ?? defaults.signatureMode ?? "required";
|
|
167
|
+
if (!["idempotent", "non_idempotent", "side_effecting"].includes(String(idempotency))) {
|
|
168
|
+
throw new TypeError(`tools[${index}].idempotency must be idempotent|non_idempotent|side_effecting`);
|
|
169
|
+
}
|
|
170
|
+
if (!["required", "optional"].includes(String(signatureMode))) {
|
|
171
|
+
throw new TypeError(`tools[${index}].signatureMode must be required|optional`);
|
|
172
|
+
}
|
|
173
|
+
const pricing = normalizePricing(rawTool.pricing, defaults, `tools[${index}].pricing`);
|
|
174
|
+
const auth =
|
|
175
|
+
rawTool.auth && typeof rawTool.auth === "object" && !Array.isArray(rawTool.auth)
|
|
176
|
+
? normalizeForCanonicalJson(rawTool.auth, { path: "$" })
|
|
177
|
+
: normalizeForCanonicalJson({ mode: "none" }, { path: "$" });
|
|
178
|
+
const metadata =
|
|
179
|
+
rawTool.metadata && typeof rawTool.metadata === "object" && !Array.isArray(rawTool.metadata)
|
|
180
|
+
? normalizeForCanonicalJson(rawTool.metadata, { path: "$" })
|
|
181
|
+
: null;
|
|
182
|
+
|
|
183
|
+
const isV2 = schemaVersion === PAID_TOOL_MANIFEST_SCHEMA_VERSION_V2;
|
|
184
|
+
const toolClass = normalizeToolClass(
|
|
185
|
+
rawTool.toolClass ?? rawTool.tool_class ?? defaults.toolClass ?? null,
|
|
186
|
+
`tools[${index}].toolClass`
|
|
187
|
+
);
|
|
188
|
+
const riskLevel = normalizeRiskLevel(
|
|
189
|
+
rawTool.riskLevel ?? rawTool.risk_level ?? defaults.riskLevel ?? null,
|
|
190
|
+
`tools[${index}].riskLevel`
|
|
191
|
+
);
|
|
192
|
+
const capabilityTags = normalizeStringArray(
|
|
193
|
+
rawTool.capabilityTags ?? rawTool.capability_tags ?? rawTool.tags ?? [],
|
|
194
|
+
`tools[${index}].capabilityTags`,
|
|
195
|
+
{ maxItems: 64, maxLen: 80 }
|
|
196
|
+
);
|
|
197
|
+
const security = normalizeToolSecurity(rawTool.security ?? null, defaults, `tools[${index}].security`);
|
|
198
|
+
|
|
199
|
+
return normalizeForCanonicalJson(
|
|
200
|
+
{
|
|
201
|
+
toolId,
|
|
202
|
+
mcpToolName,
|
|
203
|
+
description,
|
|
204
|
+
method,
|
|
205
|
+
upstreamPath,
|
|
206
|
+
paidPath,
|
|
207
|
+
pricing,
|
|
208
|
+
idempotency,
|
|
209
|
+
signatureMode,
|
|
210
|
+
auth,
|
|
211
|
+
metadata,
|
|
212
|
+
...(isV2
|
|
213
|
+
? {
|
|
214
|
+
toolClass: toolClass ?? "read",
|
|
215
|
+
riskLevel: riskLevel ?? "low",
|
|
216
|
+
capabilityTags,
|
|
217
|
+
security
|
|
218
|
+
}
|
|
219
|
+
: {})
|
|
220
|
+
},
|
|
221
|
+
{ path: "$" }
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function normalizePaidToolManifestV1(manifestInput) {
|
|
226
|
+
assertPlainObject(manifestInput, "manifest");
|
|
227
|
+
const schemaVersion = normalizeManifestSchemaVersion(manifestInput.schemaVersion);
|
|
228
|
+
const isV2 = schemaVersion === PAID_TOOL_MANIFEST_SCHEMA_VERSION_V2;
|
|
229
|
+
const providerId = normalizeNonEmptyString(manifestInput.providerId, "manifest.providerId", { max: 160 });
|
|
230
|
+
const upstreamBaseUrl = normalizeOptionalString(manifestInput.upstreamBaseUrl, "manifest.upstreamBaseUrl", { max: 2048 });
|
|
231
|
+
const publishProofJwksUrl = normalizeOptionalAbsoluteHttpUrl(
|
|
232
|
+
manifestInput.publishProofJwksUrl,
|
|
233
|
+
"manifest.publishProofJwksUrl"
|
|
234
|
+
);
|
|
235
|
+
const sourceOpenApiPath = normalizeOptionalString(manifestInput.sourceOpenApiPath, "manifest.sourceOpenApiPath", { max: 2048 });
|
|
236
|
+
|
|
237
|
+
const defaultsRaw =
|
|
238
|
+
manifestInput.defaults && typeof manifestInput.defaults === "object" && !Array.isArray(manifestInput.defaults)
|
|
239
|
+
? manifestInput.defaults
|
|
240
|
+
: {};
|
|
241
|
+
const defaults = normalizeForCanonicalJson(
|
|
242
|
+
{
|
|
243
|
+
amountCents: normalizePositiveSafeInt(defaultsRaw.amountCents ?? 500, "manifest.defaults.amountCents"),
|
|
244
|
+
currency: normalizeCurrency(defaultsRaw.currency ?? "USD", "manifest.defaults.currency"),
|
|
245
|
+
idempotency: normalizeOptionalString(defaultsRaw.idempotency, "manifest.defaults.idempotency", { max: 64 }) ?? "idempotent",
|
|
246
|
+
signatureMode: normalizeOptionalString(defaultsRaw.signatureMode, "manifest.defaults.signatureMode", { max: 64 }) ?? "required",
|
|
247
|
+
...(isV2
|
|
248
|
+
? {
|
|
249
|
+
toolClass: normalizeToolClass(defaultsRaw.toolClass ?? defaultsRaw.tool_class ?? null, "manifest.defaults.toolClass") ?? "read",
|
|
250
|
+
riskLevel: normalizeRiskLevel(defaultsRaw.riskLevel ?? defaultsRaw.risk_level ?? null, "manifest.defaults.riskLevel") ?? "low",
|
|
251
|
+
requiredSignatures: normalizeRequiredSignatures(
|
|
252
|
+
defaultsRaw.requiredSignatures ?? defaultsRaw.required_signatures ?? ["output"],
|
|
253
|
+
"manifest.defaults.requiredSignatures"
|
|
254
|
+
),
|
|
255
|
+
requestBinding:
|
|
256
|
+
normalizeRequestBinding(
|
|
257
|
+
defaultsRaw.requestBinding ?? defaultsRaw.request_binding ?? null,
|
|
258
|
+
"manifest.defaults.requestBinding"
|
|
259
|
+
) ?? "recommended"
|
|
260
|
+
}
|
|
261
|
+
: {})
|
|
262
|
+
},
|
|
263
|
+
{ path: "$" }
|
|
264
|
+
);
|
|
265
|
+
if (!["idempotent", "non_idempotent", "side_effecting"].includes(String(defaults.idempotency))) {
|
|
266
|
+
throw new TypeError("manifest.defaults.idempotency must be idempotent|non_idempotent|side_effecting");
|
|
267
|
+
}
|
|
268
|
+
if (!["required", "optional"].includes(String(defaults.signatureMode))) {
|
|
269
|
+
throw new TypeError("manifest.defaults.signatureMode must be required|optional");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!Array.isArray(manifestInput.tools) || manifestInput.tools.length === 0) {
|
|
273
|
+
throw new TypeError("manifest.tools must be a non-empty array");
|
|
274
|
+
}
|
|
275
|
+
const tools = manifestInput.tools.map((row, index) => normalizeTool(row, defaults, { index, schemaVersion }));
|
|
276
|
+
const seenToolIds = new Set();
|
|
277
|
+
const seenPaidPaths = new Set();
|
|
278
|
+
for (const tool of tools) {
|
|
279
|
+
if (seenToolIds.has(tool.toolId)) throw new TypeError(`manifest.tools contains duplicate toolId: ${tool.toolId}`);
|
|
280
|
+
if (seenPaidPaths.has(tool.paidPath)) throw new TypeError(`manifest.tools contains duplicate paidPath: ${tool.paidPath}`);
|
|
281
|
+
seenToolIds.add(tool.toolId);
|
|
282
|
+
seenPaidPaths.add(tool.paidPath);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const capabilityTags = normalizeStringArray(
|
|
286
|
+
manifestInput.capabilityTags ?? manifestInput.capability_tags ?? manifestInput.tags ?? [],
|
|
287
|
+
"manifest.capabilityTags",
|
|
288
|
+
{ maxItems: 64, maxLen: 80 }
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
return normalizeForCanonicalJson(
|
|
292
|
+
{
|
|
293
|
+
schemaVersion,
|
|
294
|
+
providerId,
|
|
295
|
+
upstreamBaseUrl,
|
|
296
|
+
publishProofJwksUrl,
|
|
297
|
+
sourceOpenApiPath,
|
|
298
|
+
defaults,
|
|
299
|
+
tools,
|
|
300
|
+
...(isV2 ? { capabilityTags } : {})
|
|
301
|
+
},
|
|
302
|
+
{ path: "$" }
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function validatePaidToolManifestV1(manifestInput) {
|
|
307
|
+
try {
|
|
308
|
+
const manifest = normalizePaidToolManifestV1(manifestInput);
|
|
309
|
+
return { ok: true, manifest };
|
|
310
|
+
} catch (err) {
|
|
311
|
+
return { ok: false, code: "PAID_TOOL_MANIFEST_INVALID", message: err?.message ?? String(err ?? "") };
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function computePaidToolManifestHashV1(manifestInput) {
|
|
316
|
+
const manifest = normalizePaidToolManifestV1(manifestInput);
|
|
317
|
+
return sha256Hex(canonicalJsonStringify(manifest));
|
|
318
|
+
}
|