settld 0.1.1 → 0.1.5
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 +61 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +13 -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 +140 -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 +337 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +143 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -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 +15 -0
- package/docs/RELEASE_CHECKLIST.md +156 -0
- package/docs/RELEASING.md +81 -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 +70 -0
- package/docs/SUMMARY.md +16 -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 +52 -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 +104 -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 +14 -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/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/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +67 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +28 -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 +103 -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 +47 -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/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/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/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/PRODUCER_ERRORS.md +42 -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 +104 -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 +51 -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/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 +148 -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 +21 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +40 -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 +148 -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 +246 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +325 -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-smoke.mjs +275 -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/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 +1201 -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/hosted-baseline-evidence.mjs +681 -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/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/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/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/slo/check.mjs +178 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +931 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +45 -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 +947 -0
- package/src/api/app.js +32517 -16877
- package/src/api/maintenance.js +70 -0
- package/src/api/openapi.js +1130 -17
- package/src/api/persistence.js +272 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1248 -6
- 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 +97 -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/maintenance-locks.js +1 -0
- package/src/core/paid-tool-manifest.js +318 -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 +213 -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/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 +838 -72
package/src/api/store.js
CHANGED
|
@@ -18,23 +18,100 @@ import { computeFinanceAccountMapHash, validateFinanceAccountMapV1 } from "../co
|
|
|
18
18
|
import { appendChainedEvent, createChainedEvent } from "../core/event-chain.js";
|
|
19
19
|
import { normalizeBillingPlanId } from "../core/billing-plans.js";
|
|
20
20
|
|
|
21
|
+
const SERVER_SIGNER_FILENAME = "server-signer.json";
|
|
22
|
+
const SETTLD_PAY_KEYSET_STORE_FILENAME = "settld-pay-keyset-store.json";
|
|
23
|
+
|
|
24
|
+
function readJsonFileSafe(filePath) {
|
|
25
|
+
if (!fs.existsSync(filePath)) return null;
|
|
26
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeSettldPayPreviousRows(rows) {
|
|
31
|
+
const out = [];
|
|
32
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
for (const row of list) {
|
|
35
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
36
|
+
const publicKeyPem = typeof row.publicKeyPem === "string" ? row.publicKeyPem : "";
|
|
37
|
+
if (!publicKeyPem.trim()) continue;
|
|
38
|
+
const derivedKeyId = keyIdFromPublicKeyPem(publicKeyPem);
|
|
39
|
+
const keyId = typeof row.keyId === "string" && row.keyId.trim() !== "" ? row.keyId.trim() : derivedKeyId;
|
|
40
|
+
if (keyId !== derivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: previous[].keyId mismatch");
|
|
41
|
+
if (seen.has(keyId)) continue;
|
|
42
|
+
seen.add(keyId);
|
|
43
|
+
const rotatedAt = typeof row.rotatedAt === "string" && row.rotatedAt.trim() !== "" ? row.rotatedAt.trim() : null;
|
|
44
|
+
out.push({ keyId, publicKeyPem, rotatedAt });
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeSettldPayKeysetStore(payload) {
|
|
50
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
51
|
+
throw new Error("invalid settld-pay-keyset-store.json: object expected");
|
|
52
|
+
}
|
|
53
|
+
const active = payload.active;
|
|
54
|
+
if (!active || typeof active !== "object" || Array.isArray(active)) {
|
|
55
|
+
throw new Error("invalid settld-pay-keyset-store.json: active key missing");
|
|
56
|
+
}
|
|
57
|
+
const activePublicKeyPem = typeof active.publicKeyPem === "string" ? active.publicKeyPem : "";
|
|
58
|
+
const activePrivateKeyPem = typeof active.privateKeyPem === "string" ? active.privateKeyPem : "";
|
|
59
|
+
if (!activePublicKeyPem.trim() || !activePrivateKeyPem.trim()) {
|
|
60
|
+
throw new Error("invalid settld-pay-keyset-store.json: active keypair missing");
|
|
61
|
+
}
|
|
62
|
+
const activeDerivedKeyId = keyIdFromPublicKeyPem(activePublicKeyPem);
|
|
63
|
+
const activeKeyId = typeof active.keyId === "string" && active.keyId.trim() !== "" ? active.keyId.trim() : activeDerivedKeyId;
|
|
64
|
+
if (activeKeyId !== activeDerivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: active.keyId mismatch");
|
|
65
|
+
|
|
66
|
+
const previous = normalizeSettldPayPreviousRows(payload.previous);
|
|
67
|
+
return {
|
|
68
|
+
active: {
|
|
69
|
+
keyId: activeKeyId,
|
|
70
|
+
publicKeyPem: activePublicKeyPem,
|
|
71
|
+
privateKeyPem: activePrivateKeyPem
|
|
72
|
+
},
|
|
73
|
+
previous
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
21
77
|
function loadOrCreateServerSigner({ persistenceDir }) {
|
|
22
78
|
if (!persistenceDir) {
|
|
23
79
|
const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
|
|
24
|
-
return { publicKeyPem, privateKeyPem };
|
|
80
|
+
return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-ephemeral" };
|
|
25
81
|
}
|
|
26
82
|
|
|
27
83
|
fs.mkdirSync(persistenceDir, { recursive: true });
|
|
28
|
-
const
|
|
84
|
+
const keysetStorePath = path.join(persistenceDir, SETTLD_PAY_KEYSET_STORE_FILENAME);
|
|
85
|
+
const signerPath = path.join(persistenceDir, SERVER_SIGNER_FILENAME);
|
|
86
|
+
if (fs.existsSync(keysetStorePath)) {
|
|
87
|
+
const parsed = readJsonFileSafe(keysetStorePath);
|
|
88
|
+
const normalized = normalizeSettldPayKeysetStore(parsed);
|
|
89
|
+
// Keep legacy signer file in sync for compatibility with existing tooling.
|
|
90
|
+
fs.writeFileSync(
|
|
91
|
+
signerPath,
|
|
92
|
+
JSON.stringify({ publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem }, null, 2),
|
|
93
|
+
"utf8"
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
active: { publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem },
|
|
97
|
+
previous: normalized.previous,
|
|
98
|
+
source: "keyset-store"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
29
102
|
if (fs.existsSync(signerPath)) {
|
|
30
103
|
const parsed = JSON.parse(fs.readFileSync(signerPath, "utf8"));
|
|
31
104
|
if (!parsed?.publicKeyPem || !parsed?.privateKeyPem) throw new Error("invalid server-signer.json");
|
|
32
|
-
return {
|
|
105
|
+
return {
|
|
106
|
+
active: { publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem },
|
|
107
|
+
previous: [],
|
|
108
|
+
source: "legacy-signer"
|
|
109
|
+
};
|
|
33
110
|
}
|
|
34
111
|
|
|
35
112
|
const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
|
|
36
113
|
fs.writeFileSync(signerPath, JSON.stringify({ publicKeyPem, privateKeyPem }, null, 2), "utf8");
|
|
37
|
-
return { publicKeyPem, privateKeyPem };
|
|
114
|
+
return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-persistent" };
|
|
38
115
|
}
|
|
39
116
|
|
|
40
117
|
export function createStore({ persistenceDir = null, serverSignerKeypair = null } = {}) {
|
|
@@ -49,8 +126,18 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
49
126
|
throw new Error("invalid serverSignerKeypair");
|
|
50
127
|
}
|
|
51
128
|
|
|
52
|
-
const
|
|
53
|
-
resolvedServerSignerKeypair
|
|
129
|
+
const loadedServerSigner =
|
|
130
|
+
resolvedServerSignerKeypair === null
|
|
131
|
+
? loadOrCreateServerSigner({ persistenceDir })
|
|
132
|
+
: {
|
|
133
|
+
active: {
|
|
134
|
+
publicKeyPem: resolvedServerSignerKeypair.publicKeyPem,
|
|
135
|
+
privateKeyPem: resolvedServerSignerKeypair.privateKeyPem
|
|
136
|
+
},
|
|
137
|
+
previous: [],
|
|
138
|
+
source: "explicit-override"
|
|
139
|
+
};
|
|
140
|
+
const { publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem } = loadedServerSigner.active;
|
|
54
141
|
const serverKeyId = keyIdFromPublicKeyPem(serverPublicKeyPem);
|
|
55
142
|
|
|
56
143
|
function parseEvidenceRetentionMaxDaysByTenant() {
|
|
@@ -222,6 +309,19 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
222
309
|
agentRunEvents: new Map(), // `${tenantId}\n${runId}` -> AgentEvent.v1[]
|
|
223
310
|
agentRunSettlements: new Map(), // `${tenantId}\n${runId}` -> AgentRunSettlement.v1
|
|
224
311
|
arbitrationCases: new Map(), // `${tenantId}\n${caseId}` -> ArbitrationCase.v1 snapshot
|
|
312
|
+
agreementDelegations: new Map(), // `${tenantId}\n${delegationId}` -> AgreementDelegation.v1
|
|
313
|
+
x402Gates: new Map(), // `${tenantId}\n${gateId}` -> X402 gate record (internal API surface)
|
|
314
|
+
x402Receipts: new Map(), // `${tenantId}\n${receiptId}` -> immutable X402ReceiptRecord.v1 base record
|
|
315
|
+
x402WalletPolicies: new Map(), // `${tenantId}\n${sponsorWalletRef}::${policyRef}::${policyVersion}` -> X402WalletPolicy.v1
|
|
316
|
+
x402ZkVerificationKeys: new Map(), // `${tenantId}\n${verificationKeyId}` -> X402ZkVerificationKey.v1
|
|
317
|
+
x402ReversalEvents: new Map(), // `${tenantId}\n${eventId}` -> X402GateReversalEvent.v1
|
|
318
|
+
x402ReversalNonceUsage: new Map(), // `${tenantId}\n${sponsorRef}\n${nonce}` -> X402ReversalNonceUsage.v1
|
|
319
|
+
x402ReversalCommandUsage: new Map(), // `${tenantId}\n${commandId}` -> X402ReversalCommandUsage.v1
|
|
320
|
+
x402Escalations: new Map(), // `${tenantId}\n${escalationId}` -> X402AuthorizationEscalation.v1
|
|
321
|
+
x402EscalationEvents: new Map(), // `${tenantId}\n${eventId}` -> X402AuthorizationEscalationEvent.v1
|
|
322
|
+
x402EscalationOverrideUsage: new Map(), // `${tenantId}\n${overrideId}` -> X402EscalationOverrideUsage.v1
|
|
323
|
+
x402AgentLifecycles: new Map(), // `${tenantId}\n${agentId}` -> X402AgentLifecycle.v1
|
|
324
|
+
x402WebhookEndpoints: new Map(), // `${tenantId}\n${endpointId}` -> X402WebhookEndpoint.v1
|
|
225
325
|
toolCallHolds: new Map(), // `${tenantId}\n${holdHash}` -> FundingHold.v1 snapshot
|
|
226
326
|
settlementAdjustments: new Map(), // `${tenantId}\n${adjustmentId}` -> SettlementAdjustment.v1 snapshot
|
|
227
327
|
moneyRailOperations: new Map(), // `${tenantId}\n${providerId}\n${operationId}` -> MoneyRailOperation.v1
|
|
@@ -230,12 +330,14 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
230
330
|
financeReconciliationTriages: new Map(), // `${tenantId}\n${triageKey}` -> FinanceReconciliationTriage.v1
|
|
231
331
|
marketplaceRfqs: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceRfq.v1
|
|
232
332
|
marketplaceRfqBids: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceBid.v1[]
|
|
333
|
+
marketplaceProviderPublications: new Map(), // `${tenantId}\n${providerRef}` -> MarketplaceProviderPublication.v1
|
|
233
334
|
tenantSettlementPolicies: new Map(), // `${tenantId}\n${policyId}\n${policyVersion}` -> TenantSettlementPolicy.v1
|
|
234
335
|
tenantSettlementPolicyRollouts: new Map(), // `${tenantId}\nrollout` -> TenantSettlementPolicyRollout.v1
|
|
235
336
|
contracts: new Map(), // `${tenantId}\n${contractId}` -> contract
|
|
236
337
|
idempotency: new Map(),
|
|
237
338
|
publicKeyByKeyId,
|
|
238
339
|
serverSigner: { keyId: serverKeyId, publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem },
|
|
340
|
+
settldPayFallbackKeys: loadedServerSigner.previous.map((row) => ({ keyId: row.keyId, publicKeyPem: row.publicKeyPem })),
|
|
239
341
|
ledgerByTenant,
|
|
240
342
|
// Back-compat: keep store.ledger and store.config as the default tenant's objects.
|
|
241
343
|
ledger: ledgerByTenant.get(DEFAULT_TENANT_ID),
|
|
@@ -254,6 +356,7 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
254
356
|
monthCloseCursor: 0,
|
|
255
357
|
artifactCursor: 0,
|
|
256
358
|
deliveryCursor: 0,
|
|
359
|
+
x402WinddownReversalCursor: 0,
|
|
257
360
|
persistence: persistenceDir ? createFileTxLog({ dir: persistenceDir }) : null
|
|
258
361
|
};
|
|
259
362
|
|
|
@@ -782,12 +885,1102 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
782
885
|
return store.agentRunSettlements.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
|
|
783
886
|
};
|
|
784
887
|
|
|
888
|
+
store.sumWalletPolicySpendCentsForDay = async function sumWalletPolicySpendCentsForDay({
|
|
889
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
890
|
+
agentId,
|
|
891
|
+
dayStartIso,
|
|
892
|
+
dayEndIso
|
|
893
|
+
} = {}) {
|
|
894
|
+
tenantId = normalizeTenantId(tenantId);
|
|
895
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
896
|
+
if (typeof dayStartIso !== "string" || dayStartIso.trim() === "" || !Number.isFinite(Date.parse(dayStartIso))) {
|
|
897
|
+
throw new TypeError("dayStartIso must be an ISO date string");
|
|
898
|
+
}
|
|
899
|
+
if (typeof dayEndIso !== "string" || dayEndIso.trim() === "" || !Number.isFinite(Date.parse(dayEndIso))) {
|
|
900
|
+
throw new TypeError("dayEndIso must be an ISO date string");
|
|
901
|
+
}
|
|
902
|
+
const startMs = Date.parse(dayStartIso);
|
|
903
|
+
const endMs = Date.parse(dayEndIso);
|
|
904
|
+
if (!(endMs > startMs)) throw new TypeError("dayEndIso must be after dayStartIso");
|
|
905
|
+
|
|
906
|
+
let total = 0;
|
|
907
|
+
|
|
908
|
+
// Agent run settlements lock escrow for the full settlement amount.
|
|
909
|
+
for (const row of store.agentRunSettlements.values()) {
|
|
910
|
+
if (!row || typeof row !== "object") continue;
|
|
911
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
912
|
+
if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
|
|
913
|
+
const lockedAt = row.lockedAt ?? null;
|
|
914
|
+
const lockedMs = typeof lockedAt === "string" ? Date.parse(lockedAt) : NaN;
|
|
915
|
+
if (!Number.isFinite(lockedMs) || lockedMs < startMs || lockedMs >= endMs) continue;
|
|
916
|
+
const amountCents = Number(row.amountCents ?? 0);
|
|
917
|
+
if (!Number.isSafeInteger(amountCents) || amountCents <= 0) continue;
|
|
918
|
+
total += amountCents;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Tool-call holds lock escrow for the holdback amount only.
|
|
922
|
+
for (const row of store.toolCallHolds.values()) {
|
|
923
|
+
if (!row || typeof row !== "object") continue;
|
|
924
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
925
|
+
if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
|
|
926
|
+
const createdAt = row.createdAt ?? null;
|
|
927
|
+
const createdMs = typeof createdAt === "string" ? Date.parse(createdAt) : NaN;
|
|
928
|
+
if (!Number.isFinite(createdMs) || createdMs < startMs || createdMs >= endMs) continue;
|
|
929
|
+
const heldAmountCents = Number(row.heldAmountCents ?? 0);
|
|
930
|
+
if (!Number.isSafeInteger(heldAmountCents) || heldAmountCents <= 0) continue;
|
|
931
|
+
total += heldAmountCents;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
return total;
|
|
935
|
+
};
|
|
936
|
+
|
|
785
937
|
store.getArbitrationCase = async function getArbitrationCase({ tenantId = DEFAULT_TENANT_ID, caseId } = {}) {
|
|
786
938
|
tenantId = normalizeTenantId(tenantId);
|
|
787
939
|
if (typeof caseId !== "string" || caseId.trim() === "") throw new TypeError("caseId is required");
|
|
788
940
|
return store.arbitrationCases.get(makeScopedKey({ tenantId, id: String(caseId) })) ?? null;
|
|
789
941
|
};
|
|
790
942
|
|
|
943
|
+
store.getAgreementDelegation = async function getAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegationId } = {}) {
|
|
944
|
+
tenantId = normalizeTenantId(tenantId);
|
|
945
|
+
if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegationId is required");
|
|
946
|
+
return store.agreementDelegations.get(makeScopedKey({ tenantId, id: String(delegationId) })) ?? null;
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
store.putAgreementDelegation = async function putAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegation, audit = null } = {}) {
|
|
950
|
+
tenantId = normalizeTenantId(tenantId);
|
|
951
|
+
if (!delegation || typeof delegation !== "object" || Array.isArray(delegation)) throw new TypeError("delegation is required");
|
|
952
|
+
const delegationId = delegation.delegationId ?? null;
|
|
953
|
+
if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegation.delegationId is required");
|
|
954
|
+
const key = makeScopedKey({ tenantId, id: String(delegationId) });
|
|
955
|
+
const at = delegation.updatedAt ?? new Date().toISOString();
|
|
956
|
+
await store.commitTx({ at, ops: [{ kind: "AGREEMENT_DELEGATION_UPSERT", tenantId, delegationId, delegation: { ...delegation, tenantId, delegationId } }], audit });
|
|
957
|
+
return store.agreementDelegations.get(key) ?? null;
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
store.listAgreementDelegations = async function listAgreementDelegations({
|
|
961
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
962
|
+
parentAgreementHash = null,
|
|
963
|
+
childAgreementHash = null,
|
|
964
|
+
status = null,
|
|
965
|
+
limit = 200,
|
|
966
|
+
offset = 0
|
|
967
|
+
} = {}) {
|
|
968
|
+
tenantId = normalizeTenantId(tenantId);
|
|
969
|
+
if (parentAgreementHash !== null && (typeof parentAgreementHash !== "string" || parentAgreementHash.trim() === "")) {
|
|
970
|
+
throw new TypeError("parentAgreementHash must be null or a non-empty string");
|
|
971
|
+
}
|
|
972
|
+
if (childAgreementHash !== null && (typeof childAgreementHash !== "string" || childAgreementHash.trim() === "")) {
|
|
973
|
+
throw new TypeError("childAgreementHash must be null or a non-empty string");
|
|
974
|
+
}
|
|
975
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
976
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
977
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
978
|
+
|
|
979
|
+
const statusFilter = status ? String(status).trim().toLowerCase() : null;
|
|
980
|
+
const parentFilter = parentAgreementHash ? String(parentAgreementHash).trim().toLowerCase() : null;
|
|
981
|
+
const childFilter = childAgreementHash ? String(childAgreementHash).trim().toLowerCase() : null;
|
|
982
|
+
const safeLimit = Math.min(1000, limit);
|
|
983
|
+
const safeOffset = offset;
|
|
984
|
+
const out = [];
|
|
985
|
+
for (const row of store.agreementDelegations.values()) {
|
|
986
|
+
if (!row || typeof row !== "object") continue;
|
|
987
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
988
|
+
if (parentFilter && String(row.parentAgreementHash ?? "").toLowerCase() !== parentFilter) continue;
|
|
989
|
+
if (childFilter && String(row.childAgreementHash ?? "").toLowerCase() !== childFilter) continue;
|
|
990
|
+
if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
991
|
+
out.push(row);
|
|
992
|
+
}
|
|
993
|
+
out.sort((left, right) => String(left.delegationId ?? "").localeCompare(String(right.delegationId ?? "")));
|
|
994
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
store.getX402Gate = async function getX402Gate({ tenantId = DEFAULT_TENANT_ID, gateId } = {}) {
|
|
998
|
+
tenantId = normalizeTenantId(tenantId);
|
|
999
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
|
|
1000
|
+
return store.x402Gates.get(makeScopedKey({ tenantId, id: String(gateId) })) ?? null;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
store.putX402Gate = async function putX402Gate({ tenantId = DEFAULT_TENANT_ID, gate, audit = null } = {}) {
|
|
1004
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1005
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) throw new TypeError("gate is required");
|
|
1006
|
+
const gateId = gate.gateId ?? gate.id ?? null;
|
|
1007
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gate.gateId is required");
|
|
1008
|
+
const key = makeScopedKey({ tenantId, id: String(gateId) });
|
|
1009
|
+
const at = gate.updatedAt ?? gate.createdAt ?? new Date().toISOString();
|
|
1010
|
+
await store.commitTx({ at, ops: [{ kind: "X402_GATE_UPSERT", tenantId, gateId, gate: { ...gate, tenantId, gateId: String(gateId) } }], audit });
|
|
1011
|
+
return store.x402Gates.get(key) ?? null;
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
store.putX402AgentLifecycle = async function putX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentLifecycle, audit = null } = {}) {
|
|
1015
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1016
|
+
if (!agentLifecycle || typeof agentLifecycle !== "object" || Array.isArray(agentLifecycle)) {
|
|
1017
|
+
throw new TypeError("agentLifecycle is required");
|
|
1018
|
+
}
|
|
1019
|
+
const agentId = typeof agentLifecycle.agentId === "string" ? agentLifecycle.agentId.trim() : "";
|
|
1020
|
+
if (!agentId) throw new TypeError("agentLifecycle.agentId is required");
|
|
1021
|
+
const status = typeof agentLifecycle.status === "string" ? agentLifecycle.status.trim().toLowerCase() : "";
|
|
1022
|
+
if (status !== "active" && status !== "frozen" && status !== "archived") {
|
|
1023
|
+
throw new TypeError("agentLifecycle.status must be active|frozen|archived");
|
|
1024
|
+
}
|
|
1025
|
+
const at = agentLifecycle.updatedAt ?? agentLifecycle.createdAt ?? new Date().toISOString();
|
|
1026
|
+
await store.commitTx({
|
|
1027
|
+
at,
|
|
1028
|
+
ops: [
|
|
1029
|
+
{
|
|
1030
|
+
kind: "X402_AGENT_LIFECYCLE_UPSERT",
|
|
1031
|
+
tenantId,
|
|
1032
|
+
agentId,
|
|
1033
|
+
agentLifecycle: { ...agentLifecycle, tenantId, agentId, status }
|
|
1034
|
+
}
|
|
1035
|
+
],
|
|
1036
|
+
audit
|
|
1037
|
+
});
|
|
1038
|
+
return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: agentId })) ?? null;
|
|
1039
|
+
};
|
|
1040
|
+
|
|
1041
|
+
store.getX402AgentLifecycle = async function getX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
|
|
1042
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1043
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
1044
|
+
return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
function x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion }) {
|
|
1048
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1049
|
+
const normalizedSponsorWalletRef = typeof sponsorWalletRef === "string" ? sponsorWalletRef.trim() : "";
|
|
1050
|
+
const normalizedPolicyRef = typeof policyRef === "string" ? policyRef.trim() : "";
|
|
1051
|
+
const normalizedPolicyVersion = Number(policyVersion);
|
|
1052
|
+
if (!normalizedSponsorWalletRef) throw new TypeError("sponsorWalletRef is required");
|
|
1053
|
+
if (!normalizedPolicyRef) throw new TypeError("policyRef is required");
|
|
1054
|
+
if (!Number.isSafeInteger(normalizedPolicyVersion) || normalizedPolicyVersion <= 0) {
|
|
1055
|
+
throw new TypeError("policyVersion must be a positive safe integer");
|
|
1056
|
+
}
|
|
1057
|
+
return makeScopedKey({
|
|
1058
|
+
tenantId: normalizedTenantId,
|
|
1059
|
+
id: `${normalizedSponsorWalletRef}::${normalizedPolicyRef}::${normalizedPolicyVersion}`
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function x402WebhookEndpointStoreKey({ tenantId, endpointId }) {
|
|
1064
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1065
|
+
const normalizedEndpointId = typeof endpointId === "string" ? endpointId.trim() : "";
|
|
1066
|
+
if (!normalizedEndpointId) throw new TypeError("endpointId is required");
|
|
1067
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedEndpointId });
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId }) {
|
|
1071
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1072
|
+
const normalizedVerificationKeyId = typeof verificationKeyId === "string" ? verificationKeyId.trim() : "";
|
|
1073
|
+
if (!normalizedVerificationKeyId) throw new TypeError("verificationKeyId is required");
|
|
1074
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedVerificationKeyId });
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
store.putX402WalletPolicy = async function putX402WalletPolicy({ tenantId = DEFAULT_TENANT_ID, policy, audit = null } = {}) {
|
|
1078
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1079
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy)) throw new TypeError("policy is required");
|
|
1080
|
+
const sponsorWalletRef = typeof policy.sponsorWalletRef === "string" ? policy.sponsorWalletRef.trim() : "";
|
|
1081
|
+
const policyRef = typeof policy.policyRef === "string" ? policy.policyRef.trim() : "";
|
|
1082
|
+
const policyVersion = Number(policy.policyVersion);
|
|
1083
|
+
if (!sponsorWalletRef) throw new TypeError("policy.sponsorWalletRef is required");
|
|
1084
|
+
if (!policyRef) throw new TypeError("policy.policyRef is required");
|
|
1085
|
+
if (!Number.isSafeInteger(policyVersion) || policyVersion <= 0) throw new TypeError("policy.policyVersion must be a positive safe integer");
|
|
1086
|
+
const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
|
|
1087
|
+
const at = policy.updatedAt ?? policy.createdAt ?? new Date().toISOString();
|
|
1088
|
+
await store.commitTx({
|
|
1089
|
+
at,
|
|
1090
|
+
ops: [
|
|
1091
|
+
{
|
|
1092
|
+
kind: "X402_WALLET_POLICY_UPSERT",
|
|
1093
|
+
tenantId,
|
|
1094
|
+
policy: { ...policy, tenantId, sponsorWalletRef, policyRef, policyVersion }
|
|
1095
|
+
}
|
|
1096
|
+
],
|
|
1097
|
+
audit
|
|
1098
|
+
});
|
|
1099
|
+
return store.x402WalletPolicies.get(key) ?? null;
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
store.getX402WalletPolicy = async function getX402WalletPolicy({
|
|
1103
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1104
|
+
sponsorWalletRef,
|
|
1105
|
+
policyRef,
|
|
1106
|
+
policyVersion
|
|
1107
|
+
} = {}) {
|
|
1108
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1109
|
+
const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
|
|
1110
|
+
return store.x402WalletPolicies.get(key) ?? null;
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
store.listX402WalletPolicies = async function listX402WalletPolicies({
|
|
1114
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1115
|
+
sponsorWalletRef = null,
|
|
1116
|
+
sponsorRef = null,
|
|
1117
|
+
policyRef = null,
|
|
1118
|
+
status = null,
|
|
1119
|
+
limit = 200,
|
|
1120
|
+
offset = 0
|
|
1121
|
+
} = {}) {
|
|
1122
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1123
|
+
if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
|
|
1124
|
+
throw new TypeError("sponsorWalletRef must be null or a non-empty string");
|
|
1125
|
+
}
|
|
1126
|
+
if (sponsorRef !== null && (typeof sponsorRef !== "string" || sponsorRef.trim() === "")) {
|
|
1127
|
+
throw new TypeError("sponsorRef must be null or a non-empty string");
|
|
1128
|
+
}
|
|
1129
|
+
if (policyRef !== null && (typeof policyRef !== "string" || policyRef.trim() === "")) {
|
|
1130
|
+
throw new TypeError("policyRef must be null or a non-empty string");
|
|
1131
|
+
}
|
|
1132
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) {
|
|
1133
|
+
throw new TypeError("status must be null or a non-empty string");
|
|
1134
|
+
}
|
|
1135
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1136
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1137
|
+
|
|
1138
|
+
const sponsorWalletFilter = sponsorWalletRef ? sponsorWalletRef.trim() : null;
|
|
1139
|
+
const sponsorFilter = sponsorRef ? sponsorRef.trim() : null;
|
|
1140
|
+
const policyFilter = policyRef ? policyRef.trim() : null;
|
|
1141
|
+
const statusFilter = status ? status.trim().toLowerCase() : null;
|
|
1142
|
+
const out = [];
|
|
1143
|
+
for (const row of store.x402WalletPolicies.values()) {
|
|
1144
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1145
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1146
|
+
if (sponsorWalletFilter && String(row.sponsorWalletRef ?? "") !== sponsorWalletFilter) continue;
|
|
1147
|
+
if (sponsorFilter && String(row.sponsorRef ?? "") !== sponsorFilter) continue;
|
|
1148
|
+
if (policyFilter && String(row.policyRef ?? "") !== policyFilter) continue;
|
|
1149
|
+
if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1150
|
+
out.push(row);
|
|
1151
|
+
}
|
|
1152
|
+
out.sort((left, right) => {
|
|
1153
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1154
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1155
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1156
|
+
const sponsorOrder = String(left?.sponsorWalletRef ?? "").localeCompare(String(right?.sponsorWalletRef ?? ""));
|
|
1157
|
+
if (sponsorOrder !== 0) return sponsorOrder;
|
|
1158
|
+
const policyOrder = String(left?.policyRef ?? "").localeCompare(String(right?.policyRef ?? ""));
|
|
1159
|
+
if (policyOrder !== 0) return policyOrder;
|
|
1160
|
+
return Number(right?.policyVersion ?? 0) - Number(left?.policyVersion ?? 0);
|
|
1161
|
+
});
|
|
1162
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
store.putX402ZkVerificationKey = async function putX402ZkVerificationKey({
|
|
1166
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1167
|
+
verificationKey,
|
|
1168
|
+
audit = null
|
|
1169
|
+
} = {}) {
|
|
1170
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1171
|
+
if (!verificationKey || typeof verificationKey !== "object" || Array.isArray(verificationKey)) {
|
|
1172
|
+
throw new TypeError("verificationKey is required");
|
|
1173
|
+
}
|
|
1174
|
+
const verificationKeyId = typeof verificationKey.verificationKeyId === "string" ? verificationKey.verificationKeyId.trim() : "";
|
|
1175
|
+
if (!verificationKeyId) throw new TypeError("verificationKey.verificationKeyId is required");
|
|
1176
|
+
const key = x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId });
|
|
1177
|
+
const at = verificationKey.updatedAt ?? verificationKey.createdAt ?? new Date().toISOString();
|
|
1178
|
+
await store.commitTx({
|
|
1179
|
+
at,
|
|
1180
|
+
ops: [
|
|
1181
|
+
{
|
|
1182
|
+
kind: "X402_ZK_VERIFICATION_KEY_PUT",
|
|
1183
|
+
tenantId,
|
|
1184
|
+
verificationKeyId,
|
|
1185
|
+
verificationKey: { ...verificationKey, tenantId, verificationKeyId }
|
|
1186
|
+
}
|
|
1187
|
+
],
|
|
1188
|
+
audit
|
|
1189
|
+
});
|
|
1190
|
+
return store.x402ZkVerificationKeys.get(key) ?? null;
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
store.getX402ZkVerificationKey = async function getX402ZkVerificationKey({
|
|
1194
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1195
|
+
verificationKeyId
|
|
1196
|
+
} = {}) {
|
|
1197
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1198
|
+
return store.x402ZkVerificationKeys.get(x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId })) ?? null;
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
store.listX402ZkVerificationKeys = async function listX402ZkVerificationKeys({
|
|
1202
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1203
|
+
protocol = null,
|
|
1204
|
+
providerRef = null,
|
|
1205
|
+
limit = 200,
|
|
1206
|
+
offset = 0
|
|
1207
|
+
} = {}) {
|
|
1208
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1209
|
+
if (protocol !== null && (typeof protocol !== "string" || protocol.trim() === "")) {
|
|
1210
|
+
throw new TypeError("protocol must be null or a non-empty string");
|
|
1211
|
+
}
|
|
1212
|
+
if (providerRef !== null && (typeof providerRef !== "string" || providerRef.trim() === "")) {
|
|
1213
|
+
throw new TypeError("providerRef must be null or a non-empty string");
|
|
1214
|
+
}
|
|
1215
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1216
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1217
|
+
const protocolFilter = protocol ? protocol.trim().toLowerCase() : null;
|
|
1218
|
+
const providerRefFilter = providerRef ? providerRef.trim() : null;
|
|
1219
|
+
const out = [];
|
|
1220
|
+
for (const row of store.x402ZkVerificationKeys.values()) {
|
|
1221
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1222
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1223
|
+
if (protocolFilter && String(row.protocol ?? "").trim().toLowerCase() !== protocolFilter) continue;
|
|
1224
|
+
if (providerRefFilter && String(row.providerRef ?? "") !== providerRefFilter) continue;
|
|
1225
|
+
out.push(row);
|
|
1226
|
+
}
|
|
1227
|
+
out.sort((left, right) => {
|
|
1228
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.createdAt ?? ""))) ? Date.parse(String(left.createdAt)) : Number.NaN;
|
|
1229
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.createdAt ?? ""))) ? Date.parse(String(right.createdAt)) : Number.NaN;
|
|
1230
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1231
|
+
return String(left?.verificationKeyId ?? "").localeCompare(String(right?.verificationKeyId ?? ""));
|
|
1232
|
+
});
|
|
1233
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
store.putX402WebhookEndpoint = async function putX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpoint, audit = null } = {}) {
|
|
1237
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1238
|
+
if (!endpoint || typeof endpoint !== "object" || Array.isArray(endpoint)) throw new TypeError("endpoint is required");
|
|
1239
|
+
const endpointId = typeof endpoint.endpointId === "string" ? endpoint.endpointId.trim() : "";
|
|
1240
|
+
if (!endpointId) throw new TypeError("endpoint.endpointId is required");
|
|
1241
|
+
const at = endpoint.updatedAt ?? endpoint.createdAt ?? new Date().toISOString();
|
|
1242
|
+
await store.commitTx({
|
|
1243
|
+
at,
|
|
1244
|
+
ops: [{ kind: "X402_WEBHOOK_ENDPOINT_UPSERT", tenantId, endpointId, endpoint: { ...endpoint, tenantId, endpointId } }],
|
|
1245
|
+
audit
|
|
1246
|
+
});
|
|
1247
|
+
return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
store.getX402WebhookEndpoint = async function getX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpointId } = {}) {
|
|
1251
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1252
|
+
return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
store.listX402WebhookEndpoints = async function listX402WebhookEndpoints({
|
|
1256
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1257
|
+
endpointId = null,
|
|
1258
|
+
destinationId = null,
|
|
1259
|
+
status = null,
|
|
1260
|
+
event = null,
|
|
1261
|
+
limit = 200,
|
|
1262
|
+
offset = 0
|
|
1263
|
+
} = {}) {
|
|
1264
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1265
|
+
if (endpointId !== null && (typeof endpointId !== "string" || endpointId.trim() === "")) {
|
|
1266
|
+
throw new TypeError("endpointId must be null or a non-empty string");
|
|
1267
|
+
}
|
|
1268
|
+
if (destinationId !== null && (typeof destinationId !== "string" || destinationId.trim() === "")) {
|
|
1269
|
+
throw new TypeError("destinationId must be null or a non-empty string");
|
|
1270
|
+
}
|
|
1271
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) {
|
|
1272
|
+
throw new TypeError("status must be null or a non-empty string");
|
|
1273
|
+
}
|
|
1274
|
+
if (event !== null && (typeof event !== "string" || event.trim() === "")) {
|
|
1275
|
+
throw new TypeError("event must be null or a non-empty string");
|
|
1276
|
+
}
|
|
1277
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1278
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1279
|
+
|
|
1280
|
+
const endpointFilter = endpointId ? endpointId.trim() : null;
|
|
1281
|
+
const destinationFilter = destinationId ? destinationId.trim() : null;
|
|
1282
|
+
const statusFilter = status ? status.trim().toLowerCase() : null;
|
|
1283
|
+
const eventFilter = event ? event.trim().toLowerCase() : null;
|
|
1284
|
+
const out = [];
|
|
1285
|
+
for (const row of store.x402WebhookEndpoints.values()) {
|
|
1286
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1287
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1288
|
+
if (endpointFilter && String(row.endpointId ?? "") !== endpointFilter) continue;
|
|
1289
|
+
if (destinationFilter && String(row.destinationId ?? "") !== destinationFilter) continue;
|
|
1290
|
+
if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1291
|
+
if (eventFilter) {
|
|
1292
|
+
const events = Array.isArray(row.events) ? row.events.map((value) => String(value).trim().toLowerCase()) : [];
|
|
1293
|
+
if (!events.includes(eventFilter)) continue;
|
|
1294
|
+
}
|
|
1295
|
+
out.push(row);
|
|
1296
|
+
}
|
|
1297
|
+
out.sort((left, right) => {
|
|
1298
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1299
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1300
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1301
|
+
return String(left?.endpointId ?? "").localeCompare(String(right?.endpointId ?? ""));
|
|
1302
|
+
});
|
|
1303
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
store.recordX402WebhookEndpointDeliveryResult = async function recordX402WebhookEndpointDeliveryResult({
|
|
1307
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1308
|
+
destinationId,
|
|
1309
|
+
deliveredAt = new Date().toISOString(),
|
|
1310
|
+
success,
|
|
1311
|
+
failureReason = null,
|
|
1312
|
+
statusCode = null,
|
|
1313
|
+
autoDisableThreshold = null,
|
|
1314
|
+
audit = null
|
|
1315
|
+
} = {}) {
|
|
1316
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1317
|
+
if (typeof destinationId !== "string" || destinationId.trim() === "") throw new TypeError("destinationId is required");
|
|
1318
|
+
const matched = await store.listX402WebhookEndpoints({ tenantId, destinationId: destinationId.trim(), limit: 1, offset: 0 });
|
|
1319
|
+
const endpoint = Array.isArray(matched) && matched.length > 0 ? matched[0] : null;
|
|
1320
|
+
if (!endpoint) return null;
|
|
1321
|
+
if (String(endpoint.status ?? "").toLowerCase() === "revoked") return endpoint;
|
|
1322
|
+
const thresholdRaw = autoDisableThreshold ?? store.x402WebhookAutoDisableFailures ?? 10;
|
|
1323
|
+
const threshold = Number.isSafeInteger(Number(thresholdRaw)) && Number(thresholdRaw) > 0 ? Number(thresholdRaw) : 10;
|
|
1324
|
+
const next = {
|
|
1325
|
+
...endpoint,
|
|
1326
|
+
updatedAt: deliveredAt,
|
|
1327
|
+
lastDeliveryAt: deliveredAt
|
|
1328
|
+
};
|
|
1329
|
+
if (success) {
|
|
1330
|
+
next.consecutiveFailures = 0;
|
|
1331
|
+
next.lastFailureReason = null;
|
|
1332
|
+
next.lastFailureAt = null;
|
|
1333
|
+
next.lastFailureStatusCode = null;
|
|
1334
|
+
} else {
|
|
1335
|
+
const prevFailures = Number.isSafeInteger(Number(endpoint.consecutiveFailures)) ? Number(endpoint.consecutiveFailures) : 0;
|
|
1336
|
+
next.consecutiveFailures = prevFailures + 1;
|
|
1337
|
+
next.lastFailureReason = failureReason ? String(failureReason) : "delivery_failed";
|
|
1338
|
+
next.lastFailureAt = deliveredAt;
|
|
1339
|
+
next.lastFailureStatusCode = Number.isSafeInteger(Number(statusCode)) ? Number(statusCode) : null;
|
|
1340
|
+
if (String(endpoint.status ?? "").toLowerCase() === "active" && next.consecutiveFailures >= threshold) {
|
|
1341
|
+
next.status = "disabled";
|
|
1342
|
+
next.disabledAt = deliveredAt;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return store.putX402WebhookEndpoint({ tenantId, endpoint: next, audit });
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
function x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce }) {
|
|
1349
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1350
|
+
const normalizedSponsorRef = typeof sponsorRef === "string" ? sponsorRef.trim() : "";
|
|
1351
|
+
const normalizedNonce = typeof nonce === "string" ? nonce.trim() : "";
|
|
1352
|
+
if (!normalizedSponsorRef) throw new TypeError("sponsorRef is required");
|
|
1353
|
+
if (!normalizedNonce) throw new TypeError("nonce is required");
|
|
1354
|
+
return `${normalizedTenantId}\n${normalizedSponsorRef}\n${normalizedNonce}`;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
store.putX402ReversalEvent = async function putX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, gateId, event, audit = null } = {}) {
|
|
1358
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1359
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
|
|
1360
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
|
|
1361
|
+
const eventId = event.eventId ?? event.id ?? null;
|
|
1362
|
+
if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("event.eventId is required");
|
|
1363
|
+
const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
|
|
1364
|
+
await store.commitTx({
|
|
1365
|
+
at,
|
|
1366
|
+
ops: [{ kind: "X402_REVERSAL_EVENT_APPEND", tenantId, gateId: String(gateId), eventId: String(eventId), event: { ...event, tenantId, gateId: String(gateId), eventId: String(eventId) } }],
|
|
1367
|
+
audit
|
|
1368
|
+
});
|
|
1369
|
+
return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
|
|
1370
|
+
};
|
|
1371
|
+
|
|
1372
|
+
store.getX402ReversalEvent = async function getX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, eventId } = {}) {
|
|
1373
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1374
|
+
if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("eventId is required");
|
|
1375
|
+
return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1378
|
+
store.listX402ReversalEvents = async function listX402ReversalEvents({
|
|
1379
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1380
|
+
gateId = null,
|
|
1381
|
+
receiptId = null,
|
|
1382
|
+
action = null,
|
|
1383
|
+
from = null,
|
|
1384
|
+
to = null,
|
|
1385
|
+
limit = 200,
|
|
1386
|
+
offset = 0
|
|
1387
|
+
} = {}) {
|
|
1388
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1389
|
+
if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
|
|
1390
|
+
if (receiptId !== null && (typeof receiptId !== "string" || receiptId.trim() === "")) throw new TypeError("receiptId must be null or a non-empty string");
|
|
1391
|
+
if (action !== null && (typeof action !== "string" || action.trim() === "")) throw new TypeError("action must be null or a non-empty string");
|
|
1392
|
+
if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
|
|
1393
|
+
if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
|
|
1394
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1395
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1396
|
+
|
|
1397
|
+
const normalizedGateId = gateId ? gateId.trim() : null;
|
|
1398
|
+
const normalizedReceiptId = receiptId ? receiptId.trim() : null;
|
|
1399
|
+
const normalizedAction = action ? action.trim().toLowerCase() : null;
|
|
1400
|
+
const fromMs = from ? Date.parse(String(from)) : null;
|
|
1401
|
+
const toMs = to ? Date.parse(String(to)) : null;
|
|
1402
|
+
|
|
1403
|
+
const out = [];
|
|
1404
|
+
for (const row of store.x402ReversalEvents.values()) {
|
|
1405
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1406
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1407
|
+
if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
|
|
1408
|
+
if (normalizedReceiptId && String(row.receiptId ?? "") !== normalizedReceiptId) continue;
|
|
1409
|
+
if (normalizedAction && String(row.action ?? "").toLowerCase() !== normalizedAction) continue;
|
|
1410
|
+
const occurredAtMs = Number.isFinite(Date.parse(String(row.occurredAt ?? ""))) ? Date.parse(String(row.occurredAt)) : Number.NaN;
|
|
1411
|
+
if (fromMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs < fromMs)) continue;
|
|
1412
|
+
if (toMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs > toMs)) continue;
|
|
1413
|
+
out.push(row);
|
|
1414
|
+
}
|
|
1415
|
+
out.sort((left, right) => {
|
|
1416
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1417
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1418
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1419
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1420
|
+
});
|
|
1421
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1422
|
+
};
|
|
1423
|
+
|
|
1424
|
+
store.putX402ReversalNonceUsage = async function putX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
1425
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1426
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1427
|
+
const sponsorRef = typeof usage.sponsorRef === "string" ? usage.sponsorRef.trim() : "";
|
|
1428
|
+
const nonce = typeof usage.nonce === "string" ? usage.nonce.trim() : "";
|
|
1429
|
+
if (!sponsorRef) throw new TypeError("usage.sponsorRef is required");
|
|
1430
|
+
if (!nonce) throw new TypeError("usage.nonce is required");
|
|
1431
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1432
|
+
await store.commitTx({
|
|
1433
|
+
at,
|
|
1434
|
+
ops: [{ kind: "X402_REVERSAL_NONCE_PUT", tenantId, sponsorRef, nonce, usage: { ...usage, tenantId, sponsorRef, nonce } }],
|
|
1435
|
+
audit
|
|
1436
|
+
});
|
|
1437
|
+
return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
store.getX402ReversalNonceUsage = async function getX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, sponsorRef, nonce } = {}) {
|
|
1441
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1442
|
+
return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
store.putX402ReversalCommandUsage = async function putX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
1446
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1447
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1448
|
+
const commandId = typeof usage.commandId === "string" ? usage.commandId.trim() : "";
|
|
1449
|
+
if (!commandId) throw new TypeError("usage.commandId is required");
|
|
1450
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1451
|
+
await store.commitTx({
|
|
1452
|
+
at,
|
|
1453
|
+
ops: [{ kind: "X402_REVERSAL_COMMAND_PUT", tenantId, commandId, usage: { ...usage, tenantId, commandId } }],
|
|
1454
|
+
audit
|
|
1455
|
+
});
|
|
1456
|
+
return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId })) ?? null;
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
store.getX402ReversalCommandUsage = async function getX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, commandId } = {}) {
|
|
1460
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1461
|
+
if (typeof commandId !== "string" || commandId.trim() === "") throw new TypeError("commandId is required");
|
|
1462
|
+
return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId.trim() })) ?? null;
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
store.putX402Escalation = async function putX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalation, audit = null } = {}) {
|
|
1466
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1467
|
+
if (!escalation || typeof escalation !== "object" || Array.isArray(escalation)) throw new TypeError("escalation is required");
|
|
1468
|
+
const escalationId = typeof escalation.escalationId === "string" ? escalation.escalationId.trim() : "";
|
|
1469
|
+
if (!escalationId) throw new TypeError("escalation.escalationId is required");
|
|
1470
|
+
const at = escalation.updatedAt ?? escalation.createdAt ?? new Date().toISOString();
|
|
1471
|
+
await store.commitTx({
|
|
1472
|
+
at,
|
|
1473
|
+
ops: [{ kind: "X402_ESCALATION_UPSERT", tenantId, escalationId, escalation: { ...escalation, tenantId, escalationId } }],
|
|
1474
|
+
audit
|
|
1475
|
+
});
|
|
1476
|
+
return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId })) ?? null;
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
store.getX402Escalation = async function getX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalationId } = {}) {
|
|
1480
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1481
|
+
if (typeof escalationId !== "string" || escalationId.trim() === "") throw new TypeError("escalationId is required");
|
|
1482
|
+
return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId.trim() })) ?? null;
|
|
1483
|
+
};
|
|
1484
|
+
|
|
1485
|
+
store.listX402Escalations = async function listX402Escalations({
|
|
1486
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1487
|
+
gateId = null,
|
|
1488
|
+
agentId = null,
|
|
1489
|
+
status = null,
|
|
1490
|
+
limit = 200,
|
|
1491
|
+
offset = 0
|
|
1492
|
+
} = {}) {
|
|
1493
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1494
|
+
if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
|
|
1495
|
+
if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
|
|
1496
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
1497
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1498
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1499
|
+
|
|
1500
|
+
const normalizedGateId = gateId ? gateId.trim() : null;
|
|
1501
|
+
const normalizedAgentId = agentId ? agentId.trim() : null;
|
|
1502
|
+
const normalizedStatus = status ? status.trim().toLowerCase() : null;
|
|
1503
|
+
const out = [];
|
|
1504
|
+
for (const row of store.x402Escalations.values()) {
|
|
1505
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1506
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1507
|
+
if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
|
|
1508
|
+
if (
|
|
1509
|
+
normalizedAgentId &&
|
|
1510
|
+
String(
|
|
1511
|
+
row.requesterAgentId ??
|
|
1512
|
+
row.payerAgentId ??
|
|
1513
|
+
null
|
|
1514
|
+
).trim() !== normalizedAgentId
|
|
1515
|
+
) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (normalizedStatus && String(row.status ?? "").toLowerCase() !== normalizedStatus) continue;
|
|
1519
|
+
out.push(row);
|
|
1520
|
+
}
|
|
1521
|
+
out.sort((left, right) => {
|
|
1522
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1523
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1524
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1525
|
+
return String(left?.escalationId ?? "").localeCompare(String(right?.escalationId ?? ""));
|
|
1526
|
+
});
|
|
1527
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
store.appendX402EscalationEvent = async function appendX402EscalationEvent({ tenantId = DEFAULT_TENANT_ID, event, audit = null } = {}) {
|
|
1531
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1532
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
|
|
1533
|
+
const eventId = typeof event.eventId === "string" ? event.eventId.trim() : "";
|
|
1534
|
+
const escalationId = typeof event.escalationId === "string" ? event.escalationId.trim() : "";
|
|
1535
|
+
if (!eventId) throw new TypeError("event.eventId is required");
|
|
1536
|
+
if (!escalationId) throw new TypeError("event.escalationId is required");
|
|
1537
|
+
const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
|
|
1538
|
+
await store.commitTx({
|
|
1539
|
+
at,
|
|
1540
|
+
ops: [{ kind: "X402_ESCALATION_EVENT_APPEND", tenantId, eventId, escalationId, event: { ...event, tenantId, eventId, escalationId } }],
|
|
1541
|
+
audit
|
|
1542
|
+
});
|
|
1543
|
+
return store.x402EscalationEvents.get(makeScopedKey({ tenantId, id: eventId })) ?? null;
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
store.listX402EscalationEvents = async function listX402EscalationEvents({
|
|
1547
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1548
|
+
escalationId = null,
|
|
1549
|
+
limit = 200,
|
|
1550
|
+
offset = 0
|
|
1551
|
+
} = {}) {
|
|
1552
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1553
|
+
if (escalationId !== null && (typeof escalationId !== "string" || escalationId.trim() === "")) {
|
|
1554
|
+
throw new TypeError("escalationId must be null or a non-empty string");
|
|
1555
|
+
}
|
|
1556
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1557
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1558
|
+
const normalizedEscalationId = escalationId ? escalationId.trim() : null;
|
|
1559
|
+
const out = [];
|
|
1560
|
+
for (const row of store.x402EscalationEvents.values()) {
|
|
1561
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1562
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1563
|
+
if (normalizedEscalationId && String(row.escalationId ?? "") !== normalizedEscalationId) continue;
|
|
1564
|
+
out.push(row);
|
|
1565
|
+
}
|
|
1566
|
+
out.sort((left, right) => {
|
|
1567
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1568
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1569
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
1570
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1571
|
+
});
|
|
1572
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1573
|
+
};
|
|
1574
|
+
|
|
1575
|
+
store.putX402EscalationOverrideUsage = async function putX402EscalationOverrideUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
1576
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1577
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1578
|
+
const overrideId = typeof usage.overrideId === "string" ? usage.overrideId.trim() : "";
|
|
1579
|
+
if (!overrideId) throw new TypeError("usage.overrideId is required");
|
|
1580
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1581
|
+
await store.commitTx({
|
|
1582
|
+
at,
|
|
1583
|
+
ops: [{ kind: "X402_ESCALATION_OVERRIDE_USAGE_PUT", tenantId, overrideId, usage: { ...usage, tenantId, overrideId } }],
|
|
1584
|
+
audit
|
|
1585
|
+
});
|
|
1586
|
+
return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId })) ?? null;
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
store.getX402EscalationOverrideUsage = async function getX402EscalationOverrideUsage({
|
|
1590
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1591
|
+
overrideId
|
|
1592
|
+
} = {}) {
|
|
1593
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1594
|
+
if (typeof overrideId !== "string" || overrideId.trim() === "") throw new TypeError("overrideId is required");
|
|
1595
|
+
return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId.trim() })) ?? null;
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
function x402ReceiptStoreKey({ tenantId, receiptId }) {
|
|
1599
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1600
|
+
const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
|
|
1601
|
+
if (!normalizedReceiptId) throw new TypeError("receiptId is required");
|
|
1602
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedReceiptId });
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
function listX402ReversalEventsForReceiptSync({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
|
|
1606
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1607
|
+
const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
|
|
1608
|
+
if (!normalizedReceiptId) return [];
|
|
1609
|
+
const out = [];
|
|
1610
|
+
for (const row of store.x402ReversalEvents.values()) {
|
|
1611
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1612
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== normalizedTenantId) continue;
|
|
1613
|
+
if (String(row.receiptId ?? "") !== normalizedReceiptId) continue;
|
|
1614
|
+
out.push(row);
|
|
1615
|
+
}
|
|
1616
|
+
out.sort((left, right) => {
|
|
1617
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1618
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1619
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
1620
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1621
|
+
});
|
|
1622
|
+
return out;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function toX402ReceiptRecord({ tenantId, gate, settlement: settlementInput = null, includeReversalContext = true } = {}) {
|
|
1626
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) return null;
|
|
1627
|
+
const runId = typeof gate.runId === "string" && gate.runId.trim() !== "" ? gate.runId.trim() : null;
|
|
1628
|
+
const settlement =
|
|
1629
|
+
settlementInput && typeof settlementInput === "object" && !Array.isArray(settlementInput)
|
|
1630
|
+
? settlementInput
|
|
1631
|
+
: runId && store.agentRunSettlements instanceof Map
|
|
1632
|
+
? store.agentRunSettlements.get(makeScopedKey({ tenantId, id: runId })) ?? null
|
|
1633
|
+
: null;
|
|
1634
|
+
const decisionTrace =
|
|
1635
|
+
settlement?.decisionTrace && typeof settlement.decisionTrace === "object" && !Array.isArray(settlement.decisionTrace)
|
|
1636
|
+
? settlement.decisionTrace
|
|
1637
|
+
: gate.decisionTrace && typeof gate.decisionTrace === "object" && !Array.isArray(gate.decisionTrace)
|
|
1638
|
+
? gate.decisionTrace
|
|
1639
|
+
: null;
|
|
1640
|
+
const settlementReceipt =
|
|
1641
|
+
decisionTrace?.settlementReceipt && typeof decisionTrace.settlementReceipt === "object" && !Array.isArray(decisionTrace.settlementReceipt)
|
|
1642
|
+
? decisionTrace.settlementReceipt
|
|
1643
|
+
: null;
|
|
1644
|
+
if (!settlementReceipt) return null;
|
|
1645
|
+
const receiptId = typeof settlementReceipt.receiptId === "string" ? settlementReceipt.receiptId.trim() : "";
|
|
1646
|
+
if (!receiptId) return null;
|
|
1647
|
+
const bindings =
|
|
1648
|
+
decisionTrace?.bindings && typeof decisionTrace.bindings === "object" && !Array.isArray(decisionTrace.bindings)
|
|
1649
|
+
? decisionTrace.bindings
|
|
1650
|
+
: null;
|
|
1651
|
+
const verificationContext =
|
|
1652
|
+
gate?.verificationContext && typeof gate.verificationContext === "object" && !Array.isArray(gate.verificationContext)
|
|
1653
|
+
? gate.verificationContext
|
|
1654
|
+
: gate?.decision?.verificationContext &&
|
|
1655
|
+
typeof gate.decision.verificationContext === "object" &&
|
|
1656
|
+
!Array.isArray(gate.decision.verificationContext)
|
|
1657
|
+
? gate.decision.verificationContext
|
|
1658
|
+
: null;
|
|
1659
|
+
const sponsorRef =
|
|
1660
|
+
typeof bindings?.spendAuthorization?.sponsorRef === "string" && bindings.spendAuthorization.sponsorRef.trim() !== ""
|
|
1661
|
+
? bindings.spendAuthorization.sponsorRef.trim()
|
|
1662
|
+
: null;
|
|
1663
|
+
const sponsorWalletRef =
|
|
1664
|
+
typeof bindings?.spendAuthorization?.sponsorWalletRef === "string" && bindings.spendAuthorization.sponsorWalletRef.trim() !== ""
|
|
1665
|
+
? bindings.spendAuthorization.sponsorWalletRef.trim()
|
|
1666
|
+
: null;
|
|
1667
|
+
const agentKeyId =
|
|
1668
|
+
typeof bindings?.spendAuthorization?.agentKeyId === "string" && bindings.spendAuthorization.agentKeyId.trim() !== ""
|
|
1669
|
+
? bindings.spendAuthorization.agentKeyId.trim()
|
|
1670
|
+
: null;
|
|
1671
|
+
const settledAt =
|
|
1672
|
+
typeof settlementReceipt.settledAt === "string" && settlementReceipt.settledAt.trim() !== ""
|
|
1673
|
+
? settlementReceipt.settledAt.trim()
|
|
1674
|
+
: typeof gate.resolvedAt === "string" && gate.resolvedAt.trim() !== ""
|
|
1675
|
+
? gate.resolvedAt.trim()
|
|
1676
|
+
: null;
|
|
1677
|
+
const base = {
|
|
1678
|
+
schemaVersion: "X402ReceiptRecord.v1",
|
|
1679
|
+
tenantId,
|
|
1680
|
+
receiptId,
|
|
1681
|
+
gateId: typeof gate.gateId === "string" ? gate.gateId : null,
|
|
1682
|
+
runId,
|
|
1683
|
+
payerAgentId: typeof gate.payerAgentId === "string" ? gate.payerAgentId : null,
|
|
1684
|
+
providerId: typeof gate.payeeAgentId === "string" ? gate.payeeAgentId : null,
|
|
1685
|
+
toolId:
|
|
1686
|
+
typeof gate.toolId === "string" && gate.toolId.trim() !== ""
|
|
1687
|
+
? gate.toolId.trim()
|
|
1688
|
+
: typeof gate?.quote?.toolId === "string" && gate.quote.toolId.trim() !== ""
|
|
1689
|
+
? gate.quote.toolId.trim()
|
|
1690
|
+
: null,
|
|
1691
|
+
sponsorRef,
|
|
1692
|
+
sponsorWalletRef,
|
|
1693
|
+
agentKeyId,
|
|
1694
|
+
settlementState:
|
|
1695
|
+
typeof settlementReceipt.status === "string" && settlementReceipt.status.trim() !== ""
|
|
1696
|
+
? settlementReceipt.status.trim().toLowerCase()
|
|
1697
|
+
: typeof settlement?.status === "string" && settlement.status.trim() !== ""
|
|
1698
|
+
? settlement.status.trim().toLowerCase()
|
|
1699
|
+
: null,
|
|
1700
|
+
verificationStatus:
|
|
1701
|
+
typeof decisionTrace?.verificationStatus === "string" && decisionTrace.verificationStatus.trim() !== ""
|
|
1702
|
+
? decisionTrace.verificationStatus.trim().toLowerCase()
|
|
1703
|
+
: null,
|
|
1704
|
+
settledAt,
|
|
1705
|
+
createdAt:
|
|
1706
|
+
typeof settlementReceipt.createdAt === "string" && settlementReceipt.createdAt.trim() !== ""
|
|
1707
|
+
? settlementReceipt.createdAt.trim()
|
|
1708
|
+
: null,
|
|
1709
|
+
updatedAt:
|
|
1710
|
+
typeof gate.updatedAt === "string" && gate.updatedAt.trim() !== ""
|
|
1711
|
+
? gate.updatedAt.trim()
|
|
1712
|
+
: typeof settledAt === "string"
|
|
1713
|
+
? settledAt
|
|
1714
|
+
: null,
|
|
1715
|
+
evidenceRefs: Array.isArray(gate.evidenceRefs) ? gate.evidenceRefs.slice() : [],
|
|
1716
|
+
verificationContext,
|
|
1717
|
+
bindings,
|
|
1718
|
+
providerSignature:
|
|
1719
|
+
gate?.providerSignature && typeof gate.providerSignature === "object" && !Array.isArray(gate.providerSignature)
|
|
1720
|
+
? gate.providerSignature
|
|
1721
|
+
: null,
|
|
1722
|
+
providerQuoteSignature:
|
|
1723
|
+
gate?.providerQuoteSignature && typeof gate.providerQuoteSignature === "object" && !Array.isArray(gate.providerQuoteSignature)
|
|
1724
|
+
? gate.providerQuoteSignature
|
|
1725
|
+
: null,
|
|
1726
|
+
providerQuotePayload:
|
|
1727
|
+
gate?.providerQuotePayload && typeof gate.providerQuotePayload === "object" && !Array.isArray(gate.providerQuotePayload)
|
|
1728
|
+
? gate.providerQuotePayload
|
|
1729
|
+
: null,
|
|
1730
|
+
zkProof:
|
|
1731
|
+
gate?.zkProof && typeof gate.zkProof === "object" && !Array.isArray(gate.zkProof)
|
|
1732
|
+
? gate.zkProof
|
|
1733
|
+
: null,
|
|
1734
|
+
decisionRecord:
|
|
1735
|
+
decisionTrace?.decisionRecord && typeof decisionTrace.decisionRecord === "object" && !Array.isArray(decisionTrace.decisionRecord)
|
|
1736
|
+
? decisionTrace.decisionRecord
|
|
1737
|
+
: null,
|
|
1738
|
+
settlementReceipt
|
|
1739
|
+
};
|
|
1740
|
+
if (!includeReversalContext) return base;
|
|
1741
|
+
return {
|
|
1742
|
+
...base,
|
|
1743
|
+
reversal:
|
|
1744
|
+
gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
|
|
1745
|
+
? gate.reversal
|
|
1746
|
+
: null,
|
|
1747
|
+
reversalEvents: listX402ReversalEventsForReceiptSync({ tenantId, receiptId })
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
function normalizeX402ReceiptRecordForStorage({ receipt } = {}) {
|
|
1752
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
|
|
1753
|
+
const stableUpdatedAt =
|
|
1754
|
+
typeof receipt.createdAt === "string" && receipt.createdAt.trim() !== ""
|
|
1755
|
+
? receipt.createdAt
|
|
1756
|
+
: typeof receipt.settledAt === "string" && receipt.settledAt.trim() !== ""
|
|
1757
|
+
? receipt.settledAt
|
|
1758
|
+
: typeof receipt.updatedAt === "string" && receipt.updatedAt.trim() !== ""
|
|
1759
|
+
? receipt.updatedAt
|
|
1760
|
+
: new Date().toISOString();
|
|
1761
|
+
const normalized = {
|
|
1762
|
+
...receipt,
|
|
1763
|
+
reversal: null,
|
|
1764
|
+
reversalEvents: [],
|
|
1765
|
+
updatedAt: stableUpdatedAt
|
|
1766
|
+
};
|
|
1767
|
+
return normalized;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
function projectX402ReceiptRecord({ tenantId, receipt } = {}) {
|
|
1771
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
|
|
1772
|
+
const receiptId = typeof receipt.receiptId === "string" ? receipt.receiptId.trim() : "";
|
|
1773
|
+
if (!receiptId) return null;
|
|
1774
|
+
const gateId = typeof receipt.gateId === "string" ? receipt.gateId.trim() : "";
|
|
1775
|
+
const gate =
|
|
1776
|
+
gateId && store.x402Gates instanceof Map
|
|
1777
|
+
? store.x402Gates.get(makeScopedKey({ tenantId, id: gateId })) ?? null
|
|
1778
|
+
: null;
|
|
1779
|
+
const reversalEvents = listX402ReversalEventsForReceiptSync({ tenantId, receiptId });
|
|
1780
|
+
const latestEvent = reversalEvents.length > 0 ? reversalEvents[reversalEvents.length - 1] : null;
|
|
1781
|
+
const derivedState =
|
|
1782
|
+
typeof latestEvent?.settlementStatusAfter === "string" && latestEvent.settlementStatusAfter.trim() !== ""
|
|
1783
|
+
? latestEvent.settlementStatusAfter.trim().toLowerCase()
|
|
1784
|
+
: null;
|
|
1785
|
+
const updatedAtCandidates = [receipt.updatedAt, latestEvent?.occurredAt]
|
|
1786
|
+
.map((value) => (typeof value === "string" && value.trim() !== "" && Number.isFinite(Date.parse(value)) ? new Date(Date.parse(value)).toISOString() : null))
|
|
1787
|
+
.filter(Boolean);
|
|
1788
|
+
const updatedAt = updatedAtCandidates.length > 0 ? updatedAtCandidates.sort((a, b) => Date.parse(a) - Date.parse(b))[updatedAtCandidates.length - 1] : null;
|
|
1789
|
+
return {
|
|
1790
|
+
...receipt,
|
|
1791
|
+
settlementState: derivedState ?? receipt.settlementState ?? null,
|
|
1792
|
+
updatedAt: updatedAt ?? receipt.updatedAt ?? null,
|
|
1793
|
+
reversal:
|
|
1794
|
+
gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
|
|
1795
|
+
? gate.reversal
|
|
1796
|
+
: receipt?.reversal && typeof receipt.reversal === "object" && !Array.isArray(receipt.reversal)
|
|
1797
|
+
? receipt.reversal
|
|
1798
|
+
: null,
|
|
1799
|
+
reversalEvents
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
function compareX402ReceiptRecords(left, right) {
|
|
1804
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.settledAt ?? ""))) ? Date.parse(String(left.settledAt)) : Number.NaN;
|
|
1805
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.settledAt ?? ""))) ? Date.parse(String(right.settledAt)) : Number.NaN;
|
|
1806
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1807
|
+
return String(left?.receiptId ?? "").localeCompare(String(right?.receiptId ?? ""));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function encodeX402ReceiptCursor(record) {
|
|
1811
|
+
const settledAt = typeof record?.settledAt === "string" && record.settledAt.trim() !== "" ? new Date(Date.parse(record.settledAt)).toISOString() : null;
|
|
1812
|
+
const receiptId = typeof record?.receiptId === "string" && record.receiptId.trim() !== "" ? record.receiptId.trim() : null;
|
|
1813
|
+
if (!settledAt || !receiptId) return null;
|
|
1814
|
+
return Buffer.from(JSON.stringify({ settledAt, receiptId }), "utf8").toString("base64url");
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
function decodeX402ReceiptCursor(raw) {
|
|
1818
|
+
if (raw === null || raw === undefined || String(raw).trim() === "") return null;
|
|
1819
|
+
let parsed;
|
|
1820
|
+
try {
|
|
1821
|
+
parsed = JSON.parse(Buffer.from(String(raw).trim(), "base64url").toString("utf8"));
|
|
1822
|
+
} catch {
|
|
1823
|
+
throw new TypeError("cursor must be base64url-encoded JSON");
|
|
1824
|
+
}
|
|
1825
|
+
const settledAt = typeof parsed?.settledAt === "string" && Number.isFinite(Date.parse(parsed.settledAt)) ? new Date(Date.parse(parsed.settledAt)).toISOString() : null;
|
|
1826
|
+
const receiptId = typeof parsed?.receiptId === "string" && parsed.receiptId.trim() !== "" ? parsed.receiptId.trim() : null;
|
|
1827
|
+
if (!settledAt || !receiptId) throw new TypeError("cursor is invalid");
|
|
1828
|
+
return { settledAt, receiptId };
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
function isReceiptAfterCursor(record, cursor) {
|
|
1832
|
+
if (!cursor) return true;
|
|
1833
|
+
const recordMs = Number.isFinite(Date.parse(String(record?.settledAt ?? ""))) ? Date.parse(String(record.settledAt)) : Number.NaN;
|
|
1834
|
+
const cursorMs = Date.parse(cursor.settledAt);
|
|
1835
|
+
if (!Number.isFinite(recordMs)) return false;
|
|
1836
|
+
if (recordMs < cursorMs) return true;
|
|
1837
|
+
if (recordMs > cursorMs) return false;
|
|
1838
|
+
return String(record?.receiptId ?? "") > String(cursor.receiptId ?? "");
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
function listX402ReceiptCandidates({ tenantId }) {
|
|
1842
|
+
const byReceiptId = new Map();
|
|
1843
|
+
if (store.x402Receipts instanceof Map) {
|
|
1844
|
+
for (const row of store.x402Receipts.values()) {
|
|
1845
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1846
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1847
|
+
const receiptId = typeof row.receiptId === "string" ? row.receiptId.trim() : "";
|
|
1848
|
+
if (!receiptId) continue;
|
|
1849
|
+
byReceiptId.set(receiptId, projectX402ReceiptRecord({ tenantId, receipt: row }));
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
if (byReceiptId.size === 0 && store.x402Gates instanceof Map) {
|
|
1853
|
+
for (const gate of store.x402Gates.values()) {
|
|
1854
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
|
|
1855
|
+
if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1856
|
+
const derived = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
|
|
1857
|
+
if (!derived) continue;
|
|
1858
|
+
const receiptId = typeof derived.receiptId === "string" ? derived.receiptId.trim() : "";
|
|
1859
|
+
if (!receiptId) continue;
|
|
1860
|
+
byReceiptId.set(receiptId, derived);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return Array.from(byReceiptId.values()).filter(Boolean);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
store.deriveX402ReceiptRecord = function deriveX402ReceiptRecord({
|
|
1867
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1868
|
+
gate,
|
|
1869
|
+
settlement = null,
|
|
1870
|
+
includeReversalContext = false
|
|
1871
|
+
} = {}) {
|
|
1872
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1873
|
+
const derived = toX402ReceiptRecord({ tenantId, gate, settlement, includeReversalContext });
|
|
1874
|
+
return normalizeX402ReceiptRecordForStorage({ receipt: derived });
|
|
1875
|
+
};
|
|
1876
|
+
|
|
1877
|
+
store.putX402Receipt = async function putX402Receipt({ tenantId = DEFAULT_TENANT_ID, receipt, audit = null } = {}) {
|
|
1878
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1879
|
+
const normalized = normalizeX402ReceiptRecordForStorage({ receipt });
|
|
1880
|
+
if (!normalized) throw new TypeError("receipt is required");
|
|
1881
|
+
const receiptId = typeof normalized.receiptId === "string" ? normalized.receiptId.trim() : "";
|
|
1882
|
+
if (!receiptId) throw new TypeError("receipt.receiptId is required");
|
|
1883
|
+
const key = x402ReceiptStoreKey({ tenantId, receiptId });
|
|
1884
|
+
const at =
|
|
1885
|
+
typeof normalized.updatedAt === "string" && normalized.updatedAt.trim() !== ""
|
|
1886
|
+
? normalized.updatedAt
|
|
1887
|
+
: typeof normalized.createdAt === "string" && normalized.createdAt.trim() !== ""
|
|
1888
|
+
? normalized.createdAt
|
|
1889
|
+
: new Date().toISOString();
|
|
1890
|
+
await store.commitTx({
|
|
1891
|
+
at,
|
|
1892
|
+
ops: [{ kind: "X402_RECEIPT_PUT", tenantId, receiptId, receipt: { ...normalized, tenantId, receiptId } }],
|
|
1893
|
+
audit
|
|
1894
|
+
});
|
|
1895
|
+
return store.x402Receipts.get(key) ?? null;
|
|
1896
|
+
};
|
|
1897
|
+
|
|
1898
|
+
store.getX402Receipt = async function getX402Receipt({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
|
|
1899
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1900
|
+
if (typeof receiptId !== "string" || receiptId.trim() === "") throw new TypeError("receiptId is required");
|
|
1901
|
+
const wanted = receiptId.trim();
|
|
1902
|
+
const key = x402ReceiptStoreKey({ tenantId, receiptId: wanted });
|
|
1903
|
+
const stored = store.x402Receipts instanceof Map ? store.x402Receipts.get(key) ?? null : null;
|
|
1904
|
+
if (stored) return projectX402ReceiptRecord({ tenantId, receipt: stored });
|
|
1905
|
+
for (const gate of store.x402Gates.values()) {
|
|
1906
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
|
|
1907
|
+
if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1908
|
+
const receipt = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
|
|
1909
|
+
if (!receipt) continue;
|
|
1910
|
+
if (String(receipt.receiptId ?? "") === wanted) return receipt;
|
|
1911
|
+
}
|
|
1912
|
+
return null;
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
store.listX402ReceiptsPage = async function listX402ReceiptsPage({
|
|
1916
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1917
|
+
agentId = null,
|
|
1918
|
+
sponsorId = null,
|
|
1919
|
+
sponsorWalletRef = null,
|
|
1920
|
+
toolId = null,
|
|
1921
|
+
state = null,
|
|
1922
|
+
from = null,
|
|
1923
|
+
to = null,
|
|
1924
|
+
limit = 200,
|
|
1925
|
+
offset = 0,
|
|
1926
|
+
cursor = null
|
|
1927
|
+
} = {}) {
|
|
1928
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1929
|
+
if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
|
|
1930
|
+
if (sponsorId !== null && (typeof sponsorId !== "string" || sponsorId.trim() === "")) throw new TypeError("sponsorId must be null or a non-empty string");
|
|
1931
|
+
if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
|
|
1932
|
+
throw new TypeError("sponsorWalletRef must be null or a non-empty string");
|
|
1933
|
+
}
|
|
1934
|
+
if (toolId !== null && (typeof toolId !== "string" || toolId.trim() === "")) throw new TypeError("toolId must be null or a non-empty string");
|
|
1935
|
+
if (state !== null && (typeof state !== "string" || state.trim() === "")) throw new TypeError("state must be null or a non-empty string");
|
|
1936
|
+
if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
|
|
1937
|
+
if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
|
|
1938
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1939
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1940
|
+
|
|
1941
|
+
const normalizedAgentId = agentId ? agentId.trim() : null;
|
|
1942
|
+
const normalizedSponsorId = sponsorId ? sponsorId.trim() : null;
|
|
1943
|
+
const normalizedSponsorWalletRef = sponsorWalletRef ? sponsorWalletRef.trim() : null;
|
|
1944
|
+
const normalizedToolId = toolId ? toolId.trim() : null;
|
|
1945
|
+
const normalizedState = state ? state.trim().toLowerCase() : null;
|
|
1946
|
+
const fromMs = from ? Date.parse(String(from)) : null;
|
|
1947
|
+
const toMs = to ? Date.parse(String(to)) : null;
|
|
1948
|
+
const decodedCursor = cursor === null ? null : decodeX402ReceiptCursor(cursor);
|
|
1949
|
+
|
|
1950
|
+
const all = listX402ReceiptCandidates({ tenantId });
|
|
1951
|
+
const filtered = all.filter((receipt) => {
|
|
1952
|
+
if (!receipt) return false;
|
|
1953
|
+
if (
|
|
1954
|
+
normalizedAgentId &&
|
|
1955
|
+
String(receipt.payerAgentId ?? "") !== normalizedAgentId &&
|
|
1956
|
+
String(receipt.providerId ?? "") !== normalizedAgentId &&
|
|
1957
|
+
String(receipt.agentKeyId ?? "") !== normalizedAgentId
|
|
1958
|
+
) {
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
if (normalizedSponsorId && String(receipt.sponsorRef ?? "") !== normalizedSponsorId) return false;
|
|
1962
|
+
if (normalizedSponsorWalletRef && String(receipt.sponsorWalletRef ?? "") !== normalizedSponsorWalletRef) return false;
|
|
1963
|
+
if (normalizedToolId && String(receipt.toolId ?? "") !== normalizedToolId) return false;
|
|
1964
|
+
if (normalizedState && String(receipt.settlementState ?? "").toLowerCase() !== normalizedState) return false;
|
|
1965
|
+
const settledAtMs = Number.isFinite(Date.parse(String(receipt.settledAt ?? ""))) ? Date.parse(String(receipt.settledAt)) : Number.NaN;
|
|
1966
|
+
if (fromMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs < fromMs)) return false;
|
|
1967
|
+
if (toMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs > toMs)) return false;
|
|
1968
|
+
return true;
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
filtered.sort(compareX402ReceiptRecords);
|
|
1972
|
+
const cursorFiltered = decodedCursor ? filtered.filter((row) => isReceiptAfterCursor(row, decodedCursor)) : filtered;
|
|
1973
|
+
const paged = cursorFiltered.slice(offset, offset + Math.min(1000, limit));
|
|
1974
|
+
const hasMore = cursorFiltered.length > offset + paged.length;
|
|
1975
|
+
const nextCursor = hasMore && paged.length > 0 ? encodeX402ReceiptCursor(paged[paged.length - 1]) : null;
|
|
1976
|
+
return { receipts: paged, nextCursor };
|
|
1977
|
+
};
|
|
1978
|
+
|
|
1979
|
+
store.listX402Receipts = async function listX402Receipts(args = {}) {
|
|
1980
|
+
const page = await store.listX402ReceiptsPage(args);
|
|
1981
|
+
return page.receipts;
|
|
1982
|
+
};
|
|
1983
|
+
|
|
791
1984
|
store.listArbitrationCases = async function listArbitrationCases({
|
|
792
1985
|
tenantId = DEFAULT_TENANT_ID,
|
|
793
1986
|
runId = null,
|
|
@@ -1299,6 +2492,55 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
1299
2492
|
return all;
|
|
1300
2493
|
};
|
|
1301
2494
|
|
|
2495
|
+
store.listReputationEvents = async function listReputationEvents({
|
|
2496
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
2497
|
+
agentId,
|
|
2498
|
+
toolId = null,
|
|
2499
|
+
occurredAtGte = null,
|
|
2500
|
+
occurredAtLte = null,
|
|
2501
|
+
limit = 1000,
|
|
2502
|
+
offset = 0
|
|
2503
|
+
} = {}) {
|
|
2504
|
+
tenantId = normalizeTenantId(tenantId);
|
|
2505
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
2506
|
+
if (toolId !== null && toolId !== undefined && (typeof toolId !== "string" || toolId.trim() === "")) {
|
|
2507
|
+
throw new TypeError("toolId must be null or a non-empty string");
|
|
2508
|
+
}
|
|
2509
|
+
if (occurredAtGte !== null && occurredAtGte !== undefined && !Number.isFinite(Date.parse(String(occurredAtGte)))) {
|
|
2510
|
+
throw new TypeError("occurredAtGte must be an ISO date-time");
|
|
2511
|
+
}
|
|
2512
|
+
if (occurredAtLte !== null && occurredAtLte !== undefined && !Number.isFinite(Date.parse(String(occurredAtLte)))) {
|
|
2513
|
+
throw new TypeError("occurredAtLte must be an ISO date-time");
|
|
2514
|
+
}
|
|
2515
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
2516
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
2517
|
+
|
|
2518
|
+
const minMs = occurredAtGte ? Date.parse(String(occurredAtGte)) : Number.NaN;
|
|
2519
|
+
const maxMs = occurredAtLte ? Date.parse(String(occurredAtLte)) : Number.NaN;
|
|
2520
|
+
const out = [];
|
|
2521
|
+
for (const art of store.artifacts.values()) {
|
|
2522
|
+
if (!art || typeof art !== "object" || Array.isArray(art)) continue;
|
|
2523
|
+
if (normalizeTenantId(art.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
2524
|
+
if (String(art.schemaVersion ?? "") !== "ReputationEvent.v1") continue;
|
|
2525
|
+
const subject = art.subject && typeof art.subject === "object" && !Array.isArray(art.subject) ? art.subject : null;
|
|
2526
|
+
if (!subject) continue;
|
|
2527
|
+
if (String(subject.agentId ?? "") !== String(agentId)) continue;
|
|
2528
|
+
if (toolId !== null && toolId !== undefined && String(subject.toolId ?? "") !== String(toolId)) continue;
|
|
2529
|
+
const occurredAtMs = Date.parse(String(art.occurredAt ?? ""));
|
|
2530
|
+
if (!Number.isFinite(occurredAtMs)) continue;
|
|
2531
|
+
if (Number.isFinite(minMs) && occurredAtMs < minMs) continue;
|
|
2532
|
+
if (Number.isFinite(maxMs) && occurredAtMs > maxMs) continue;
|
|
2533
|
+
out.push(art);
|
|
2534
|
+
}
|
|
2535
|
+
out.sort((left, right) => {
|
|
2536
|
+
const leftMs = Date.parse(String(left?.occurredAt ?? ""));
|
|
2537
|
+
const rightMs = Date.parse(String(right?.occurredAt ?? ""));
|
|
2538
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
2539
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
2540
|
+
});
|
|
2541
|
+
return out.slice(offset, offset + Math.min(5000, limit));
|
|
2542
|
+
};
|
|
2543
|
+
|
|
1302
2544
|
store.getArtifact = async function getArtifact({ tenantId = DEFAULT_TENANT_ID, artifactId }) {
|
|
1303
2545
|
tenantId = normalizeTenantId(tenantId);
|
|
1304
2546
|
if (typeof artifactId !== "string" || artifactId.trim() === "") throw new TypeError("artifactId is required");
|