settld 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +71 -0
- package/conformance/kernel-v0/README.md +7 -0
- package/conformance/kernel-v0/run.mjs +292 -4
- package/docs/ACCESS.md +57 -0
- package/docs/ADOPTION_CHECKLIST.md +44 -0
- package/docs/ALERTS.md +198 -0
- package/docs/ARCHITECTURE.md +69 -0
- package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
- package/docs/ARTIFACTS.md +60 -0
- package/docs/CERTIFICATION_CHECKLIST.md +33 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
- package/docs/CONFIG.md +297 -0
- package/docs/CONTRACTS_APIS.md +23 -0
- package/docs/DEPRECATION.md +31 -0
- package/docs/DOMAIN_MODEL.md +92 -0
- package/docs/EVENT_ENVELOPE.md +53 -0
- package/docs/FINANCE_PACK_FORMAT.md +53 -0
- package/docs/INCIDENT_TAXONOMY.md +30 -0
- package/docs/JOB_STATE_MACHINE.md +66 -0
- package/docs/KERNEL_COMPATIBLE.md +60 -0
- package/docs/KERNEL_V0.md +40 -0
- package/docs/KEY_ROTATION.md +80 -0
- package/docs/LEDGER.md +82 -0
- package/docs/LIVENESS.md +76 -0
- package/docs/MVP_BUILD_ORDER.md +36 -0
- package/docs/ONCALL_PLAYBOOK.md +39 -0
- package/docs/OPERATIONS_SIGNING.md +20 -0
- package/docs/OVERVIEW.md +190 -0
- package/docs/PERF_BASELINE.md +85 -0
- package/docs/PRD.md +77 -0
- package/docs/QUICKSTART_KERNEL_V0.md +96 -0
- package/docs/QUICKSTART_MCP.md +377 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
- package/docs/QUICKSTART_SDK.md +125 -0
- package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
- package/docs/QUICKSTART_VERIFY.md +54 -0
- package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
- package/docs/README.md +33 -0
- package/docs/RELEASE_CHECKLIST.md +182 -0
- package/docs/RELEASING.md +82 -0
- package/docs/REPO_SETTINGS.md +37 -0
- package/docs/RUNBOOK.md +86 -0
- package/docs/SKILLS.md +42 -0
- package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
- package/docs/SLO.md +131 -0
- package/docs/SUMMARY.md +17 -0
- package/docs/SUPPORT.md +31 -0
- package/docs/THREAT_MODEL.md +36 -0
- package/docs/TRUST.md +59 -0
- package/docs/WORKFLOW.md +35 -0
- package/docs/X402_BATCH_SETTLEMENT.md +126 -0
- package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
- package/docs/examples/x402-provider-payout-registry.example.json +14 -0
- package/docs/gitbook/README.md +64 -0
- package/docs/gitbook/SETUP.md +25 -0
- package/docs/gitbook/SUMMARY.md +15 -0
- package/docs/gitbook/api-reference.md +73 -0
- package/docs/gitbook/closepacks.md +55 -0
- package/docs/gitbook/conformance.md +59 -0
- package/docs/gitbook/core-primitives.md +85 -0
- package/docs/gitbook/dispute-lifecycle.md +33 -0
- package/docs/gitbook/faq.md +21 -0
- package/docs/gitbook/guides.md +49 -0
- package/docs/gitbook/operations-runbook.md +36 -0
- package/docs/gitbook/quickstart.md +103 -0
- package/docs/gitbook/replay-and-audit.md +30 -0
- package/docs/gitbook/sdk-reference.md +35 -0
- package/docs/gitbook/security-model.md +58 -0
- package/docs/integrations/README.md +15 -0
- package/docs/integrations/github-actions-verify.yml +31 -0
- package/docs/integrations/github-actions.md +34 -0
- package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
- package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
- package/docs/kernel-compatible/capabilities.json +36 -0
- package/docs/marketing/agent-commerce-substrate.md +78 -0
- package/docs/marketing/hn-repost-2026-02-17.md +102 -0
- package/docs/marketing/show-hn-post.md +45 -0
- package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
- package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
- package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
- package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
- package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
- package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
- package/docs/ops/R1_SLOS.md +66 -0
- package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
- package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
- package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
- package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
- package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
- package/docs/ops/tool-call-disputes-holdback.md +52 -0
- package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
- package/docs/pilot-kit/README.md +29 -0
- package/docs/pilot-kit/architecture-one-pager.md +48 -0
- package/docs/pilot-kit/buyer-email.txt +19 -0
- package/docs/pilot-kit/buyer-one-pager.md +31 -0
- package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
- package/docs/pilot-kit/offline-verify.md +33 -0
- package/docs/pilot-kit/procurement-one-pager.md +50 -0
- package/docs/pilot-kit/rfp-clause.md +46 -0
- package/docs/pilot-kit/roi-calculator-template.csv +2 -0
- package/docs/pilot-kit/security-qa.md +153 -0
- package/docs/pilot-kit/security-summary.md +35 -0
- package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/AcceptanceCriteria.v1.md +17 -0
- package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
- package/docs/spec/AgentEvent.v1.md +47 -0
- package/docs/spec/AgentIdentity.v1.md +62 -0
- package/docs/spec/AgentPassport.v1.md +95 -0
- package/docs/spec/AgentReputation.v1.md +59 -0
- package/docs/spec/AgentReputation.v2.md +52 -0
- package/docs/spec/AgentRun.v1.md +47 -0
- package/docs/spec/AgentRunSettlement.v1.md +52 -0
- package/docs/spec/AgentWallet.v1.md +43 -0
- package/docs/spec/AgreementDelegation.v1.md +109 -0
- package/docs/spec/ArbitrationCase.v1.md +67 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/ArbitrationVerdict.v1.md +60 -0
- package/docs/spec/BundleHeadAttestation.v1.md +32 -0
- package/docs/spec/CANONICAL_JSON.md +31 -0
- package/docs/spec/CRYPTOGRAPHY.md +61 -0
- package/docs/spec/ClosePack.v1.md +49 -0
- package/docs/spec/ClosePackManifest.v1.md +24 -0
- package/docs/spec/DelegationGrant.v1.md +90 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
- package/docs/spec/ERRORS.md +76 -0
- package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
- package/docs/spec/EvidenceIndex.v1.md +20 -0
- package/docs/spec/ExecutionIntent.v1.md +90 -0
- package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
- package/docs/spec/FundingHold.v1.md +60 -0
- package/docs/spec/GovernancePolicy.v1.md +34 -0
- package/docs/spec/GovernancePolicy.v2.md +30 -0
- package/docs/spec/INVARIANTS.md +389 -0
- package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
- package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
- package/docs/spec/InvoiceClaim.v1.md +11 -0
- package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
- package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
- package/docs/spec/MarketplaceOffer.v2.md +54 -0
- package/docs/spec/MeteringReport.v1.md +18 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PRODUCER_ERRORS.md +42 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/PricingMatrix.v1.md +20 -0
- package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
- package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
- package/docs/spec/ProduceCliOutput.v1.md +46 -0
- package/docs/spec/ProofBundleManifest.v1.md +24 -0
- package/docs/spec/README.md +109 -0
- package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
- package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
- package/docs/spec/REMOTE_SIGNER.md +66 -0
- package/docs/spec/ReleaseIndex.v1.md +32 -0
- package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
- package/docs/spec/ReleaseTrust.v1.md +13 -0
- package/docs/spec/ReleaseTrust.v2.md +26 -0
- package/docs/spec/RemoteSignerRequest.v1.md +21 -0
- package/docs/spec/RemoteSignerResponse.v1.md +16 -0
- package/docs/spec/ReputationEvent.v1.md +63 -0
- package/docs/spec/RevocationList.v1.md +28 -0
- package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
- package/docs/spec/STRICTNESS.md +68 -0
- package/docs/spec/SUPPLY_CHAIN.md +33 -0
- package/docs/spec/SettlementAdjustment.v1.md +45 -0
- package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
- package/docs/spec/SettlementDecisionReport.v1.md +44 -0
- package/docs/spec/SettlementKernel.v1.md +59 -0
- package/docs/spec/SettlementReceipt.v1.md +63 -0
- package/docs/spec/SlaDefinition.v1.md +24 -0
- package/docs/spec/SlaEvaluation.v1.md +12 -0
- package/docs/spec/THREAT_MODEL.md +113 -0
- package/docs/spec/TOOL_PROVENANCE.md +30 -0
- package/docs/spec/TRUST_ANCHORS.md +84 -0
- package/docs/spec/TenantSettings.v1.md +90 -0
- package/docs/spec/TenantSettings.v2.md +99 -0
- package/docs/spec/TimestampProof.v1.md +25 -0
- package/docs/spec/ToolCallAgreement.v1.md +34 -0
- package/docs/spec/ToolCallEvidence.v1.md +47 -0
- package/docs/spec/ToolManifest.v1.md +47 -0
- package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
- package/docs/spec/VERSIONING.md +107 -0
- package/docs/spec/VerificationReport.v1.md +50 -0
- package/docs/spec/VerifyAboutOutput.v1.md +10 -0
- package/docs/spec/VerifyCliOutput.v1.md +28 -0
- package/docs/spec/WARNINGS.md +83 -0
- package/docs/spec/error-codes.v1.txt +285 -0
- package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
- package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
- package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
- package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
- package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
- package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
- package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
- package/docs/spec/examples/release_index_v1.example.json +15 -0
- package/docs/spec/examples/release_trust_v1.example.json +7 -0
- package/docs/spec/examples/release_trust_v2.example.json +22 -0
- package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
- package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
- package/docs/spec/examples/reputation_event_v1.example.json +29 -0
- package/docs/spec/examples/verification_report_v1.example.json +24 -0
- package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
- package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
- package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
- package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
- package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
- package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
- package/docs/spec/producer-error-codes.v1.txt +14 -0
- package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
- package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
- package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
- package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
- package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
- package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
- package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
- package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
- package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
- package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
- package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
- package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
- package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
- package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
- package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
- package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
- package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
- package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
- package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
- package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
- package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
- package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
- package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
- package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
- package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
- package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
- package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
- package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
- package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
- package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
- package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
- package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
- package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
- package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
- package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
- package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
- package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
- package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
- package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
- package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
- package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
- package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
- package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
- package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
- package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
- package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
- package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
- package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
- package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
- package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
- package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
- package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
- package/docs/spec/x402-error-codes.v1.txt +35 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +53 -6
- package/scripts/acceptance/full-stack.mjs +734 -0
- package/scripts/acceptance/full-stack.sh +99 -0
- package/scripts/audit/build-audit-packet.mjs +242 -0
- package/scripts/backup-pg.sh +45 -0
- package/scripts/backup-restore/README.md +18 -0
- package/scripts/backup-restore/capture-state.mjs +130 -0
- package/scripts/backup-restore/client.mjs +97 -0
- package/scripts/backup-restore/seed-workload.mjs +235 -0
- package/scripts/backup-restore/verify-state.mjs +139 -0
- package/scripts/backup-restore-test.sh +217 -0
- package/scripts/chaos.js +221 -0
- package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
- package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
- package/scripts/ci/changelog-guard.mjs +145 -0
- package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
- package/scripts/ci/check-secret-hygiene.mjs +78 -0
- package/scripts/ci/check-version-consistency.mjs +42 -0
- package/scripts/ci/cli-pack-smoke.mjs +160 -0
- package/scripts/ci/flake-budget-guard.mjs +68 -0
- package/scripts/ci/generate-error-codes.mjs +54 -0
- package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
- package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
- package/scripts/ci/npm-pack-smoke.mjs +454 -0
- package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
- package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
- package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
- package/scripts/ci/run-go-live-gate.mjs +150 -0
- package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
- package/scripts/ci/runtime-import-smoke.mjs +58 -0
- package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
- package/scripts/closepack/lib.mjs +286 -0
- package/scripts/collect-debug.sh +263 -0
- package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
- package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
- package/scripts/demo/delivery-robot/generate.mjs +377 -0
- package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
- package/scripts/demo/magic-link-first-green.mjs +118 -0
- package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
- package/scripts/demo/mcp-paid-exa.mjs +1110 -0
- package/scripts/dev/billing-doctor.sh +145 -0
- package/scripts/dev/billing-smoke-prod.sh +219 -0
- package/scripts/dev/billing-webhook-replay.sh +161 -0
- package/scripts/dev/env.dev.example +29 -0
- package/scripts/dev/env.sh +37 -0
- package/scripts/dev/new-sdk-key.sh +81 -0
- package/scripts/dev/sdk-first-run.sh +21 -0
- package/scripts/dev/smoke-x402-gateway.sh +115 -0
- package/scripts/dev/start-api.sh +24 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
- package/scripts/examples/sdk-first-paid-rfq.py +105 -0
- package/scripts/examples/sdk-first-verified-run.mjs +85 -0
- package/scripts/examples/sdk-first-verified-run.py +99 -0
- package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
- package/scripts/examples/sdk-tenant-analytics.py +118 -0
- package/scripts/finance-pack/bundle.mjs +284 -0
- package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
- package/scripts/governance/export.mjs +169 -0
- package/scripts/load/delivery-stress.k6.js +183 -0
- package/scripts/load/ingest-burst.k6.js +236 -0
- package/scripts/load/run-delivery-load.js +66 -0
- package/scripts/load/webhook-receiver.js +131 -0
- package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
- package/scripts/mcp/probe.mjs +238 -0
- package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
- package/scripts/mcp/settld-mcp-server.mjs +1511 -0
- package/scripts/openapi/write.mjs +13 -0
- package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
- package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
- package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
- package/scripts/ops/design-partner-run-packet.mjs +466 -0
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
- package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
- package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
- package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/pilot/finance-pack.mjs +495 -0
- package/scripts/pilot/fixtures/robot-keypair.json +4 -0
- package/scripts/pilot/fixtures/server-signer.json +4 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/proof-bundle/job.mjs +109 -0
- package/scripts/proof-bundle/lib.mjs +92 -0
- package/scripts/proof-bundle/month.mjs +103 -0
- package/scripts/provider/conformance-run.mjs +159 -0
- package/scripts/provider/keys-generate.mjs +135 -0
- package/scripts/provider/publish.mjs +420 -0
- package/scripts/quickstart/x402.mjs +334 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/release/build-artifacts.mjs +181 -0
- package/scripts/release/generate-release-index.mjs +112 -0
- package/scripts/release/release-index-lib.mjs +232 -0
- package/scripts/release/sign-release-index.mjs +85 -0
- package/scripts/release/validate-release-assets.mjs +170 -0
- package/scripts/release/verify-release.mjs +261 -0
- package/scripts/restore-pg.sh +34 -0
- package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
- package/scripts/sdk/smoke-python.py +30 -0
- package/scripts/sdk/smoke.mjs +16 -0
- package/scripts/settlement/x402-batch-worker.mjs +1091 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +239 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +59 -0
- package/scripts/trust/validate-trust-file.mjs +57 -0
- package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
- package/scripts/trust-config/wizard.mjs +161 -0
- package/scripts/vendor-contract-test-lib.mjs +182 -0
- package/scripts/vendor-contract-test.mjs +55 -0
- package/scripts/vercel/build-mkdocs.sh +9 -0
- package/scripts/vercel/ignore-mkdocs.sh +25 -0
- package/scripts/vercel/install-mkdocs.sh +6 -0
- package/scripts/verify-pg.js +217 -0
- package/scripts/x402/receipt-verify.mjs +289 -0
- package/services/finance-sink/src/dedupe-store.js +29 -6
- package/services/receiver/src/dedupe-store.js +29 -5
- package/services/x402-gateway/Dockerfile +13 -0
- package/services/x402-gateway/README.md +58 -0
- package/services/x402-gateway/examples/upstream-mock.js +337 -0
- package/services/x402-gateway/src/server.js +1058 -0
- package/src/api/app.js +34658 -16940
- package/src/api/maintenance.js +70 -0
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +1778 -70
- package/src/api/persistence.js +456 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1581 -62
- package/src/api/workers/deliveries.js +99 -4
- package/src/api/workers/insolvency-sweep.js +159 -0
- package/src/core/agent-card.js +69 -0
- package/src/core/agent-wallets.js +231 -0
- package/src/core/agreement-delegation.js +549 -0
- package/src/core/billing-plans.js +40 -6
- package/src/core/circle-reserve-adapter.js +845 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/maintenance-locks.js +1 -0
- package/src/core/operator-action.js +303 -0
- package/src/core/paid-tool-manifest.js +318 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/provider-publish-conformance.js +525 -0
- package/src/core/provider-publish-proof.js +396 -0
- package/src/core/provider-quote-signature.js +170 -0
- package/src/core/settld-keys.js +112 -0
- package/src/core/settld-pay-token.js +344 -0
- package/src/core/settlement-kernel.js +239 -2
- package/src/core/settlement-verifier.js +335 -0
- package/src/core/tool-call-agreement.js +112 -0
- package/src/core/tool-call-evidence.js +144 -0
- package/src/core/tool-provider-signature.js +98 -0
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/core/x402-escalation-override.js +258 -0
- package/src/core/x402-gate.js +118 -0
- package/src/core/x402-provider-refund-decision.js +220 -0
- package/src/core/x402-receipt-verifier.js +708 -0
- package/src/core/x402-reversal-command.js +251 -0
- package/src/core/x402-wallet-issuer-decision.js +252 -0
- package/src/core/zk-verifier.js +300 -0
- package/src/db/migrations/029_reputation_event_index.sql +54 -0
- package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
- package/src/db/pg.js +18 -7
- package/src/db/store-pg.js +1508 -111
package/src/api/store.js
CHANGED
|
@@ -18,23 +18,159 @@ 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
|
+
const EMERGENCY_SCOPE_TYPE = Object.freeze({
|
|
24
|
+
TENANT: "tenant",
|
|
25
|
+
AGENT: "agent",
|
|
26
|
+
ADAPTER: "adapter"
|
|
27
|
+
});
|
|
28
|
+
const EMERGENCY_SCOPE_TYPES = new Set(Object.values(EMERGENCY_SCOPE_TYPE));
|
|
29
|
+
const EMERGENCY_CONTROL_TYPE = Object.freeze({
|
|
30
|
+
PAUSE: "pause",
|
|
31
|
+
QUARANTINE: "quarantine",
|
|
32
|
+
REVOKE: "revoke",
|
|
33
|
+
KILL_SWITCH: "kill-switch"
|
|
34
|
+
});
|
|
35
|
+
const EMERGENCY_CONTROL_TYPES = Object.values(EMERGENCY_CONTROL_TYPE);
|
|
36
|
+
const EMERGENCY_CONTROL_TYPES_SET = new Set(EMERGENCY_CONTROL_TYPES);
|
|
37
|
+
const EMERGENCY_ACTION = Object.freeze({
|
|
38
|
+
PAUSE: "pause",
|
|
39
|
+
QUARANTINE: "quarantine",
|
|
40
|
+
REVOKE: "revoke",
|
|
41
|
+
KILL_SWITCH: "kill-switch",
|
|
42
|
+
RESUME: "resume"
|
|
43
|
+
});
|
|
44
|
+
const EMERGENCY_ACTIONS = new Set(Object.values(EMERGENCY_ACTION));
|
|
45
|
+
|
|
46
|
+
function readJsonFileSafe(filePath) {
|
|
47
|
+
if (!fs.existsSync(filePath)) return null;
|
|
48
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
49
|
+
return JSON.parse(raw);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeSettldPayPreviousRows(rows) {
|
|
53
|
+
const out = [];
|
|
54
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
for (const row of list) {
|
|
57
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
58
|
+
const publicKeyPem = typeof row.publicKeyPem === "string" ? row.publicKeyPem : "";
|
|
59
|
+
if (!publicKeyPem.trim()) continue;
|
|
60
|
+
const derivedKeyId = keyIdFromPublicKeyPem(publicKeyPem);
|
|
61
|
+
const keyId = typeof row.keyId === "string" && row.keyId.trim() !== "" ? row.keyId.trim() : derivedKeyId;
|
|
62
|
+
if (keyId !== derivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: previous[].keyId mismatch");
|
|
63
|
+
if (seen.has(keyId)) continue;
|
|
64
|
+
seen.add(keyId);
|
|
65
|
+
const rotatedAt = typeof row.rotatedAt === "string" && row.rotatedAt.trim() !== "" ? row.rotatedAt.trim() : null;
|
|
66
|
+
out.push({ keyId, publicKeyPem, rotatedAt });
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeSettldPayKeysetStore(payload) {
|
|
72
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
73
|
+
throw new Error("invalid settld-pay-keyset-store.json: object expected");
|
|
74
|
+
}
|
|
75
|
+
const active = payload.active;
|
|
76
|
+
if (!active || typeof active !== "object" || Array.isArray(active)) {
|
|
77
|
+
throw new Error("invalid settld-pay-keyset-store.json: active key missing");
|
|
78
|
+
}
|
|
79
|
+
const activePublicKeyPem = typeof active.publicKeyPem === "string" ? active.publicKeyPem : "";
|
|
80
|
+
const activePrivateKeyPem = typeof active.privateKeyPem === "string" ? active.privateKeyPem : "";
|
|
81
|
+
if (!activePublicKeyPem.trim() || !activePrivateKeyPem.trim()) {
|
|
82
|
+
throw new Error("invalid settld-pay-keyset-store.json: active keypair missing");
|
|
83
|
+
}
|
|
84
|
+
const activeDerivedKeyId = keyIdFromPublicKeyPem(activePublicKeyPem);
|
|
85
|
+
const activeKeyId = typeof active.keyId === "string" && active.keyId.trim() !== "" ? active.keyId.trim() : activeDerivedKeyId;
|
|
86
|
+
if (activeKeyId !== activeDerivedKeyId) throw new Error("invalid settld-pay-keyset-store.json: active.keyId mismatch");
|
|
87
|
+
|
|
88
|
+
const previous = normalizeSettldPayPreviousRows(payload.previous);
|
|
89
|
+
return {
|
|
90
|
+
active: {
|
|
91
|
+
keyId: activeKeyId,
|
|
92
|
+
publicKeyPem: activePublicKeyPem,
|
|
93
|
+
privateKeyPem: activePrivateKeyPem
|
|
94
|
+
},
|
|
95
|
+
previous
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
21
99
|
function loadOrCreateServerSigner({ persistenceDir }) {
|
|
22
100
|
if (!persistenceDir) {
|
|
23
101
|
const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
|
|
24
|
-
return { publicKeyPem, privateKeyPem };
|
|
102
|
+
return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-ephemeral" };
|
|
25
103
|
}
|
|
26
104
|
|
|
27
105
|
fs.mkdirSync(persistenceDir, { recursive: true });
|
|
28
|
-
const
|
|
106
|
+
const keysetStorePath = path.join(persistenceDir, SETTLD_PAY_KEYSET_STORE_FILENAME);
|
|
107
|
+
const signerPath = path.join(persistenceDir, SERVER_SIGNER_FILENAME);
|
|
108
|
+
if (fs.existsSync(keysetStorePath)) {
|
|
109
|
+
const parsed = readJsonFileSafe(keysetStorePath);
|
|
110
|
+
const normalized = normalizeSettldPayKeysetStore(parsed);
|
|
111
|
+
// Keep legacy signer file in sync for compatibility with existing tooling.
|
|
112
|
+
fs.writeFileSync(
|
|
113
|
+
signerPath,
|
|
114
|
+
JSON.stringify({ publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem }, null, 2),
|
|
115
|
+
"utf8"
|
|
116
|
+
);
|
|
117
|
+
return {
|
|
118
|
+
active: { publicKeyPem: normalized.active.publicKeyPem, privateKeyPem: normalized.active.privateKeyPem },
|
|
119
|
+
previous: normalized.previous,
|
|
120
|
+
source: "keyset-store"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
29
124
|
if (fs.existsSync(signerPath)) {
|
|
30
125
|
const parsed = JSON.parse(fs.readFileSync(signerPath, "utf8"));
|
|
31
126
|
if (!parsed?.publicKeyPem || !parsed?.privateKeyPem) throw new Error("invalid server-signer.json");
|
|
32
|
-
return {
|
|
127
|
+
return {
|
|
128
|
+
active: { publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem },
|
|
129
|
+
previous: [],
|
|
130
|
+
source: "legacy-signer"
|
|
131
|
+
};
|
|
33
132
|
}
|
|
34
133
|
|
|
35
134
|
const { publicKeyPem, privateKeyPem } = createEd25519Keypair();
|
|
36
135
|
fs.writeFileSync(signerPath, JSON.stringify({ publicKeyPem, privateKeyPem }, null, 2), "utf8");
|
|
37
|
-
return { publicKeyPem, privateKeyPem };
|
|
136
|
+
return { active: { publicKeyPem, privateKeyPem }, previous: [], source: "generated-persistent" };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeEmergencyScopeType(value) {
|
|
140
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
141
|
+
if (!EMERGENCY_SCOPE_TYPES.has(normalized)) throw new TypeError("scope.type must be tenant|agent|adapter");
|
|
142
|
+
return normalized;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function normalizeEmergencyScopeId(scopeType, value) {
|
|
146
|
+
if (scopeType === EMERGENCY_SCOPE_TYPE.TENANT) return null;
|
|
147
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
148
|
+
if (!normalized) throw new TypeError("scope.id is required for scope.type agent|adapter");
|
|
149
|
+
return normalized;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeEmergencyControlType(value, { allowNull = false } = {}) {
|
|
153
|
+
if (allowNull && (value === null || value === undefined || String(value).trim() === "")) return null;
|
|
154
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
155
|
+
if (!EMERGENCY_CONTROL_TYPES_SET.has(normalized)) throw new TypeError("controlType must be pause|quarantine|revoke|kill-switch");
|
|
156
|
+
return normalized;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function normalizeEmergencyAction(value) {
|
|
160
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
161
|
+
if (!EMERGENCY_ACTIONS.has(normalized)) throw new TypeError("action must be pause|quarantine|revoke|kill-switch|resume");
|
|
162
|
+
return normalized;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function normalizeEmergencyResumeControlTypes(value) {
|
|
166
|
+
const values = Array.isArray(value) ? value : value === null || value === undefined ? EMERGENCY_CONTROL_TYPES : [value];
|
|
167
|
+
const dedupe = new Set();
|
|
168
|
+
for (const item of values) {
|
|
169
|
+
dedupe.add(normalizeEmergencyControlType(item, { allowNull: false }));
|
|
170
|
+
}
|
|
171
|
+
const out = Array.from(dedupe.values());
|
|
172
|
+
out.sort((left, right) => left.localeCompare(right));
|
|
173
|
+
return out;
|
|
38
174
|
}
|
|
39
175
|
|
|
40
176
|
export function createStore({ persistenceDir = null, serverSignerKeypair = null } = {}) {
|
|
@@ -49,8 +185,18 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
49
185
|
throw new Error("invalid serverSignerKeypair");
|
|
50
186
|
}
|
|
51
187
|
|
|
52
|
-
const
|
|
53
|
-
resolvedServerSignerKeypair
|
|
188
|
+
const loadedServerSigner =
|
|
189
|
+
resolvedServerSignerKeypair === null
|
|
190
|
+
? loadOrCreateServerSigner({ persistenceDir })
|
|
191
|
+
: {
|
|
192
|
+
active: {
|
|
193
|
+
publicKeyPem: resolvedServerSignerKeypair.publicKeyPem,
|
|
194
|
+
privateKeyPem: resolvedServerSignerKeypair.privateKeyPem
|
|
195
|
+
},
|
|
196
|
+
previous: [],
|
|
197
|
+
source: "explicit-override"
|
|
198
|
+
};
|
|
199
|
+
const { publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem } = loadedServerSigner.active;
|
|
54
200
|
const serverKeyId = keyIdFromPublicKeyPem(serverPublicKeyPem);
|
|
55
201
|
|
|
56
202
|
function parseEvidenceRetentionMaxDaysByTenant() {
|
|
@@ -217,11 +363,27 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
217
363
|
operators: new Map(), // `${tenantId}\n${operatorId}` -> snapshot
|
|
218
364
|
operatorEvents: new Map(), // `${tenantId}\n${operatorId}` -> chained events
|
|
219
365
|
agentIdentities: new Map(), // `${tenantId}\n${agentId}` -> AgentIdentity.v1 record
|
|
366
|
+
agentPassports: new Map(), // `${tenantId}\n${agentId}` -> AgentPassport.v1 record
|
|
220
367
|
agentWallets: new Map(), // `${tenantId}\n${agentId}` -> AgentWallet.v1 record
|
|
221
368
|
agentRuns: new Map(), // `${tenantId}\n${runId}` -> AgentRun.v1 snapshot
|
|
222
369
|
agentRunEvents: new Map(), // `${tenantId}\n${runId}` -> AgentEvent.v1[]
|
|
223
370
|
agentRunSettlements: new Map(), // `${tenantId}\n${runId}` -> AgentRunSettlement.v1
|
|
224
371
|
arbitrationCases: new Map(), // `${tenantId}\n${caseId}` -> ArbitrationCase.v1 snapshot
|
|
372
|
+
agreementDelegations: new Map(), // `${tenantId}\n${delegationId}` -> AgreementDelegation.v1
|
|
373
|
+
x402Gates: new Map(), // `${tenantId}\n${gateId}` -> X402 gate record (internal API surface)
|
|
374
|
+
x402Receipts: new Map(), // `${tenantId}\n${receiptId}` -> immutable X402ReceiptRecord.v1 base record
|
|
375
|
+
x402WalletPolicies: new Map(), // `${tenantId}\n${sponsorWalletRef}::${policyRef}::${policyVersion}` -> X402WalletPolicy.v1
|
|
376
|
+
x402ZkVerificationKeys: new Map(), // `${tenantId}\n${verificationKeyId}` -> X402ZkVerificationKey.v1
|
|
377
|
+
x402ReversalEvents: new Map(), // `${tenantId}\n${eventId}` -> X402GateReversalEvent.v1
|
|
378
|
+
x402ReversalNonceUsage: new Map(), // `${tenantId}\n${sponsorRef}\n${nonce}` -> X402ReversalNonceUsage.v1
|
|
379
|
+
x402ReversalCommandUsage: new Map(), // `${tenantId}\n${commandId}` -> X402ReversalCommandUsage.v1
|
|
380
|
+
x402Escalations: new Map(), // `${tenantId}\n${escalationId}` -> X402AuthorizationEscalation.v1
|
|
381
|
+
x402EscalationEvents: new Map(), // `${tenantId}\n${eventId}` -> X402AuthorizationEscalationEvent.v1
|
|
382
|
+
x402EscalationOverrideUsage: new Map(), // `${tenantId}\n${overrideId}` -> X402EscalationOverrideUsage.v1
|
|
383
|
+
x402AgentLifecycles: new Map(), // `${tenantId}\n${agentId}` -> X402AgentLifecycle.v1
|
|
384
|
+
x402WebhookEndpoints: new Map(), // `${tenantId}\n${endpointId}` -> X402WebhookEndpoint.v1
|
|
385
|
+
emergencyControlEvents: new Map(), // `${tenantId}\n${eventId}` -> OpsEmergencyControlEvent.v1
|
|
386
|
+
emergencyControlState: new Map(), // `${tenantId}\n${scopeType}::${scopeId}::${controlType}` -> OpsEmergencyControlState.v1
|
|
225
387
|
toolCallHolds: new Map(), // `${tenantId}\n${holdHash}` -> FundingHold.v1 snapshot
|
|
226
388
|
settlementAdjustments: new Map(), // `${tenantId}\n${adjustmentId}` -> SettlementAdjustment.v1 snapshot
|
|
227
389
|
moneyRailOperations: new Map(), // `${tenantId}\n${providerId}\n${operationId}` -> MoneyRailOperation.v1
|
|
@@ -230,12 +392,14 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
230
392
|
financeReconciliationTriages: new Map(), // `${tenantId}\n${triageKey}` -> FinanceReconciliationTriage.v1
|
|
231
393
|
marketplaceRfqs: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceRfq.v1
|
|
232
394
|
marketplaceRfqBids: new Map(), // `${tenantId}\n${rfqId}` -> MarketplaceBid.v1[]
|
|
395
|
+
marketplaceProviderPublications: new Map(), // `${tenantId}\n${providerRef}` -> MarketplaceProviderPublication.v1
|
|
233
396
|
tenantSettlementPolicies: new Map(), // `${tenantId}\n${policyId}\n${policyVersion}` -> TenantSettlementPolicy.v1
|
|
234
397
|
tenantSettlementPolicyRollouts: new Map(), // `${tenantId}\nrollout` -> TenantSettlementPolicyRollout.v1
|
|
235
398
|
contracts: new Map(), // `${tenantId}\n${contractId}` -> contract
|
|
236
399
|
idempotency: new Map(),
|
|
237
400
|
publicKeyByKeyId,
|
|
238
401
|
serverSigner: { keyId: serverKeyId, publicKeyPem: serverPublicKeyPem, privateKeyPem: serverPrivateKeyPem },
|
|
402
|
+
settldPayFallbackKeys: loadedServerSigner.previous.map((row) => ({ keyId: row.keyId, publicKeyPem: row.publicKeyPem })),
|
|
239
403
|
ledgerByTenant,
|
|
240
404
|
// Back-compat: keep store.ledger and store.config as the default tenant's objects.
|
|
241
405
|
ledger: ledgerByTenant.get(DEFAULT_TENANT_ID),
|
|
@@ -254,6 +418,7 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
254
418
|
monthCloseCursor: 0,
|
|
255
419
|
artifactCursor: 0,
|
|
256
420
|
deliveryCursor: 0,
|
|
421
|
+
x402WinddownReversalCursor: 0,
|
|
257
422
|
persistence: persistenceDir ? createFileTxLog({ dir: persistenceDir }) : null
|
|
258
423
|
};
|
|
259
424
|
|
|
@@ -692,100 +857,1223 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
692
857
|
updatedAt: nowAt
|
|
693
858
|
};
|
|
694
859
|
|
|
695
|
-
const ops = [
|
|
696
|
-
{ kind: "AGENT_IDENTITY_UPSERT", tenantId, agentIdentity: record },
|
|
697
|
-
{ kind: "PUBLIC_KEY_PUT", keyId: String(keyId), publicKeyPem: String(publicKeyPem) }
|
|
698
|
-
];
|
|
699
|
-
await store.commitTx({ at: nowAt, ops, audit });
|
|
700
|
-
return store.getAgentIdentity({ tenantId, agentId: String(agentId) });
|
|
860
|
+
const ops = [
|
|
861
|
+
{ kind: "AGENT_IDENTITY_UPSERT", tenantId, agentIdentity: record },
|
|
862
|
+
{ kind: "PUBLIC_KEY_PUT", keyId: String(keyId), publicKeyPem: String(publicKeyPem) }
|
|
863
|
+
];
|
|
864
|
+
await store.commitTx({ at: nowAt, ops, audit });
|
|
865
|
+
return store.getAgentIdentity({ tenantId, agentId: String(agentId) });
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
store.getAgentIdentity = async function getAgentIdentity({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
|
|
869
|
+
tenantId = normalizeTenantId(tenantId);
|
|
870
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
871
|
+
const key = makeScopedKey({ tenantId, id: String(agentId) });
|
|
872
|
+
return store.agentIdentities.get(key) ?? null;
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
store.listAgentIdentities = async function listAgentIdentities({ tenantId = DEFAULT_TENANT_ID, status = null, limit = 200, offset = 0 } = {}) {
|
|
876
|
+
tenantId = normalizeTenantId(tenantId);
|
|
877
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
878
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
879
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
880
|
+
const statusFilter = status ? String(status).trim().toLowerCase() : null;
|
|
881
|
+
const safeLimit = Math.min(1000, limit);
|
|
882
|
+
const safeOffset = offset;
|
|
883
|
+
const out = [];
|
|
884
|
+
for (const row of store.agentIdentities.values()) {
|
|
885
|
+
if (!row || typeof row !== "object") continue;
|
|
886
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
887
|
+
if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
888
|
+
out.push(row);
|
|
889
|
+
}
|
|
890
|
+
out.sort((left, right) => String(left.agentId ?? "").localeCompare(String(right.agentId ?? "")));
|
|
891
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
store.putAgentPassport = async function putAgentPassport({ tenantId = DEFAULT_TENANT_ID, agentPassport, audit = null } = {}) {
|
|
895
|
+
tenantId = normalizeTenantId(tenantId);
|
|
896
|
+
if (!agentPassport || typeof agentPassport !== "object" || Array.isArray(agentPassport)) {
|
|
897
|
+
throw new TypeError("agentPassport is required");
|
|
898
|
+
}
|
|
899
|
+
const agentId = typeof agentPassport.agentId === "string" ? agentPassport.agentId.trim() : "";
|
|
900
|
+
if (!agentId) throw new TypeError("agentPassport.agentId is required");
|
|
901
|
+
const status = typeof agentPassport.status === "string" ? agentPassport.status.trim().toLowerCase() : "";
|
|
902
|
+
if (status !== "active" && status !== "suspended" && status !== "revoked") {
|
|
903
|
+
throw new TypeError("agentPassport.status must be active|suspended|revoked");
|
|
904
|
+
}
|
|
905
|
+
const at = agentPassport.updatedAt ?? agentPassport.createdAt ?? new Date().toISOString();
|
|
906
|
+
await store.commitTx({
|
|
907
|
+
at,
|
|
908
|
+
ops: [
|
|
909
|
+
{
|
|
910
|
+
kind: "AGENT_PASSPORT_UPSERT",
|
|
911
|
+
tenantId,
|
|
912
|
+
agentId,
|
|
913
|
+
agentPassport: { ...agentPassport, tenantId, agentId, status }
|
|
914
|
+
}
|
|
915
|
+
],
|
|
916
|
+
audit
|
|
917
|
+
});
|
|
918
|
+
return store.agentPassports.get(makeScopedKey({ tenantId, id: agentId })) ?? null;
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
store.getAgentPassport = async function getAgentPassport({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
|
|
922
|
+
tenantId = normalizeTenantId(tenantId);
|
|
923
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
924
|
+
return store.agentPassports.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
store.getAgentWallet = async function getAgentWallet({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
|
|
928
|
+
tenantId = normalizeTenantId(tenantId);
|
|
929
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
930
|
+
return store.agentWallets.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
store.putAgentWallet = async function putAgentWallet({ tenantId = DEFAULT_TENANT_ID, wallet } = {}) {
|
|
934
|
+
tenantId = normalizeTenantId(tenantId);
|
|
935
|
+
if (!wallet || typeof wallet !== "object" || Array.isArray(wallet)) throw new TypeError("wallet is required");
|
|
936
|
+
const agentId = wallet.agentId ?? null;
|
|
937
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("wallet.agentId is required");
|
|
938
|
+
const key = makeScopedKey({ tenantId, id: String(agentId) });
|
|
939
|
+
await store.commitTx({ at: wallet.updatedAt ?? new Date().toISOString(), ops: [{ kind: "AGENT_WALLET_UPSERT", tenantId, wallet: { ...wallet, tenantId, agentId } }] });
|
|
940
|
+
return store.agentWallets.get(key) ?? null;
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
store.getAgentRun = async function getAgentRun({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
|
|
944
|
+
tenantId = normalizeTenantId(tenantId);
|
|
945
|
+
if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
|
|
946
|
+
return store.agentRuns.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
store.listAgentRuns = async function listAgentRuns({ tenantId = DEFAULT_TENANT_ID, agentId = null, status = null, limit = 200, offset = 0 } = {}) {
|
|
950
|
+
tenantId = normalizeTenantId(tenantId);
|
|
951
|
+
if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
|
|
952
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
953
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
954
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
955
|
+
|
|
956
|
+
const statusFilter = status ? String(status).trim().toLowerCase() : null;
|
|
957
|
+
const safeLimit = Math.min(1000, limit);
|
|
958
|
+
const safeOffset = offset;
|
|
959
|
+
const out = [];
|
|
960
|
+
for (const row of store.agentRuns.values()) {
|
|
961
|
+
if (!row || typeof row !== "object") continue;
|
|
962
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
963
|
+
if (agentId !== null && String(row.agentId ?? "") !== String(agentId)) continue;
|
|
964
|
+
if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
965
|
+
out.push(row);
|
|
966
|
+
}
|
|
967
|
+
out.sort((left, right) => String(left.runId ?? "").localeCompare(String(right.runId ?? "")));
|
|
968
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
store.getAgentRunEvents = async function getAgentRunEvents({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
|
|
972
|
+
tenantId = normalizeTenantId(tenantId);
|
|
973
|
+
if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
|
|
974
|
+
return store.agentRunEvents.get(makeScopedKey({ tenantId, id: String(runId) })) ?? [];
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
store.getAgentRunSettlement = async function getAgentRunSettlement({ tenantId = DEFAULT_TENANT_ID, runId } = {}) {
|
|
978
|
+
tenantId = normalizeTenantId(tenantId);
|
|
979
|
+
if (typeof runId !== "string" || runId.trim() === "") throw new TypeError("runId is required");
|
|
980
|
+
return store.agentRunSettlements.get(makeScopedKey({ tenantId, id: String(runId) })) ?? null;
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
store.sumWalletPolicySpendCentsForDay = async function sumWalletPolicySpendCentsForDay({
|
|
984
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
985
|
+
agentId,
|
|
986
|
+
dayStartIso,
|
|
987
|
+
dayEndIso
|
|
988
|
+
} = {}) {
|
|
989
|
+
tenantId = normalizeTenantId(tenantId);
|
|
990
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
991
|
+
if (typeof dayStartIso !== "string" || dayStartIso.trim() === "" || !Number.isFinite(Date.parse(dayStartIso))) {
|
|
992
|
+
throw new TypeError("dayStartIso must be an ISO date string");
|
|
993
|
+
}
|
|
994
|
+
if (typeof dayEndIso !== "string" || dayEndIso.trim() === "" || !Number.isFinite(Date.parse(dayEndIso))) {
|
|
995
|
+
throw new TypeError("dayEndIso must be an ISO date string");
|
|
996
|
+
}
|
|
997
|
+
const startMs = Date.parse(dayStartIso);
|
|
998
|
+
const endMs = Date.parse(dayEndIso);
|
|
999
|
+
if (!(endMs > startMs)) throw new TypeError("dayEndIso must be after dayStartIso");
|
|
1000
|
+
|
|
1001
|
+
let total = 0;
|
|
1002
|
+
|
|
1003
|
+
// Agent run settlements lock escrow for the full settlement amount.
|
|
1004
|
+
for (const row of store.agentRunSettlements.values()) {
|
|
1005
|
+
if (!row || typeof row !== "object") continue;
|
|
1006
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1007
|
+
if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
|
|
1008
|
+
const lockedAt = row.lockedAt ?? null;
|
|
1009
|
+
const lockedMs = typeof lockedAt === "string" ? Date.parse(lockedAt) : NaN;
|
|
1010
|
+
if (!Number.isFinite(lockedMs) || lockedMs < startMs || lockedMs >= endMs) continue;
|
|
1011
|
+
const amountCents = Number(row.amountCents ?? 0);
|
|
1012
|
+
if (!Number.isSafeInteger(amountCents) || amountCents <= 0) continue;
|
|
1013
|
+
total += amountCents;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Tool-call holds lock escrow for the holdback amount only.
|
|
1017
|
+
for (const row of store.toolCallHolds.values()) {
|
|
1018
|
+
if (!row || typeof row !== "object") continue;
|
|
1019
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1020
|
+
if (String(row.payerAgentId ?? "") !== String(agentId)) continue;
|
|
1021
|
+
const createdAt = row.createdAt ?? null;
|
|
1022
|
+
const createdMs = typeof createdAt === "string" ? Date.parse(createdAt) : NaN;
|
|
1023
|
+
if (!Number.isFinite(createdMs) || createdMs < startMs || createdMs >= endMs) continue;
|
|
1024
|
+
const heldAmountCents = Number(row.heldAmountCents ?? 0);
|
|
1025
|
+
if (!Number.isSafeInteger(heldAmountCents) || heldAmountCents <= 0) continue;
|
|
1026
|
+
total += heldAmountCents;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
return total;
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
store.getArbitrationCase = async function getArbitrationCase({ tenantId = DEFAULT_TENANT_ID, caseId } = {}) {
|
|
1033
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1034
|
+
if (typeof caseId !== "string" || caseId.trim() === "") throw new TypeError("caseId is required");
|
|
1035
|
+
return store.arbitrationCases.get(makeScopedKey({ tenantId, id: String(caseId) })) ?? null;
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
store.getAgreementDelegation = async function getAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegationId } = {}) {
|
|
1039
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1040
|
+
if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegationId is required");
|
|
1041
|
+
return store.agreementDelegations.get(makeScopedKey({ tenantId, id: String(delegationId) })) ?? null;
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
store.putAgreementDelegation = async function putAgreementDelegation({ tenantId = DEFAULT_TENANT_ID, delegation, audit = null } = {}) {
|
|
1045
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1046
|
+
if (!delegation || typeof delegation !== "object" || Array.isArray(delegation)) throw new TypeError("delegation is required");
|
|
1047
|
+
const delegationId = delegation.delegationId ?? null;
|
|
1048
|
+
if (typeof delegationId !== "string" || delegationId.trim() === "") throw new TypeError("delegation.delegationId is required");
|
|
1049
|
+
const key = makeScopedKey({ tenantId, id: String(delegationId) });
|
|
1050
|
+
const at = delegation.updatedAt ?? new Date().toISOString();
|
|
1051
|
+
await store.commitTx({ at, ops: [{ kind: "AGREEMENT_DELEGATION_UPSERT", tenantId, delegationId, delegation: { ...delegation, tenantId, delegationId } }], audit });
|
|
1052
|
+
return store.agreementDelegations.get(key) ?? null;
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
store.listAgreementDelegations = async function listAgreementDelegations({
|
|
1056
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1057
|
+
parentAgreementHash = null,
|
|
1058
|
+
childAgreementHash = null,
|
|
1059
|
+
status = null,
|
|
1060
|
+
limit = 200,
|
|
1061
|
+
offset = 0
|
|
1062
|
+
} = {}) {
|
|
1063
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1064
|
+
if (parentAgreementHash !== null && (typeof parentAgreementHash !== "string" || parentAgreementHash.trim() === "")) {
|
|
1065
|
+
throw new TypeError("parentAgreementHash must be null or a non-empty string");
|
|
1066
|
+
}
|
|
1067
|
+
if (childAgreementHash !== null && (typeof childAgreementHash !== "string" || childAgreementHash.trim() === "")) {
|
|
1068
|
+
throw new TypeError("childAgreementHash must be null or a non-empty string");
|
|
1069
|
+
}
|
|
1070
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
1071
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1072
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1073
|
+
|
|
1074
|
+
const statusFilter = status ? String(status).trim().toLowerCase() : null;
|
|
1075
|
+
const parentFilter = parentAgreementHash ? String(parentAgreementHash).trim().toLowerCase() : null;
|
|
1076
|
+
const childFilter = childAgreementHash ? String(childAgreementHash).trim().toLowerCase() : null;
|
|
1077
|
+
const safeLimit = Math.min(1000, limit);
|
|
1078
|
+
const safeOffset = offset;
|
|
1079
|
+
const out = [];
|
|
1080
|
+
for (const row of store.agreementDelegations.values()) {
|
|
1081
|
+
if (!row || typeof row !== "object") continue;
|
|
1082
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1083
|
+
if (parentFilter && String(row.parentAgreementHash ?? "").toLowerCase() !== parentFilter) continue;
|
|
1084
|
+
if (childFilter && String(row.childAgreementHash ?? "").toLowerCase() !== childFilter) continue;
|
|
1085
|
+
if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1086
|
+
out.push(row);
|
|
1087
|
+
}
|
|
1088
|
+
out.sort((left, right) => String(left.delegationId ?? "").localeCompare(String(right.delegationId ?? "")));
|
|
1089
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
store.getX402Gate = async function getX402Gate({ tenantId = DEFAULT_TENANT_ID, gateId } = {}) {
|
|
1093
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1094
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
|
|
1095
|
+
return store.x402Gates.get(makeScopedKey({ tenantId, id: String(gateId) })) ?? null;
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
store.putX402Gate = async function putX402Gate({ tenantId = DEFAULT_TENANT_ID, gate, audit = null } = {}) {
|
|
1099
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1100
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) throw new TypeError("gate is required");
|
|
1101
|
+
const gateId = gate.gateId ?? gate.id ?? null;
|
|
1102
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gate.gateId is required");
|
|
1103
|
+
const key = makeScopedKey({ tenantId, id: String(gateId) });
|
|
1104
|
+
const at = gate.updatedAt ?? gate.createdAt ?? new Date().toISOString();
|
|
1105
|
+
await store.commitTx({ at, ops: [{ kind: "X402_GATE_UPSERT", tenantId, gateId, gate: { ...gate, tenantId, gateId: String(gateId) } }], audit });
|
|
1106
|
+
return store.x402Gates.get(key) ?? null;
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
store.putX402AgentLifecycle = async function putX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentLifecycle, audit = null } = {}) {
|
|
1110
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1111
|
+
if (!agentLifecycle || typeof agentLifecycle !== "object" || Array.isArray(agentLifecycle)) {
|
|
1112
|
+
throw new TypeError("agentLifecycle is required");
|
|
1113
|
+
}
|
|
1114
|
+
const agentId = typeof agentLifecycle.agentId === "string" ? agentLifecycle.agentId.trim() : "";
|
|
1115
|
+
if (!agentId) throw new TypeError("agentLifecycle.agentId is required");
|
|
1116
|
+
const status = typeof agentLifecycle.status === "string" ? agentLifecycle.status.trim().toLowerCase() : "";
|
|
1117
|
+
if (status !== "active" && status !== "frozen" && status !== "archived") {
|
|
1118
|
+
throw new TypeError("agentLifecycle.status must be active|frozen|archived");
|
|
1119
|
+
}
|
|
1120
|
+
const at = agentLifecycle.updatedAt ?? agentLifecycle.createdAt ?? new Date().toISOString();
|
|
1121
|
+
await store.commitTx({
|
|
1122
|
+
at,
|
|
1123
|
+
ops: [
|
|
1124
|
+
{
|
|
1125
|
+
kind: "X402_AGENT_LIFECYCLE_UPSERT",
|
|
1126
|
+
tenantId,
|
|
1127
|
+
agentId,
|
|
1128
|
+
agentLifecycle: { ...agentLifecycle, tenantId, agentId, status }
|
|
1129
|
+
}
|
|
1130
|
+
],
|
|
1131
|
+
audit
|
|
1132
|
+
});
|
|
1133
|
+
return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: agentId })) ?? null;
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
store.getX402AgentLifecycle = async function getX402AgentLifecycle({ tenantId = DEFAULT_TENANT_ID, agentId } = {}) {
|
|
1137
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1138
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
1139
|
+
return store.x402AgentLifecycles.get(makeScopedKey({ tenantId, id: String(agentId) })) ?? null;
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
function x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion }) {
|
|
1143
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1144
|
+
const normalizedSponsorWalletRef = typeof sponsorWalletRef === "string" ? sponsorWalletRef.trim() : "";
|
|
1145
|
+
const normalizedPolicyRef = typeof policyRef === "string" ? policyRef.trim() : "";
|
|
1146
|
+
const normalizedPolicyVersion = Number(policyVersion);
|
|
1147
|
+
if (!normalizedSponsorWalletRef) throw new TypeError("sponsorWalletRef is required");
|
|
1148
|
+
if (!normalizedPolicyRef) throw new TypeError("policyRef is required");
|
|
1149
|
+
if (!Number.isSafeInteger(normalizedPolicyVersion) || normalizedPolicyVersion <= 0) {
|
|
1150
|
+
throw new TypeError("policyVersion must be a positive safe integer");
|
|
1151
|
+
}
|
|
1152
|
+
return makeScopedKey({
|
|
1153
|
+
tenantId: normalizedTenantId,
|
|
1154
|
+
id: `${normalizedSponsorWalletRef}::${normalizedPolicyRef}::${normalizedPolicyVersion}`
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function x402WebhookEndpointStoreKey({ tenantId, endpointId }) {
|
|
1159
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1160
|
+
const normalizedEndpointId = typeof endpointId === "string" ? endpointId.trim() : "";
|
|
1161
|
+
if (!normalizedEndpointId) throw new TypeError("endpointId is required");
|
|
1162
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedEndpointId });
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId }) {
|
|
1166
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1167
|
+
const normalizedVerificationKeyId = typeof verificationKeyId === "string" ? verificationKeyId.trim() : "";
|
|
1168
|
+
if (!normalizedVerificationKeyId) throw new TypeError("verificationKeyId is required");
|
|
1169
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedVerificationKeyId });
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
store.putX402WalletPolicy = async function putX402WalletPolicy({ tenantId = DEFAULT_TENANT_ID, policy, audit = null } = {}) {
|
|
1173
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1174
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy)) throw new TypeError("policy is required");
|
|
1175
|
+
const sponsorWalletRef = typeof policy.sponsorWalletRef === "string" ? policy.sponsorWalletRef.trim() : "";
|
|
1176
|
+
const policyRef = typeof policy.policyRef === "string" ? policy.policyRef.trim() : "";
|
|
1177
|
+
const policyVersion = Number(policy.policyVersion);
|
|
1178
|
+
if (!sponsorWalletRef) throw new TypeError("policy.sponsorWalletRef is required");
|
|
1179
|
+
if (!policyRef) throw new TypeError("policy.policyRef is required");
|
|
1180
|
+
if (!Number.isSafeInteger(policyVersion) || policyVersion <= 0) throw new TypeError("policy.policyVersion must be a positive safe integer");
|
|
1181
|
+
const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
|
|
1182
|
+
const at = policy.updatedAt ?? policy.createdAt ?? new Date().toISOString();
|
|
1183
|
+
await store.commitTx({
|
|
1184
|
+
at,
|
|
1185
|
+
ops: [
|
|
1186
|
+
{
|
|
1187
|
+
kind: "X402_WALLET_POLICY_UPSERT",
|
|
1188
|
+
tenantId,
|
|
1189
|
+
policy: { ...policy, tenantId, sponsorWalletRef, policyRef, policyVersion }
|
|
1190
|
+
}
|
|
1191
|
+
],
|
|
1192
|
+
audit
|
|
1193
|
+
});
|
|
1194
|
+
return store.x402WalletPolicies.get(key) ?? null;
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
store.getX402WalletPolicy = async function getX402WalletPolicy({
|
|
1198
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1199
|
+
sponsorWalletRef,
|
|
1200
|
+
policyRef,
|
|
1201
|
+
policyVersion
|
|
1202
|
+
} = {}) {
|
|
1203
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1204
|
+
const key = x402WalletPolicyStoreKey({ tenantId, sponsorWalletRef, policyRef, policyVersion });
|
|
1205
|
+
return store.x402WalletPolicies.get(key) ?? null;
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
store.listX402WalletPolicies = async function listX402WalletPolicies({
|
|
1209
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1210
|
+
sponsorWalletRef = null,
|
|
1211
|
+
sponsorRef = null,
|
|
1212
|
+
policyRef = null,
|
|
1213
|
+
status = null,
|
|
1214
|
+
limit = 200,
|
|
1215
|
+
offset = 0
|
|
1216
|
+
} = {}) {
|
|
1217
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1218
|
+
if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
|
|
1219
|
+
throw new TypeError("sponsorWalletRef must be null or a non-empty string");
|
|
1220
|
+
}
|
|
1221
|
+
if (sponsorRef !== null && (typeof sponsorRef !== "string" || sponsorRef.trim() === "")) {
|
|
1222
|
+
throw new TypeError("sponsorRef must be null or a non-empty string");
|
|
1223
|
+
}
|
|
1224
|
+
if (policyRef !== null && (typeof policyRef !== "string" || policyRef.trim() === "")) {
|
|
1225
|
+
throw new TypeError("policyRef must be null or a non-empty string");
|
|
1226
|
+
}
|
|
1227
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) {
|
|
1228
|
+
throw new TypeError("status must be null or a non-empty string");
|
|
1229
|
+
}
|
|
1230
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1231
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1232
|
+
|
|
1233
|
+
const sponsorWalletFilter = sponsorWalletRef ? sponsorWalletRef.trim() : null;
|
|
1234
|
+
const sponsorFilter = sponsorRef ? sponsorRef.trim() : null;
|
|
1235
|
+
const policyFilter = policyRef ? policyRef.trim() : null;
|
|
1236
|
+
const statusFilter = status ? status.trim().toLowerCase() : null;
|
|
1237
|
+
const out = [];
|
|
1238
|
+
for (const row of store.x402WalletPolicies.values()) {
|
|
1239
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1240
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1241
|
+
if (sponsorWalletFilter && String(row.sponsorWalletRef ?? "") !== sponsorWalletFilter) continue;
|
|
1242
|
+
if (sponsorFilter && String(row.sponsorRef ?? "") !== sponsorFilter) continue;
|
|
1243
|
+
if (policyFilter && String(row.policyRef ?? "") !== policyFilter) continue;
|
|
1244
|
+
if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1245
|
+
out.push(row);
|
|
1246
|
+
}
|
|
1247
|
+
out.sort((left, right) => {
|
|
1248
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1249
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1250
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1251
|
+
const sponsorOrder = String(left?.sponsorWalletRef ?? "").localeCompare(String(right?.sponsorWalletRef ?? ""));
|
|
1252
|
+
if (sponsorOrder !== 0) return sponsorOrder;
|
|
1253
|
+
const policyOrder = String(left?.policyRef ?? "").localeCompare(String(right?.policyRef ?? ""));
|
|
1254
|
+
if (policyOrder !== 0) return policyOrder;
|
|
1255
|
+
return Number(right?.policyVersion ?? 0) - Number(left?.policyVersion ?? 0);
|
|
1256
|
+
});
|
|
1257
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
store.putX402ZkVerificationKey = async function putX402ZkVerificationKey({
|
|
1261
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1262
|
+
verificationKey,
|
|
1263
|
+
audit = null
|
|
1264
|
+
} = {}) {
|
|
1265
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1266
|
+
if (!verificationKey || typeof verificationKey !== "object" || Array.isArray(verificationKey)) {
|
|
1267
|
+
throw new TypeError("verificationKey is required");
|
|
1268
|
+
}
|
|
1269
|
+
const verificationKeyId = typeof verificationKey.verificationKeyId === "string" ? verificationKey.verificationKeyId.trim() : "";
|
|
1270
|
+
if (!verificationKeyId) throw new TypeError("verificationKey.verificationKeyId is required");
|
|
1271
|
+
const key = x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId });
|
|
1272
|
+
const at = verificationKey.updatedAt ?? verificationKey.createdAt ?? new Date().toISOString();
|
|
1273
|
+
await store.commitTx({
|
|
1274
|
+
at,
|
|
1275
|
+
ops: [
|
|
1276
|
+
{
|
|
1277
|
+
kind: "X402_ZK_VERIFICATION_KEY_PUT",
|
|
1278
|
+
tenantId,
|
|
1279
|
+
verificationKeyId,
|
|
1280
|
+
verificationKey: { ...verificationKey, tenantId, verificationKeyId }
|
|
1281
|
+
}
|
|
1282
|
+
],
|
|
1283
|
+
audit
|
|
1284
|
+
});
|
|
1285
|
+
return store.x402ZkVerificationKeys.get(key) ?? null;
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
store.getX402ZkVerificationKey = async function getX402ZkVerificationKey({
|
|
1289
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1290
|
+
verificationKeyId
|
|
1291
|
+
} = {}) {
|
|
1292
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1293
|
+
return store.x402ZkVerificationKeys.get(x402ZkVerificationKeyStoreKey({ tenantId, verificationKeyId })) ?? null;
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
store.listX402ZkVerificationKeys = async function listX402ZkVerificationKeys({
|
|
1297
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1298
|
+
protocol = null,
|
|
1299
|
+
providerRef = null,
|
|
1300
|
+
limit = 200,
|
|
1301
|
+
offset = 0
|
|
1302
|
+
} = {}) {
|
|
1303
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1304
|
+
if (protocol !== null && (typeof protocol !== "string" || protocol.trim() === "")) {
|
|
1305
|
+
throw new TypeError("protocol must be null or a non-empty string");
|
|
1306
|
+
}
|
|
1307
|
+
if (providerRef !== null && (typeof providerRef !== "string" || providerRef.trim() === "")) {
|
|
1308
|
+
throw new TypeError("providerRef must be null or a non-empty string");
|
|
1309
|
+
}
|
|
1310
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1311
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1312
|
+
const protocolFilter = protocol ? protocol.trim().toLowerCase() : null;
|
|
1313
|
+
const providerRefFilter = providerRef ? providerRef.trim() : null;
|
|
1314
|
+
const out = [];
|
|
1315
|
+
for (const row of store.x402ZkVerificationKeys.values()) {
|
|
1316
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1317
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1318
|
+
if (protocolFilter && String(row.protocol ?? "").trim().toLowerCase() !== protocolFilter) continue;
|
|
1319
|
+
if (providerRefFilter && String(row.providerRef ?? "") !== providerRefFilter) continue;
|
|
1320
|
+
out.push(row);
|
|
1321
|
+
}
|
|
1322
|
+
out.sort((left, right) => {
|
|
1323
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.createdAt ?? ""))) ? Date.parse(String(left.createdAt)) : Number.NaN;
|
|
1324
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.createdAt ?? ""))) ? Date.parse(String(right.createdAt)) : Number.NaN;
|
|
1325
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1326
|
+
return String(left?.verificationKeyId ?? "").localeCompare(String(right?.verificationKeyId ?? ""));
|
|
1327
|
+
});
|
|
1328
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
store.putX402WebhookEndpoint = async function putX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpoint, audit = null } = {}) {
|
|
1332
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1333
|
+
if (!endpoint || typeof endpoint !== "object" || Array.isArray(endpoint)) throw new TypeError("endpoint is required");
|
|
1334
|
+
const endpointId = typeof endpoint.endpointId === "string" ? endpoint.endpointId.trim() : "";
|
|
1335
|
+
if (!endpointId) throw new TypeError("endpoint.endpointId is required");
|
|
1336
|
+
const at = endpoint.updatedAt ?? endpoint.createdAt ?? new Date().toISOString();
|
|
1337
|
+
await store.commitTx({
|
|
1338
|
+
at,
|
|
1339
|
+
ops: [{ kind: "X402_WEBHOOK_ENDPOINT_UPSERT", tenantId, endpointId, endpoint: { ...endpoint, tenantId, endpointId } }],
|
|
1340
|
+
audit
|
|
1341
|
+
});
|
|
1342
|
+
return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
store.getX402WebhookEndpoint = async function getX402WebhookEndpoint({ tenantId = DEFAULT_TENANT_ID, endpointId } = {}) {
|
|
1346
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1347
|
+
return store.x402WebhookEndpoints.get(x402WebhookEndpointStoreKey({ tenantId, endpointId })) ?? null;
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
store.listX402WebhookEndpoints = async function listX402WebhookEndpoints({
|
|
1351
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1352
|
+
endpointId = null,
|
|
1353
|
+
destinationId = null,
|
|
1354
|
+
status = null,
|
|
1355
|
+
event = null,
|
|
1356
|
+
limit = 200,
|
|
1357
|
+
offset = 0
|
|
1358
|
+
} = {}) {
|
|
1359
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1360
|
+
if (endpointId !== null && (typeof endpointId !== "string" || endpointId.trim() === "")) {
|
|
1361
|
+
throw new TypeError("endpointId must be null or a non-empty string");
|
|
1362
|
+
}
|
|
1363
|
+
if (destinationId !== null && (typeof destinationId !== "string" || destinationId.trim() === "")) {
|
|
1364
|
+
throw new TypeError("destinationId must be null or a non-empty string");
|
|
1365
|
+
}
|
|
1366
|
+
if (status !== null && (typeof status !== "string" || status.trim() === "")) {
|
|
1367
|
+
throw new TypeError("status must be null or a non-empty string");
|
|
1368
|
+
}
|
|
1369
|
+
if (event !== null && (typeof event !== "string" || event.trim() === "")) {
|
|
1370
|
+
throw new TypeError("event must be null or a non-empty string");
|
|
1371
|
+
}
|
|
1372
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1373
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1374
|
+
|
|
1375
|
+
const endpointFilter = endpointId ? endpointId.trim() : null;
|
|
1376
|
+
const destinationFilter = destinationId ? destinationId.trim() : null;
|
|
1377
|
+
const statusFilter = status ? status.trim().toLowerCase() : null;
|
|
1378
|
+
const eventFilter = event ? event.trim().toLowerCase() : null;
|
|
1379
|
+
const out = [];
|
|
1380
|
+
for (const row of store.x402WebhookEndpoints.values()) {
|
|
1381
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1382
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1383
|
+
if (endpointFilter && String(row.endpointId ?? "") !== endpointFilter) continue;
|
|
1384
|
+
if (destinationFilter && String(row.destinationId ?? "") !== destinationFilter) continue;
|
|
1385
|
+
if (statusFilter && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1386
|
+
if (eventFilter) {
|
|
1387
|
+
const events = Array.isArray(row.events) ? row.events.map((value) => String(value).trim().toLowerCase()) : [];
|
|
1388
|
+
if (!events.includes(eventFilter)) continue;
|
|
1389
|
+
}
|
|
1390
|
+
out.push(row);
|
|
1391
|
+
}
|
|
1392
|
+
out.sort((left, right) => {
|
|
1393
|
+
const leftAt = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1394
|
+
const rightAt = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1395
|
+
if (Number.isFinite(leftAt) && Number.isFinite(rightAt) && rightAt !== leftAt) return rightAt - leftAt;
|
|
1396
|
+
return String(left?.endpointId ?? "").localeCompare(String(right?.endpointId ?? ""));
|
|
1397
|
+
});
|
|
1398
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
store.recordX402WebhookEndpointDeliveryResult = async function recordX402WebhookEndpointDeliveryResult({
|
|
1402
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1403
|
+
destinationId,
|
|
1404
|
+
deliveredAt = new Date().toISOString(),
|
|
1405
|
+
success,
|
|
1406
|
+
failureReason = null,
|
|
1407
|
+
statusCode = null,
|
|
1408
|
+
autoDisableThreshold = null,
|
|
1409
|
+
audit = null
|
|
1410
|
+
} = {}) {
|
|
1411
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1412
|
+
if (typeof destinationId !== "string" || destinationId.trim() === "") throw new TypeError("destinationId is required");
|
|
1413
|
+
const matched = await store.listX402WebhookEndpoints({ tenantId, destinationId: destinationId.trim(), limit: 1, offset: 0 });
|
|
1414
|
+
const endpoint = Array.isArray(matched) && matched.length > 0 ? matched[0] : null;
|
|
1415
|
+
if (!endpoint) return null;
|
|
1416
|
+
if (String(endpoint.status ?? "").toLowerCase() === "revoked") return endpoint;
|
|
1417
|
+
const thresholdRaw = autoDisableThreshold ?? store.x402WebhookAutoDisableFailures ?? 10;
|
|
1418
|
+
const threshold = Number.isSafeInteger(Number(thresholdRaw)) && Number(thresholdRaw) > 0 ? Number(thresholdRaw) : 10;
|
|
1419
|
+
const next = {
|
|
1420
|
+
...endpoint,
|
|
1421
|
+
updatedAt: deliveredAt,
|
|
1422
|
+
lastDeliveryAt: deliveredAt
|
|
1423
|
+
};
|
|
1424
|
+
if (success) {
|
|
1425
|
+
next.consecutiveFailures = 0;
|
|
1426
|
+
next.lastFailureReason = null;
|
|
1427
|
+
next.lastFailureAt = null;
|
|
1428
|
+
next.lastFailureStatusCode = null;
|
|
1429
|
+
} else {
|
|
1430
|
+
const prevFailures = Number.isSafeInteger(Number(endpoint.consecutiveFailures)) ? Number(endpoint.consecutiveFailures) : 0;
|
|
1431
|
+
next.consecutiveFailures = prevFailures + 1;
|
|
1432
|
+
next.lastFailureReason = failureReason ? String(failureReason) : "delivery_failed";
|
|
1433
|
+
next.lastFailureAt = deliveredAt;
|
|
1434
|
+
next.lastFailureStatusCode = Number.isSafeInteger(Number(statusCode)) ? Number(statusCode) : null;
|
|
1435
|
+
if (String(endpoint.status ?? "").toLowerCase() === "active" && next.consecutiveFailures >= threshold) {
|
|
1436
|
+
next.status = "disabled";
|
|
1437
|
+
next.disabledAt = deliveredAt;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return store.putX402WebhookEndpoint({ tenantId, endpoint: next, audit });
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
function x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce }) {
|
|
1444
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1445
|
+
const normalizedSponsorRef = typeof sponsorRef === "string" ? sponsorRef.trim() : "";
|
|
1446
|
+
const normalizedNonce = typeof nonce === "string" ? nonce.trim() : "";
|
|
1447
|
+
if (!normalizedSponsorRef) throw new TypeError("sponsorRef is required");
|
|
1448
|
+
if (!normalizedNonce) throw new TypeError("nonce is required");
|
|
1449
|
+
return `${normalizedTenantId}\n${normalizedSponsorRef}\n${normalizedNonce}`;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
store.putX402ReversalEvent = async function putX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, gateId, event, audit = null } = {}) {
|
|
1453
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1454
|
+
if (typeof gateId !== "string" || gateId.trim() === "") throw new TypeError("gateId is required");
|
|
1455
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
|
|
1456
|
+
const eventId = event.eventId ?? event.id ?? null;
|
|
1457
|
+
if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("event.eventId is required");
|
|
1458
|
+
const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
|
|
1459
|
+
await store.commitTx({
|
|
1460
|
+
at,
|
|
1461
|
+
ops: [{ kind: "X402_REVERSAL_EVENT_APPEND", tenantId, gateId: String(gateId), eventId: String(eventId), event: { ...event, tenantId, gateId: String(gateId), eventId: String(eventId) } }],
|
|
1462
|
+
audit
|
|
1463
|
+
});
|
|
1464
|
+
return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
|
|
1465
|
+
};
|
|
1466
|
+
|
|
1467
|
+
store.getX402ReversalEvent = async function getX402ReversalEvent({ tenantId = DEFAULT_TENANT_ID, eventId } = {}) {
|
|
1468
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1469
|
+
if (typeof eventId !== "string" || eventId.trim() === "") throw new TypeError("eventId is required");
|
|
1470
|
+
return store.x402ReversalEvents.get(makeScopedKey({ tenantId, id: String(eventId) })) ?? null;
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
store.listX402ReversalEvents = async function listX402ReversalEvents({
|
|
1474
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1475
|
+
gateId = null,
|
|
1476
|
+
receiptId = null,
|
|
1477
|
+
action = null,
|
|
1478
|
+
from = null,
|
|
1479
|
+
to = null,
|
|
1480
|
+
limit = 200,
|
|
1481
|
+
offset = 0
|
|
1482
|
+
} = {}) {
|
|
1483
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1484
|
+
if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
|
|
1485
|
+
if (receiptId !== null && (typeof receiptId !== "string" || receiptId.trim() === "")) throw new TypeError("receiptId must be null or a non-empty string");
|
|
1486
|
+
if (action !== null && (typeof action !== "string" || action.trim() === "")) throw new TypeError("action must be null or a non-empty string");
|
|
1487
|
+
if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
|
|
1488
|
+
if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
|
|
1489
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1490
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1491
|
+
|
|
1492
|
+
const normalizedGateId = gateId ? gateId.trim() : null;
|
|
1493
|
+
const normalizedReceiptId = receiptId ? receiptId.trim() : null;
|
|
1494
|
+
const normalizedAction = action ? action.trim().toLowerCase() : null;
|
|
1495
|
+
const fromMs = from ? Date.parse(String(from)) : null;
|
|
1496
|
+
const toMs = to ? Date.parse(String(to)) : null;
|
|
1497
|
+
|
|
1498
|
+
const out = [];
|
|
1499
|
+
for (const row of store.x402ReversalEvents.values()) {
|
|
1500
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1501
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1502
|
+
if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
|
|
1503
|
+
if (normalizedReceiptId && String(row.receiptId ?? "") !== normalizedReceiptId) continue;
|
|
1504
|
+
if (normalizedAction && String(row.action ?? "").toLowerCase() !== normalizedAction) continue;
|
|
1505
|
+
const occurredAtMs = Number.isFinite(Date.parse(String(row.occurredAt ?? ""))) ? Date.parse(String(row.occurredAt)) : Number.NaN;
|
|
1506
|
+
if (fromMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs < fromMs)) continue;
|
|
1507
|
+
if (toMs !== null && (!Number.isFinite(occurredAtMs) || occurredAtMs > toMs)) continue;
|
|
1508
|
+
out.push(row);
|
|
1509
|
+
}
|
|
1510
|
+
out.sort((left, right) => {
|
|
1511
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1512
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1513
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1514
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1515
|
+
});
|
|
1516
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
store.putX402ReversalNonceUsage = async function putX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
1520
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1521
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1522
|
+
const sponsorRef = typeof usage.sponsorRef === "string" ? usage.sponsorRef.trim() : "";
|
|
1523
|
+
const nonce = typeof usage.nonce === "string" ? usage.nonce.trim() : "";
|
|
1524
|
+
if (!sponsorRef) throw new TypeError("usage.sponsorRef is required");
|
|
1525
|
+
if (!nonce) throw new TypeError("usage.nonce is required");
|
|
1526
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1527
|
+
await store.commitTx({
|
|
1528
|
+
at,
|
|
1529
|
+
ops: [{ kind: "X402_REVERSAL_NONCE_PUT", tenantId, sponsorRef, nonce, usage: { ...usage, tenantId, sponsorRef, nonce } }],
|
|
1530
|
+
audit
|
|
1531
|
+
});
|
|
1532
|
+
return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
store.getX402ReversalNonceUsage = async function getX402ReversalNonceUsage({ tenantId = DEFAULT_TENANT_ID, sponsorRef, nonce } = {}) {
|
|
1536
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1537
|
+
return store.x402ReversalNonceUsage.get(x402ReversalNonceStoreKey({ tenantId, sponsorRef, nonce })) ?? null;
|
|
1538
|
+
};
|
|
1539
|
+
|
|
1540
|
+
store.putX402ReversalCommandUsage = async function putX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
1541
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1542
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1543
|
+
const commandId = typeof usage.commandId === "string" ? usage.commandId.trim() : "";
|
|
1544
|
+
if (!commandId) throw new TypeError("usage.commandId is required");
|
|
1545
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1546
|
+
await store.commitTx({
|
|
1547
|
+
at,
|
|
1548
|
+
ops: [{ kind: "X402_REVERSAL_COMMAND_PUT", tenantId, commandId, usage: { ...usage, tenantId, commandId } }],
|
|
1549
|
+
audit
|
|
1550
|
+
});
|
|
1551
|
+
return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId })) ?? null;
|
|
701
1552
|
};
|
|
702
1553
|
|
|
703
|
-
store.
|
|
1554
|
+
store.getX402ReversalCommandUsage = async function getX402ReversalCommandUsage({ tenantId = DEFAULT_TENANT_ID, commandId } = {}) {
|
|
704
1555
|
tenantId = normalizeTenantId(tenantId);
|
|
705
|
-
if (typeof
|
|
706
|
-
|
|
707
|
-
return store.agentIdentities.get(key) ?? null;
|
|
1556
|
+
if (typeof commandId !== "string" || commandId.trim() === "") throw new TypeError("commandId is required");
|
|
1557
|
+
return store.x402ReversalCommandUsage.get(makeScopedKey({ tenantId, id: commandId.trim() })) ?? null;
|
|
708
1558
|
};
|
|
709
1559
|
|
|
710
|
-
store.
|
|
1560
|
+
store.putX402Escalation = async function putX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalation, audit = null } = {}) {
|
|
1561
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1562
|
+
if (!escalation || typeof escalation !== "object" || Array.isArray(escalation)) throw new TypeError("escalation is required");
|
|
1563
|
+
const escalationId = typeof escalation.escalationId === "string" ? escalation.escalationId.trim() : "";
|
|
1564
|
+
if (!escalationId) throw new TypeError("escalation.escalationId is required");
|
|
1565
|
+
const at = escalation.updatedAt ?? escalation.createdAt ?? new Date().toISOString();
|
|
1566
|
+
await store.commitTx({
|
|
1567
|
+
at,
|
|
1568
|
+
ops: [{ kind: "X402_ESCALATION_UPSERT", tenantId, escalationId, escalation: { ...escalation, tenantId, escalationId } }],
|
|
1569
|
+
audit
|
|
1570
|
+
});
|
|
1571
|
+
return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId })) ?? null;
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
store.getX402Escalation = async function getX402Escalation({ tenantId = DEFAULT_TENANT_ID, escalationId } = {}) {
|
|
1575
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1576
|
+
if (typeof escalationId !== "string" || escalationId.trim() === "") throw new TypeError("escalationId is required");
|
|
1577
|
+
return store.x402Escalations.get(makeScopedKey({ tenantId, id: escalationId.trim() })) ?? null;
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1580
|
+
store.listX402Escalations = async function listX402Escalations({
|
|
1581
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1582
|
+
gateId = null,
|
|
1583
|
+
agentId = null,
|
|
1584
|
+
status = null,
|
|
1585
|
+
limit = 200,
|
|
1586
|
+
offset = 0
|
|
1587
|
+
} = {}) {
|
|
711
1588
|
tenantId = normalizeTenantId(tenantId);
|
|
1589
|
+
if (gateId !== null && (typeof gateId !== "string" || gateId.trim() === "")) throw new TypeError("gateId must be null or a non-empty string");
|
|
1590
|
+
if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
|
|
712
1591
|
if (status !== null && (typeof status !== "string" || status.trim() === "")) throw new TypeError("status must be null or a non-empty string");
|
|
713
1592
|
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
714
1593
|
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
const
|
|
1594
|
+
|
|
1595
|
+
const normalizedGateId = gateId ? gateId.trim() : null;
|
|
1596
|
+
const normalizedAgentId = agentId ? agentId.trim() : null;
|
|
1597
|
+
const normalizedStatus = status ? status.trim().toLowerCase() : null;
|
|
718
1598
|
const out = [];
|
|
719
|
-
for (const row of store.
|
|
720
|
-
if (!row || typeof row !== "object") continue;
|
|
1599
|
+
for (const row of store.x402Escalations.values()) {
|
|
1600
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
721
1601
|
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
722
|
-
if (
|
|
1602
|
+
if (normalizedGateId && String(row.gateId ?? "") !== normalizedGateId) continue;
|
|
1603
|
+
if (
|
|
1604
|
+
normalizedAgentId &&
|
|
1605
|
+
String(
|
|
1606
|
+
row.requesterAgentId ??
|
|
1607
|
+
row.payerAgentId ??
|
|
1608
|
+
null
|
|
1609
|
+
).trim() !== normalizedAgentId
|
|
1610
|
+
) {
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
if (normalizedStatus && String(row.status ?? "").toLowerCase() !== normalizedStatus) continue;
|
|
723
1614
|
out.push(row);
|
|
724
1615
|
}
|
|
725
|
-
out.sort((left, right) =>
|
|
726
|
-
|
|
1616
|
+
out.sort((left, right) => {
|
|
1617
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
1618
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
1619
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1620
|
+
return String(left?.escalationId ?? "").localeCompare(String(right?.escalationId ?? ""));
|
|
1621
|
+
});
|
|
1622
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
727
1623
|
};
|
|
728
1624
|
|
|
729
|
-
store.
|
|
1625
|
+
store.appendX402EscalationEvent = async function appendX402EscalationEvent({ tenantId = DEFAULT_TENANT_ID, event, audit = null } = {}) {
|
|
730
1626
|
tenantId = normalizeTenantId(tenantId);
|
|
731
|
-
if (typeof
|
|
732
|
-
|
|
1627
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
|
|
1628
|
+
const eventId = typeof event.eventId === "string" ? event.eventId.trim() : "";
|
|
1629
|
+
const escalationId = typeof event.escalationId === "string" ? event.escalationId.trim() : "";
|
|
1630
|
+
if (!eventId) throw new TypeError("event.eventId is required");
|
|
1631
|
+
if (!escalationId) throw new TypeError("event.escalationId is required");
|
|
1632
|
+
const at = event.occurredAt ?? event.createdAt ?? new Date().toISOString();
|
|
1633
|
+
await store.commitTx({
|
|
1634
|
+
at,
|
|
1635
|
+
ops: [{ kind: "X402_ESCALATION_EVENT_APPEND", tenantId, eventId, escalationId, event: { ...event, tenantId, eventId, escalationId } }],
|
|
1636
|
+
audit
|
|
1637
|
+
});
|
|
1638
|
+
return store.x402EscalationEvents.get(makeScopedKey({ tenantId, id: eventId })) ?? null;
|
|
733
1639
|
};
|
|
734
1640
|
|
|
735
|
-
store.
|
|
1641
|
+
store.listX402EscalationEvents = async function listX402EscalationEvents({
|
|
1642
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1643
|
+
escalationId = null,
|
|
1644
|
+
limit = 200,
|
|
1645
|
+
offset = 0
|
|
1646
|
+
} = {}) {
|
|
736
1647
|
tenantId = normalizeTenantId(tenantId);
|
|
737
|
-
if (
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1648
|
+
if (escalationId !== null && (typeof escalationId !== "string" || escalationId.trim() === "")) {
|
|
1649
|
+
throw new TypeError("escalationId must be null or a non-empty string");
|
|
1650
|
+
}
|
|
1651
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
1652
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1653
|
+
const normalizedEscalationId = escalationId ? escalationId.trim() : null;
|
|
1654
|
+
const out = [];
|
|
1655
|
+
for (const row of store.x402EscalationEvents.values()) {
|
|
1656
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1657
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1658
|
+
if (normalizedEscalationId && String(row.escalationId ?? "") !== normalizedEscalationId) continue;
|
|
1659
|
+
out.push(row);
|
|
1660
|
+
}
|
|
1661
|
+
out.sort((left, right) => {
|
|
1662
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1663
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1664
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
1665
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1666
|
+
});
|
|
1667
|
+
return out.slice(offset, offset + Math.min(1000, limit));
|
|
743
1668
|
};
|
|
744
1669
|
|
|
745
|
-
store.
|
|
1670
|
+
store.putX402EscalationOverrideUsage = async function putX402EscalationOverrideUsage({ tenantId = DEFAULT_TENANT_ID, usage, audit = null } = {}) {
|
|
746
1671
|
tenantId = normalizeTenantId(tenantId);
|
|
747
|
-
if (typeof
|
|
748
|
-
|
|
1672
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) throw new TypeError("usage is required");
|
|
1673
|
+
const overrideId = typeof usage.overrideId === "string" ? usage.overrideId.trim() : "";
|
|
1674
|
+
if (!overrideId) throw new TypeError("usage.overrideId is required");
|
|
1675
|
+
const at = usage.usedAt ?? new Date().toISOString();
|
|
1676
|
+
await store.commitTx({
|
|
1677
|
+
at,
|
|
1678
|
+
ops: [{ kind: "X402_ESCALATION_OVERRIDE_USAGE_PUT", tenantId, overrideId, usage: { ...usage, tenantId, overrideId } }],
|
|
1679
|
+
audit
|
|
1680
|
+
});
|
|
1681
|
+
return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId })) ?? null;
|
|
749
1682
|
};
|
|
750
1683
|
|
|
751
|
-
store.
|
|
1684
|
+
store.getX402EscalationOverrideUsage = async function getX402EscalationOverrideUsage({
|
|
1685
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1686
|
+
overrideId
|
|
1687
|
+
} = {}) {
|
|
752
1688
|
tenantId = normalizeTenantId(tenantId);
|
|
753
|
-
if (
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
1689
|
+
if (typeof overrideId !== "string" || overrideId.trim() === "") throw new TypeError("overrideId is required");
|
|
1690
|
+
return store.x402EscalationOverrideUsage.get(makeScopedKey({ tenantId, id: overrideId.trim() })) ?? null;
|
|
1691
|
+
};
|
|
757
1692
|
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
const
|
|
1693
|
+
function x402ReceiptStoreKey({ tenantId, receiptId }) {
|
|
1694
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1695
|
+
const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
|
|
1696
|
+
if (!normalizedReceiptId) throw new TypeError("receiptId is required");
|
|
1697
|
+
return makeScopedKey({ tenantId: normalizedTenantId, id: normalizedReceiptId });
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
function listX402ReversalEventsForReceiptSync({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
|
|
1701
|
+
const normalizedTenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
1702
|
+
const normalizedReceiptId = typeof receiptId === "string" ? receiptId.trim() : "";
|
|
1703
|
+
if (!normalizedReceiptId) return [];
|
|
761
1704
|
const out = [];
|
|
762
|
-
for (const row of store.
|
|
763
|
-
if (!row || typeof row !== "object") continue;
|
|
764
|
-
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !==
|
|
765
|
-
if (
|
|
766
|
-
if (statusFilter !== null && String(row.status ?? "").toLowerCase() !== statusFilter) continue;
|
|
1705
|
+
for (const row of store.x402ReversalEvents.values()) {
|
|
1706
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1707
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== normalizedTenantId) continue;
|
|
1708
|
+
if (String(row.receiptId ?? "") !== normalizedReceiptId) continue;
|
|
767
1709
|
out.push(row);
|
|
768
1710
|
}
|
|
769
|
-
out.sort((left, right) =>
|
|
770
|
-
|
|
1711
|
+
out.sort((left, right) => {
|
|
1712
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.occurredAt ?? ""))) ? Date.parse(String(left.occurredAt)) : Number.NaN;
|
|
1713
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.occurredAt ?? ""))) ? Date.parse(String(right.occurredAt)) : Number.NaN;
|
|
1714
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
1715
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
1716
|
+
});
|
|
1717
|
+
return out;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function toX402ReceiptRecord({ tenantId, gate, settlement: settlementInput = null, includeReversalContext = true } = {}) {
|
|
1721
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) return null;
|
|
1722
|
+
const runId = typeof gate.runId === "string" && gate.runId.trim() !== "" ? gate.runId.trim() : null;
|
|
1723
|
+
const settlement =
|
|
1724
|
+
settlementInput && typeof settlementInput === "object" && !Array.isArray(settlementInput)
|
|
1725
|
+
? settlementInput
|
|
1726
|
+
: runId && store.agentRunSettlements instanceof Map
|
|
1727
|
+
? store.agentRunSettlements.get(makeScopedKey({ tenantId, id: runId })) ?? null
|
|
1728
|
+
: null;
|
|
1729
|
+
const decisionTrace =
|
|
1730
|
+
settlement?.decisionTrace && typeof settlement.decisionTrace === "object" && !Array.isArray(settlement.decisionTrace)
|
|
1731
|
+
? settlement.decisionTrace
|
|
1732
|
+
: gate.decisionTrace && typeof gate.decisionTrace === "object" && !Array.isArray(gate.decisionTrace)
|
|
1733
|
+
? gate.decisionTrace
|
|
1734
|
+
: null;
|
|
1735
|
+
const settlementReceipt =
|
|
1736
|
+
decisionTrace?.settlementReceipt && typeof decisionTrace.settlementReceipt === "object" && !Array.isArray(decisionTrace.settlementReceipt)
|
|
1737
|
+
? decisionTrace.settlementReceipt
|
|
1738
|
+
: null;
|
|
1739
|
+
if (!settlementReceipt) return null;
|
|
1740
|
+
const receiptId = typeof settlementReceipt.receiptId === "string" ? settlementReceipt.receiptId.trim() : "";
|
|
1741
|
+
if (!receiptId) return null;
|
|
1742
|
+
const bindings =
|
|
1743
|
+
decisionTrace?.bindings && typeof decisionTrace.bindings === "object" && !Array.isArray(decisionTrace.bindings)
|
|
1744
|
+
? decisionTrace.bindings
|
|
1745
|
+
: null;
|
|
1746
|
+
const verificationContext =
|
|
1747
|
+
gate?.verificationContext && typeof gate.verificationContext === "object" && !Array.isArray(gate.verificationContext)
|
|
1748
|
+
? gate.verificationContext
|
|
1749
|
+
: gate?.decision?.verificationContext &&
|
|
1750
|
+
typeof gate.decision.verificationContext === "object" &&
|
|
1751
|
+
!Array.isArray(gate.decision.verificationContext)
|
|
1752
|
+
? gate.decision.verificationContext
|
|
1753
|
+
: null;
|
|
1754
|
+
const sponsorRef =
|
|
1755
|
+
typeof bindings?.spendAuthorization?.sponsorRef === "string" && bindings.spendAuthorization.sponsorRef.trim() !== ""
|
|
1756
|
+
? bindings.spendAuthorization.sponsorRef.trim()
|
|
1757
|
+
: null;
|
|
1758
|
+
const sponsorWalletRef =
|
|
1759
|
+
typeof bindings?.spendAuthorization?.sponsorWalletRef === "string" && bindings.spendAuthorization.sponsorWalletRef.trim() !== ""
|
|
1760
|
+
? bindings.spendAuthorization.sponsorWalletRef.trim()
|
|
1761
|
+
: null;
|
|
1762
|
+
const agentKeyId =
|
|
1763
|
+
typeof bindings?.spendAuthorization?.agentKeyId === "string" && bindings.spendAuthorization.agentKeyId.trim() !== ""
|
|
1764
|
+
? bindings.spendAuthorization.agentKeyId.trim()
|
|
1765
|
+
: null;
|
|
1766
|
+
const settledAt =
|
|
1767
|
+
typeof settlementReceipt.settledAt === "string" && settlementReceipt.settledAt.trim() !== ""
|
|
1768
|
+
? settlementReceipt.settledAt.trim()
|
|
1769
|
+
: typeof gate.resolvedAt === "string" && gate.resolvedAt.trim() !== ""
|
|
1770
|
+
? gate.resolvedAt.trim()
|
|
1771
|
+
: null;
|
|
1772
|
+
const base = {
|
|
1773
|
+
schemaVersion: "X402ReceiptRecord.v1",
|
|
1774
|
+
tenantId,
|
|
1775
|
+
receiptId,
|
|
1776
|
+
gateId: typeof gate.gateId === "string" ? gate.gateId : null,
|
|
1777
|
+
runId,
|
|
1778
|
+
payerAgentId: typeof gate.payerAgentId === "string" ? gate.payerAgentId : null,
|
|
1779
|
+
providerId: typeof gate.payeeAgentId === "string" ? gate.payeeAgentId : null,
|
|
1780
|
+
toolId:
|
|
1781
|
+
typeof gate.toolId === "string" && gate.toolId.trim() !== ""
|
|
1782
|
+
? gate.toolId.trim()
|
|
1783
|
+
: typeof gate?.quote?.toolId === "string" && gate.quote.toolId.trim() !== ""
|
|
1784
|
+
? gate.quote.toolId.trim()
|
|
1785
|
+
: null,
|
|
1786
|
+
sponsorRef,
|
|
1787
|
+
sponsorWalletRef,
|
|
1788
|
+
agentKeyId,
|
|
1789
|
+
settlementState:
|
|
1790
|
+
typeof settlementReceipt.status === "string" && settlementReceipt.status.trim() !== ""
|
|
1791
|
+
? settlementReceipt.status.trim().toLowerCase()
|
|
1792
|
+
: typeof settlement?.status === "string" && settlement.status.trim() !== ""
|
|
1793
|
+
? settlement.status.trim().toLowerCase()
|
|
1794
|
+
: null,
|
|
1795
|
+
verificationStatus:
|
|
1796
|
+
typeof decisionTrace?.verificationStatus === "string" && decisionTrace.verificationStatus.trim() !== ""
|
|
1797
|
+
? decisionTrace.verificationStatus.trim().toLowerCase()
|
|
1798
|
+
: null,
|
|
1799
|
+
settledAt,
|
|
1800
|
+
createdAt:
|
|
1801
|
+
typeof settlementReceipt.createdAt === "string" && settlementReceipt.createdAt.trim() !== ""
|
|
1802
|
+
? settlementReceipt.createdAt.trim()
|
|
1803
|
+
: null,
|
|
1804
|
+
updatedAt:
|
|
1805
|
+
typeof gate.updatedAt === "string" && gate.updatedAt.trim() !== ""
|
|
1806
|
+
? gate.updatedAt.trim()
|
|
1807
|
+
: typeof settledAt === "string"
|
|
1808
|
+
? settledAt
|
|
1809
|
+
: null,
|
|
1810
|
+
evidenceRefs: Array.isArray(gate.evidenceRefs) ? gate.evidenceRefs.slice() : [],
|
|
1811
|
+
verificationContext,
|
|
1812
|
+
bindings,
|
|
1813
|
+
providerSignature:
|
|
1814
|
+
gate?.providerSignature && typeof gate.providerSignature === "object" && !Array.isArray(gate.providerSignature)
|
|
1815
|
+
? gate.providerSignature
|
|
1816
|
+
: null,
|
|
1817
|
+
providerQuoteSignature:
|
|
1818
|
+
gate?.providerQuoteSignature && typeof gate.providerQuoteSignature === "object" && !Array.isArray(gate.providerQuoteSignature)
|
|
1819
|
+
? gate.providerQuoteSignature
|
|
1820
|
+
: null,
|
|
1821
|
+
providerQuotePayload:
|
|
1822
|
+
gate?.providerQuotePayload && typeof gate.providerQuotePayload === "object" && !Array.isArray(gate.providerQuotePayload)
|
|
1823
|
+
? gate.providerQuotePayload
|
|
1824
|
+
: null,
|
|
1825
|
+
zkProof:
|
|
1826
|
+
gate?.zkProof && typeof gate.zkProof === "object" && !Array.isArray(gate.zkProof)
|
|
1827
|
+
? gate.zkProof
|
|
1828
|
+
: null,
|
|
1829
|
+
decisionRecord:
|
|
1830
|
+
decisionTrace?.decisionRecord && typeof decisionTrace.decisionRecord === "object" && !Array.isArray(decisionTrace.decisionRecord)
|
|
1831
|
+
? decisionTrace.decisionRecord
|
|
1832
|
+
: null,
|
|
1833
|
+
settlementReceipt
|
|
1834
|
+
};
|
|
1835
|
+
if (!includeReversalContext) return base;
|
|
1836
|
+
return {
|
|
1837
|
+
...base,
|
|
1838
|
+
reversal:
|
|
1839
|
+
gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
|
|
1840
|
+
? gate.reversal
|
|
1841
|
+
: null,
|
|
1842
|
+
reversalEvents: listX402ReversalEventsForReceiptSync({ tenantId, receiptId })
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function normalizeX402ReceiptRecordForStorage({ receipt } = {}) {
|
|
1847
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
|
|
1848
|
+
const stableUpdatedAt =
|
|
1849
|
+
typeof receipt.createdAt === "string" && receipt.createdAt.trim() !== ""
|
|
1850
|
+
? receipt.createdAt
|
|
1851
|
+
: typeof receipt.settledAt === "string" && receipt.settledAt.trim() !== ""
|
|
1852
|
+
? receipt.settledAt
|
|
1853
|
+
: typeof receipt.updatedAt === "string" && receipt.updatedAt.trim() !== ""
|
|
1854
|
+
? receipt.updatedAt
|
|
1855
|
+
: new Date().toISOString();
|
|
1856
|
+
const normalized = {
|
|
1857
|
+
...receipt,
|
|
1858
|
+
reversal: null,
|
|
1859
|
+
reversalEvents: [],
|
|
1860
|
+
updatedAt: stableUpdatedAt
|
|
1861
|
+
};
|
|
1862
|
+
return normalized;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
function projectX402ReceiptRecord({ tenantId, receipt } = {}) {
|
|
1866
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return null;
|
|
1867
|
+
const receiptId = typeof receipt.receiptId === "string" ? receipt.receiptId.trim() : "";
|
|
1868
|
+
if (!receiptId) return null;
|
|
1869
|
+
const gateId = typeof receipt.gateId === "string" ? receipt.gateId.trim() : "";
|
|
1870
|
+
const gate =
|
|
1871
|
+
gateId && store.x402Gates instanceof Map
|
|
1872
|
+
? store.x402Gates.get(makeScopedKey({ tenantId, id: gateId })) ?? null
|
|
1873
|
+
: null;
|
|
1874
|
+
const reversalEvents = listX402ReversalEventsForReceiptSync({ tenantId, receiptId });
|
|
1875
|
+
const latestEvent = reversalEvents.length > 0 ? reversalEvents[reversalEvents.length - 1] : null;
|
|
1876
|
+
const derivedState =
|
|
1877
|
+
typeof latestEvent?.settlementStatusAfter === "string" && latestEvent.settlementStatusAfter.trim() !== ""
|
|
1878
|
+
? latestEvent.settlementStatusAfter.trim().toLowerCase()
|
|
1879
|
+
: null;
|
|
1880
|
+
const updatedAtCandidates = [receipt.updatedAt, latestEvent?.occurredAt]
|
|
1881
|
+
.map((value) => (typeof value === "string" && value.trim() !== "" && Number.isFinite(Date.parse(value)) ? new Date(Date.parse(value)).toISOString() : null))
|
|
1882
|
+
.filter(Boolean);
|
|
1883
|
+
const updatedAt = updatedAtCandidates.length > 0 ? updatedAtCandidates.sort((a, b) => Date.parse(a) - Date.parse(b))[updatedAtCandidates.length - 1] : null;
|
|
1884
|
+
return {
|
|
1885
|
+
...receipt,
|
|
1886
|
+
settlementState: derivedState ?? receipt.settlementState ?? null,
|
|
1887
|
+
updatedAt: updatedAt ?? receipt.updatedAt ?? null,
|
|
1888
|
+
reversal:
|
|
1889
|
+
gate?.reversal && typeof gate.reversal === "object" && !Array.isArray(gate.reversal)
|
|
1890
|
+
? gate.reversal
|
|
1891
|
+
: receipt?.reversal && typeof receipt.reversal === "object" && !Array.isArray(receipt.reversal)
|
|
1892
|
+
? receipt.reversal
|
|
1893
|
+
: null,
|
|
1894
|
+
reversalEvents
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
function compareX402ReceiptRecords(left, right) {
|
|
1899
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.settledAt ?? ""))) ? Date.parse(String(left.settledAt)) : Number.NaN;
|
|
1900
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.settledAt ?? ""))) ? Date.parse(String(right.settledAt)) : Number.NaN;
|
|
1901
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return rightMs - leftMs;
|
|
1902
|
+
return String(left?.receiptId ?? "").localeCompare(String(right?.receiptId ?? ""));
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
function encodeX402ReceiptCursor(record) {
|
|
1906
|
+
const settledAt = typeof record?.settledAt === "string" && record.settledAt.trim() !== "" ? new Date(Date.parse(record.settledAt)).toISOString() : null;
|
|
1907
|
+
const receiptId = typeof record?.receiptId === "string" && record.receiptId.trim() !== "" ? record.receiptId.trim() : null;
|
|
1908
|
+
if (!settledAt || !receiptId) return null;
|
|
1909
|
+
return Buffer.from(JSON.stringify({ settledAt, receiptId }), "utf8").toString("base64url");
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function decodeX402ReceiptCursor(raw) {
|
|
1913
|
+
if (raw === null || raw === undefined || String(raw).trim() === "") return null;
|
|
1914
|
+
let parsed;
|
|
1915
|
+
try {
|
|
1916
|
+
parsed = JSON.parse(Buffer.from(String(raw).trim(), "base64url").toString("utf8"));
|
|
1917
|
+
} catch {
|
|
1918
|
+
throw new TypeError("cursor must be base64url-encoded JSON");
|
|
1919
|
+
}
|
|
1920
|
+
const settledAt = typeof parsed?.settledAt === "string" && Number.isFinite(Date.parse(parsed.settledAt)) ? new Date(Date.parse(parsed.settledAt)).toISOString() : null;
|
|
1921
|
+
const receiptId = typeof parsed?.receiptId === "string" && parsed.receiptId.trim() !== "" ? parsed.receiptId.trim() : null;
|
|
1922
|
+
if (!settledAt || !receiptId) throw new TypeError("cursor is invalid");
|
|
1923
|
+
return { settledAt, receiptId };
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
function isReceiptAfterCursor(record, cursor) {
|
|
1927
|
+
if (!cursor) return true;
|
|
1928
|
+
const recordMs = Number.isFinite(Date.parse(String(record?.settledAt ?? ""))) ? Date.parse(String(record.settledAt)) : Number.NaN;
|
|
1929
|
+
const cursorMs = Date.parse(cursor.settledAt);
|
|
1930
|
+
if (!Number.isFinite(recordMs)) return false;
|
|
1931
|
+
if (recordMs < cursorMs) return true;
|
|
1932
|
+
if (recordMs > cursorMs) return false;
|
|
1933
|
+
return String(record?.receiptId ?? "") > String(cursor.receiptId ?? "");
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
function listX402ReceiptCandidates({ tenantId }) {
|
|
1937
|
+
const byReceiptId = new Map();
|
|
1938
|
+
if (store.x402Receipts instanceof Map) {
|
|
1939
|
+
for (const row of store.x402Receipts.values()) {
|
|
1940
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1941
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1942
|
+
const receiptId = typeof row.receiptId === "string" ? row.receiptId.trim() : "";
|
|
1943
|
+
if (!receiptId) continue;
|
|
1944
|
+
byReceiptId.set(receiptId, projectX402ReceiptRecord({ tenantId, receipt: row }));
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
if (byReceiptId.size === 0 && store.x402Gates instanceof Map) {
|
|
1948
|
+
for (const gate of store.x402Gates.values()) {
|
|
1949
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
|
|
1950
|
+
if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
1951
|
+
const derived = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
|
|
1952
|
+
if (!derived) continue;
|
|
1953
|
+
const receiptId = typeof derived.receiptId === "string" ? derived.receiptId.trim() : "";
|
|
1954
|
+
if (!receiptId) continue;
|
|
1955
|
+
byReceiptId.set(receiptId, derived);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return Array.from(byReceiptId.values()).filter(Boolean);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
store.deriveX402ReceiptRecord = function deriveX402ReceiptRecord({
|
|
1962
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
1963
|
+
gate,
|
|
1964
|
+
settlement = null,
|
|
1965
|
+
includeReversalContext = false
|
|
1966
|
+
} = {}) {
|
|
1967
|
+
tenantId = normalizeTenantId(tenantId);
|
|
1968
|
+
const derived = toX402ReceiptRecord({ tenantId, gate, settlement, includeReversalContext });
|
|
1969
|
+
return normalizeX402ReceiptRecordForStorage({ receipt: derived });
|
|
771
1970
|
};
|
|
772
1971
|
|
|
773
|
-
store.
|
|
1972
|
+
store.putX402Receipt = async function putX402Receipt({ tenantId = DEFAULT_TENANT_ID, receipt, audit = null } = {}) {
|
|
774
1973
|
tenantId = normalizeTenantId(tenantId);
|
|
775
|
-
|
|
776
|
-
|
|
1974
|
+
const normalized = normalizeX402ReceiptRecordForStorage({ receipt });
|
|
1975
|
+
if (!normalized) throw new TypeError("receipt is required");
|
|
1976
|
+
const receiptId = typeof normalized.receiptId === "string" ? normalized.receiptId.trim() : "";
|
|
1977
|
+
if (!receiptId) throw new TypeError("receipt.receiptId is required");
|
|
1978
|
+
const key = x402ReceiptStoreKey({ tenantId, receiptId });
|
|
1979
|
+
const at =
|
|
1980
|
+
typeof normalized.updatedAt === "string" && normalized.updatedAt.trim() !== ""
|
|
1981
|
+
? normalized.updatedAt
|
|
1982
|
+
: typeof normalized.createdAt === "string" && normalized.createdAt.trim() !== ""
|
|
1983
|
+
? normalized.createdAt
|
|
1984
|
+
: new Date().toISOString();
|
|
1985
|
+
await store.commitTx({
|
|
1986
|
+
at,
|
|
1987
|
+
ops: [{ kind: "X402_RECEIPT_PUT", tenantId, receiptId, receipt: { ...normalized, tenantId, receiptId } }],
|
|
1988
|
+
audit
|
|
1989
|
+
});
|
|
1990
|
+
return store.x402Receipts.get(key) ?? null;
|
|
777
1991
|
};
|
|
778
1992
|
|
|
779
|
-
store.
|
|
1993
|
+
store.getX402Receipt = async function getX402Receipt({ tenantId = DEFAULT_TENANT_ID, receiptId } = {}) {
|
|
780
1994
|
tenantId = normalizeTenantId(tenantId);
|
|
781
|
-
if (typeof
|
|
782
|
-
|
|
1995
|
+
if (typeof receiptId !== "string" || receiptId.trim() === "") throw new TypeError("receiptId is required");
|
|
1996
|
+
const wanted = receiptId.trim();
|
|
1997
|
+
const key = x402ReceiptStoreKey({ tenantId, receiptId: wanted });
|
|
1998
|
+
const stored = store.x402Receipts instanceof Map ? store.x402Receipts.get(key) ?? null : null;
|
|
1999
|
+
if (stored) return projectX402ReceiptRecord({ tenantId, receipt: stored });
|
|
2000
|
+
for (const gate of store.x402Gates.values()) {
|
|
2001
|
+
if (!gate || typeof gate !== "object" || Array.isArray(gate)) continue;
|
|
2002
|
+
if (normalizeTenantId(gate.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
2003
|
+
const receipt = toX402ReceiptRecord({ tenantId, gate, includeReversalContext: true });
|
|
2004
|
+
if (!receipt) continue;
|
|
2005
|
+
if (String(receipt.receiptId ?? "") === wanted) return receipt;
|
|
2006
|
+
}
|
|
2007
|
+
return null;
|
|
783
2008
|
};
|
|
784
2009
|
|
|
785
|
-
store.
|
|
2010
|
+
store.listX402ReceiptsPage = async function listX402ReceiptsPage({
|
|
2011
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
2012
|
+
agentId = null,
|
|
2013
|
+
sponsorId = null,
|
|
2014
|
+
sponsorWalletRef = null,
|
|
2015
|
+
toolId = null,
|
|
2016
|
+
state = null,
|
|
2017
|
+
from = null,
|
|
2018
|
+
to = null,
|
|
2019
|
+
limit = 200,
|
|
2020
|
+
offset = 0,
|
|
2021
|
+
cursor = null
|
|
2022
|
+
} = {}) {
|
|
786
2023
|
tenantId = normalizeTenantId(tenantId);
|
|
787
|
-
if (typeof
|
|
788
|
-
|
|
2024
|
+
if (agentId !== null && (typeof agentId !== "string" || agentId.trim() === "")) throw new TypeError("agentId must be null or a non-empty string");
|
|
2025
|
+
if (sponsorId !== null && (typeof sponsorId !== "string" || sponsorId.trim() === "")) throw new TypeError("sponsorId must be null or a non-empty string");
|
|
2026
|
+
if (sponsorWalletRef !== null && (typeof sponsorWalletRef !== "string" || sponsorWalletRef.trim() === "")) {
|
|
2027
|
+
throw new TypeError("sponsorWalletRef must be null or a non-empty string");
|
|
2028
|
+
}
|
|
2029
|
+
if (toolId !== null && (typeof toolId !== "string" || toolId.trim() === "")) throw new TypeError("toolId must be null or a non-empty string");
|
|
2030
|
+
if (state !== null && (typeof state !== "string" || state.trim() === "")) throw new TypeError("state must be null or a non-empty string");
|
|
2031
|
+
if (from !== null && !Number.isFinite(Date.parse(String(from)))) throw new TypeError("from must be null or an ISO date-time");
|
|
2032
|
+
if (to !== null && !Number.isFinite(Date.parse(String(to)))) throw new TypeError("to must be null or an ISO date-time");
|
|
2033
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
2034
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
2035
|
+
|
|
2036
|
+
const normalizedAgentId = agentId ? agentId.trim() : null;
|
|
2037
|
+
const normalizedSponsorId = sponsorId ? sponsorId.trim() : null;
|
|
2038
|
+
const normalizedSponsorWalletRef = sponsorWalletRef ? sponsorWalletRef.trim() : null;
|
|
2039
|
+
const normalizedToolId = toolId ? toolId.trim() : null;
|
|
2040
|
+
const normalizedState = state ? state.trim().toLowerCase() : null;
|
|
2041
|
+
const fromMs = from ? Date.parse(String(from)) : null;
|
|
2042
|
+
const toMs = to ? Date.parse(String(to)) : null;
|
|
2043
|
+
const decodedCursor = cursor === null ? null : decodeX402ReceiptCursor(cursor);
|
|
2044
|
+
|
|
2045
|
+
const all = listX402ReceiptCandidates({ tenantId });
|
|
2046
|
+
const filtered = all.filter((receipt) => {
|
|
2047
|
+
if (!receipt) return false;
|
|
2048
|
+
if (
|
|
2049
|
+
normalizedAgentId &&
|
|
2050
|
+
String(receipt.payerAgentId ?? "") !== normalizedAgentId &&
|
|
2051
|
+
String(receipt.providerId ?? "") !== normalizedAgentId &&
|
|
2052
|
+
String(receipt.agentKeyId ?? "") !== normalizedAgentId
|
|
2053
|
+
) {
|
|
2054
|
+
return false;
|
|
2055
|
+
}
|
|
2056
|
+
if (normalizedSponsorId && String(receipt.sponsorRef ?? "") !== normalizedSponsorId) return false;
|
|
2057
|
+
if (normalizedSponsorWalletRef && String(receipt.sponsorWalletRef ?? "") !== normalizedSponsorWalletRef) return false;
|
|
2058
|
+
if (normalizedToolId && String(receipt.toolId ?? "") !== normalizedToolId) return false;
|
|
2059
|
+
if (normalizedState && String(receipt.settlementState ?? "").toLowerCase() !== normalizedState) return false;
|
|
2060
|
+
const settledAtMs = Number.isFinite(Date.parse(String(receipt.settledAt ?? ""))) ? Date.parse(String(receipt.settledAt)) : Number.NaN;
|
|
2061
|
+
if (fromMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs < fromMs)) return false;
|
|
2062
|
+
if (toMs !== null && (!Number.isFinite(settledAtMs) || settledAtMs > toMs)) return false;
|
|
2063
|
+
return true;
|
|
2064
|
+
});
|
|
2065
|
+
|
|
2066
|
+
filtered.sort(compareX402ReceiptRecords);
|
|
2067
|
+
const cursorFiltered = decodedCursor ? filtered.filter((row) => isReceiptAfterCursor(row, decodedCursor)) : filtered;
|
|
2068
|
+
const paged = cursorFiltered.slice(offset, offset + Math.min(1000, limit));
|
|
2069
|
+
const hasMore = cursorFiltered.length > offset + paged.length;
|
|
2070
|
+
const nextCursor = hasMore && paged.length > 0 ? encodeX402ReceiptCursor(paged[paged.length - 1]) : null;
|
|
2071
|
+
return { receipts: paged, nextCursor };
|
|
2072
|
+
};
|
|
2073
|
+
|
|
2074
|
+
store.listX402Receipts = async function listX402Receipts(args = {}) {
|
|
2075
|
+
const page = await store.listX402ReceiptsPage(args);
|
|
2076
|
+
return page.receipts;
|
|
789
2077
|
};
|
|
790
2078
|
|
|
791
2079
|
store.listArbitrationCases = async function listArbitrationCases({
|
|
@@ -1299,6 +2587,55 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
1299
2587
|
return all;
|
|
1300
2588
|
};
|
|
1301
2589
|
|
|
2590
|
+
store.listReputationEvents = async function listReputationEvents({
|
|
2591
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
2592
|
+
agentId,
|
|
2593
|
+
toolId = null,
|
|
2594
|
+
occurredAtGte = null,
|
|
2595
|
+
occurredAtLte = null,
|
|
2596
|
+
limit = 1000,
|
|
2597
|
+
offset = 0
|
|
2598
|
+
} = {}) {
|
|
2599
|
+
tenantId = normalizeTenantId(tenantId);
|
|
2600
|
+
if (typeof agentId !== "string" || agentId.trim() === "") throw new TypeError("agentId is required");
|
|
2601
|
+
if (toolId !== null && toolId !== undefined && (typeof toolId !== "string" || toolId.trim() === "")) {
|
|
2602
|
+
throw new TypeError("toolId must be null or a non-empty string");
|
|
2603
|
+
}
|
|
2604
|
+
if (occurredAtGte !== null && occurredAtGte !== undefined && !Number.isFinite(Date.parse(String(occurredAtGte)))) {
|
|
2605
|
+
throw new TypeError("occurredAtGte must be an ISO date-time");
|
|
2606
|
+
}
|
|
2607
|
+
if (occurredAtLte !== null && occurredAtLte !== undefined && !Number.isFinite(Date.parse(String(occurredAtLte)))) {
|
|
2608
|
+
throw new TypeError("occurredAtLte must be an ISO date-time");
|
|
2609
|
+
}
|
|
2610
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
2611
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
2612
|
+
|
|
2613
|
+
const minMs = occurredAtGte ? Date.parse(String(occurredAtGte)) : Number.NaN;
|
|
2614
|
+
const maxMs = occurredAtLte ? Date.parse(String(occurredAtLte)) : Number.NaN;
|
|
2615
|
+
const out = [];
|
|
2616
|
+
for (const art of store.artifacts.values()) {
|
|
2617
|
+
if (!art || typeof art !== "object" || Array.isArray(art)) continue;
|
|
2618
|
+
if (normalizeTenantId(art.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
2619
|
+
if (String(art.schemaVersion ?? "") !== "ReputationEvent.v1") continue;
|
|
2620
|
+
const subject = art.subject && typeof art.subject === "object" && !Array.isArray(art.subject) ? art.subject : null;
|
|
2621
|
+
if (!subject) continue;
|
|
2622
|
+
if (String(subject.agentId ?? "") !== String(agentId)) continue;
|
|
2623
|
+
if (toolId !== null && toolId !== undefined && String(subject.toolId ?? "") !== String(toolId)) continue;
|
|
2624
|
+
const occurredAtMs = Date.parse(String(art.occurredAt ?? ""));
|
|
2625
|
+
if (!Number.isFinite(occurredAtMs)) continue;
|
|
2626
|
+
if (Number.isFinite(minMs) && occurredAtMs < minMs) continue;
|
|
2627
|
+
if (Number.isFinite(maxMs) && occurredAtMs > maxMs) continue;
|
|
2628
|
+
out.push(art);
|
|
2629
|
+
}
|
|
2630
|
+
out.sort((left, right) => {
|
|
2631
|
+
const leftMs = Date.parse(String(left?.occurredAt ?? ""));
|
|
2632
|
+
const rightMs = Date.parse(String(right?.occurredAt ?? ""));
|
|
2633
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && leftMs !== rightMs) return leftMs - rightMs;
|
|
2634
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
2635
|
+
});
|
|
2636
|
+
return out.slice(offset, offset + Math.min(5000, limit));
|
|
2637
|
+
};
|
|
2638
|
+
|
|
1302
2639
|
store.getArtifact = async function getArtifact({ tenantId = DEFAULT_TENANT_ID, artifactId }) {
|
|
1303
2640
|
tenantId = normalizeTenantId(tenantId);
|
|
1304
2641
|
if (typeof artifactId !== "string" || artifactId.trim() === "") throw new TypeError("artifactId is required");
|
|
@@ -1767,6 +3104,188 @@ export function createStore({ persistenceDir = null, serverSignerKeypair = null
|
|
|
1767
3104
|
return all.slice(safeOffset, safeOffset + safeLimit);
|
|
1768
3105
|
};
|
|
1769
3106
|
|
|
3107
|
+
store.appendEmergencyControlEvent = async function appendEmergencyControlEvent({ tenantId = DEFAULT_TENANT_ID, event, audit = null } = {}) {
|
|
3108
|
+
tenantId = normalizeTenantId(tenantId);
|
|
3109
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event is required");
|
|
3110
|
+
|
|
3111
|
+
const eventId = assertNonEmptyString(event.eventId ?? event.id ?? null, "event.eventId");
|
|
3112
|
+
const action = normalizeEmergencyAction(event.action ?? null);
|
|
3113
|
+
const scopeInput = event.scope && typeof event.scope === "object" && !Array.isArray(event.scope) ? event.scope : {};
|
|
3114
|
+
const scopeType = normalizeEmergencyScopeType(scopeInput.type ?? event.scopeType ?? EMERGENCY_SCOPE_TYPE.TENANT);
|
|
3115
|
+
const scopeId = normalizeEmergencyScopeId(scopeType, scopeInput.id ?? event.scopeId ?? null);
|
|
3116
|
+
const controlType =
|
|
3117
|
+
action === EMERGENCY_ACTION.RESUME
|
|
3118
|
+
? normalizeEmergencyControlType(event.controlType ?? null, { allowNull: true })
|
|
3119
|
+
: normalizeEmergencyControlType(event.controlType ?? action, { allowNull: false });
|
|
3120
|
+
const resumeControlTypes =
|
|
3121
|
+
action === EMERGENCY_ACTION.RESUME
|
|
3122
|
+
? normalizeEmergencyResumeControlTypes(event.resumeControlTypes ?? (controlType ? [controlType] : null))
|
|
3123
|
+
: [];
|
|
3124
|
+
|
|
3125
|
+
const nowAt = typeof store.nowIso === "function" ? store.nowIso() : new Date().toISOString();
|
|
3126
|
+
const effectiveAt =
|
|
3127
|
+
typeof event.effectiveAt === "string" && event.effectiveAt.trim() !== "" ? event.effectiveAt.trim() : nowAt;
|
|
3128
|
+
const createdAt = typeof event.createdAt === "string" && event.createdAt.trim() !== "" ? event.createdAt.trim() : nowAt;
|
|
3129
|
+
const normalizedEvent = {
|
|
3130
|
+
...event,
|
|
3131
|
+
schemaVersion:
|
|
3132
|
+
typeof event.schemaVersion === "string" && event.schemaVersion.trim() !== ""
|
|
3133
|
+
? event.schemaVersion.trim()
|
|
3134
|
+
: "OpsEmergencyControlEvent.v1",
|
|
3135
|
+
eventId,
|
|
3136
|
+
tenantId,
|
|
3137
|
+
action,
|
|
3138
|
+
controlType,
|
|
3139
|
+
resumeControlTypes,
|
|
3140
|
+
scope: { type: scopeType, id: scopeId },
|
|
3141
|
+
effectiveAt,
|
|
3142
|
+
createdAt
|
|
3143
|
+
};
|
|
3144
|
+
|
|
3145
|
+
await store.commitTx({ at: effectiveAt, ops: [{ kind: "EMERGENCY_CONTROL_EVENT_APPEND", tenantId, event: normalizedEvent }], audit });
|
|
3146
|
+
return store.emergencyControlEvents.get(makeScopedKey({ tenantId, id: eventId })) ?? normalizedEvent;
|
|
3147
|
+
};
|
|
3148
|
+
|
|
3149
|
+
store.listEmergencyControlEvents = async function listEmergencyControlEvents({
|
|
3150
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
3151
|
+
action = null,
|
|
3152
|
+
scopeType = null,
|
|
3153
|
+
scopeId = null,
|
|
3154
|
+
controlType = null,
|
|
3155
|
+
limit = 200,
|
|
3156
|
+
offset = 0
|
|
3157
|
+
} = {}) {
|
|
3158
|
+
tenantId = normalizeTenantId(tenantId);
|
|
3159
|
+
if (action !== null) action = normalizeEmergencyAction(action);
|
|
3160
|
+
if (scopeType !== null) scopeType = normalizeEmergencyScopeType(scopeType);
|
|
3161
|
+
if (controlType !== null) controlType = normalizeEmergencyControlType(controlType, { allowNull: false });
|
|
3162
|
+
if (scopeId !== null && (typeof scopeId !== "string" || scopeId.trim() === "")) {
|
|
3163
|
+
throw new TypeError("scopeId must be null or a non-empty string");
|
|
3164
|
+
}
|
|
3165
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
3166
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
3167
|
+
const safeLimit = Math.min(1000, limit);
|
|
3168
|
+
const safeOffset = offset;
|
|
3169
|
+
|
|
3170
|
+
const out = [];
|
|
3171
|
+
for (const row of store.emergencyControlEvents.values()) {
|
|
3172
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
3173
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
3174
|
+
if (action !== null && String(row.action ?? "").toLowerCase() !== action) continue;
|
|
3175
|
+
const rowScope = row.scope && typeof row.scope === "object" && !Array.isArray(row.scope) ? row.scope : {};
|
|
3176
|
+
if (scopeType !== null && String(rowScope.type ?? "").toLowerCase() !== scopeType) continue;
|
|
3177
|
+
if (scopeId !== null && String(rowScope.id ?? "") !== scopeId) continue;
|
|
3178
|
+
if (controlType !== null && String(row.controlType ?? "").toLowerCase() !== controlType) continue;
|
|
3179
|
+
out.push(row);
|
|
3180
|
+
}
|
|
3181
|
+
out.sort((left, right) => {
|
|
3182
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.effectiveAt ?? ""))) ? Date.parse(String(left.effectiveAt)) : Number.NaN;
|
|
3183
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.effectiveAt ?? ""))) ? Date.parse(String(right.effectiveAt)) : Number.NaN;
|
|
3184
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
|
|
3185
|
+
return String(left?.eventId ?? "").localeCompare(String(right?.eventId ?? ""));
|
|
3186
|
+
});
|
|
3187
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
3188
|
+
};
|
|
3189
|
+
|
|
3190
|
+
store.listEmergencyControlState = async function listEmergencyControlState({
|
|
3191
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
3192
|
+
active = null,
|
|
3193
|
+
scopeType = null,
|
|
3194
|
+
scopeId = null,
|
|
3195
|
+
controlType = null,
|
|
3196
|
+
limit = 200,
|
|
3197
|
+
offset = 0
|
|
3198
|
+
} = {}) {
|
|
3199
|
+
tenantId = normalizeTenantId(tenantId);
|
|
3200
|
+
if (active !== null && typeof active !== "boolean") throw new TypeError("active must be null or boolean");
|
|
3201
|
+
if (scopeType !== null) scopeType = normalizeEmergencyScopeType(scopeType);
|
|
3202
|
+
if (controlType !== null) controlType = normalizeEmergencyControlType(controlType, { allowNull: false });
|
|
3203
|
+
if (scopeId !== null && (typeof scopeId !== "string" || scopeId.trim() === "")) {
|
|
3204
|
+
throw new TypeError("scopeId must be null or a non-empty string");
|
|
3205
|
+
}
|
|
3206
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new TypeError("limit must be a positive safe integer");
|
|
3207
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new TypeError("offset must be a non-negative safe integer");
|
|
3208
|
+
const safeLimit = Math.min(1000, limit);
|
|
3209
|
+
const safeOffset = offset;
|
|
3210
|
+
|
|
3211
|
+
const out = [];
|
|
3212
|
+
for (const row of store.emergencyControlState.values()) {
|
|
3213
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
3214
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
3215
|
+
if (active !== null && Boolean(row.active) !== active) continue;
|
|
3216
|
+
if (scopeType !== null && String(row.scopeType ?? "").toLowerCase() !== scopeType) continue;
|
|
3217
|
+
if (scopeId !== null && String(row.scopeId ?? "") !== scopeId) continue;
|
|
3218
|
+
if (controlType !== null && String(row.controlType ?? "").toLowerCase() !== controlType) continue;
|
|
3219
|
+
out.push(row);
|
|
3220
|
+
}
|
|
3221
|
+
out.sort((left, right) => {
|
|
3222
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
3223
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
3224
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
|
|
3225
|
+
const leftScope = `${String(left?.scopeType ?? "")}:${String(left?.scopeId ?? "")}`;
|
|
3226
|
+
const rightScope = `${String(right?.scopeType ?? "")}:${String(right?.scopeId ?? "")}`;
|
|
3227
|
+
const scopeOrder = leftScope.localeCompare(rightScope);
|
|
3228
|
+
if (scopeOrder !== 0) return scopeOrder;
|
|
3229
|
+
return String(left?.controlType ?? "").localeCompare(String(right?.controlType ?? ""));
|
|
3230
|
+
});
|
|
3231
|
+
return out.slice(safeOffset, safeOffset + safeLimit);
|
|
3232
|
+
};
|
|
3233
|
+
|
|
3234
|
+
store.findEmergencyControlBlock = async function findEmergencyControlBlock({
|
|
3235
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
3236
|
+
controlTypes = null,
|
|
3237
|
+
agentIds = [],
|
|
3238
|
+
adapterIds = []
|
|
3239
|
+
} = {}) {
|
|
3240
|
+
tenantId = normalizeTenantId(tenantId);
|
|
3241
|
+
const allowedControlTypes = controlTypes === null ? null : new Set(normalizeEmergencyResumeControlTypes(controlTypes));
|
|
3242
|
+
const normalizedAgentIds = new Set((Array.isArray(agentIds) ? agentIds : []).map((value) => String(value ?? "").trim()).filter(Boolean));
|
|
3243
|
+
const normalizedAdapterIds = new Set((Array.isArray(adapterIds) ? adapterIds : []).map((value) => String(value ?? "").trim()).filter(Boolean));
|
|
3244
|
+
const controlPriority = new Map([
|
|
3245
|
+
[EMERGENCY_CONTROL_TYPE.KILL_SWITCH, 0],
|
|
3246
|
+
[EMERGENCY_CONTROL_TYPE.REVOKE, 1],
|
|
3247
|
+
[EMERGENCY_CONTROL_TYPE.QUARANTINE, 2],
|
|
3248
|
+
[EMERGENCY_CONTROL_TYPE.PAUSE, 3]
|
|
3249
|
+
]);
|
|
3250
|
+
const scopePriority = new Map([
|
|
3251
|
+
[EMERGENCY_SCOPE_TYPE.AGENT, 0],
|
|
3252
|
+
[EMERGENCY_SCOPE_TYPE.ADAPTER, 1],
|
|
3253
|
+
[EMERGENCY_SCOPE_TYPE.TENANT, 2]
|
|
3254
|
+
]);
|
|
3255
|
+
|
|
3256
|
+
const matches = [];
|
|
3257
|
+
for (const row of store.emergencyControlState.values()) {
|
|
3258
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
3259
|
+
if (normalizeTenantId(row.tenantId ?? DEFAULT_TENANT_ID) !== tenantId) continue;
|
|
3260
|
+
if (row.active !== true) continue;
|
|
3261
|
+
const rowControlType = normalizeEmergencyControlType(row.controlType ?? null, { allowNull: false });
|
|
3262
|
+
if (allowedControlTypes && !allowedControlTypes.has(rowControlType)) continue;
|
|
3263
|
+
const rowScopeType = normalizeEmergencyScopeType(row.scopeType ?? null);
|
|
3264
|
+
const rowScopeId = row.scopeId === null || row.scopeId === undefined ? null : String(row.scopeId);
|
|
3265
|
+
if (rowScopeType === EMERGENCY_SCOPE_TYPE.AGENT && !normalizedAgentIds.has(String(rowScopeId ?? ""))) continue;
|
|
3266
|
+
if (rowScopeType === EMERGENCY_SCOPE_TYPE.ADAPTER && !normalizedAdapterIds.has(String(rowScopeId ?? ""))) continue;
|
|
3267
|
+
matches.push({
|
|
3268
|
+
...row,
|
|
3269
|
+
controlType: rowControlType,
|
|
3270
|
+
scopeType: rowScopeType,
|
|
3271
|
+
scopeId: rowScopeId
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
matches.sort((left, right) => {
|
|
3275
|
+
const leftControl = controlPriority.get(left.controlType) ?? 100;
|
|
3276
|
+
const rightControl = controlPriority.get(right.controlType) ?? 100;
|
|
3277
|
+
if (leftControl !== rightControl) return leftControl - rightControl;
|
|
3278
|
+
const leftScope = scopePriority.get(left.scopeType) ?? 100;
|
|
3279
|
+
const rightScope = scopePriority.get(right.scopeType) ?? 100;
|
|
3280
|
+
if (leftScope !== rightScope) return leftScope - rightScope;
|
|
3281
|
+
const leftMs = Number.isFinite(Date.parse(String(left?.updatedAt ?? ""))) ? Date.parse(String(left.updatedAt)) : Number.NaN;
|
|
3282
|
+
const rightMs = Number.isFinite(Date.parse(String(right?.updatedAt ?? ""))) ? Date.parse(String(right.updatedAt)) : Number.NaN;
|
|
3283
|
+
if (Number.isFinite(leftMs) && Number.isFinite(rightMs) && rightMs !== leftMs) return rightMs - leftMs;
|
|
3284
|
+
return String(left?.lastEventId ?? "").localeCompare(String(right?.lastEventId ?? ""));
|
|
3285
|
+
});
|
|
3286
|
+
return matches[0] ?? null;
|
|
3287
|
+
};
|
|
3288
|
+
|
|
1770
3289
|
store.getSignerKey = async function getSignerKey({ tenantId = DEFAULT_TENANT_ID, keyId } = {}) {
|
|
1771
3290
|
tenantId = normalizeTenantId(tenantId);
|
|
1772
3291
|
if (typeof keyId !== "string" || keyId.trim() === "") throw new TypeError("keyId is required");
|