@fased/fased 0.1.62 → 0.1.64
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/CHANGELOG.md +32 -0
- package/README.md +38 -30
- package/config/dependency-ownership.json +1 -1
- package/config/signer-policies/README.md +49 -0
- package/config/signer-policies/agent.json.template +32 -0
- package/config/signer-policies/mining.json.template +69 -0
- package/config/signer-policies/network.json.template +5 -0
- package/config/signer-policies/vault.json.template +70 -0
- package/config/signer-protocol-v2.json +76 -0
- package/config/typescript-noemit-baseline.json +680 -0
- package/dist/agents/auth-profiles/store.js +267 -31
- package/dist/agents/bash-tools.exec.js +10 -4
- package/dist/agents/current-model-catalog.js +3 -3
- package/dist/agents/memory-search.js +7 -1
- package/dist/agents/model-catalog-access.js +1 -2
- package/dist/agents/model-catalog-snapshot.js +1 -1
- package/dist/agents/model-catalog-source.js +1 -1
- package/dist/agents/model-metadata.js +1 -1
- package/dist/agents/model-selection.js +2 -28
- package/dist/agents/models-config.providers.js +6 -6
- package/dist/agents/pi-embedded-runner/compact.js +15 -7
- package/dist/agents/pi-embedded-runner/model.js +1 -1
- package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
- package/dist/agents/pi-embedded-runner/run.js +2 -0
- package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
- package/dist/agents/pi-tool-definition-adapter.js +16 -1
- package/dist/agents/pi-tools.js +2 -1
- package/dist/agents/sandbox/config.js +1 -1
- package/dist/agents/sandbox/context.js +27 -2
- package/dist/agents/skills/env-overrides.js +38 -28
- package/dist/agents/skills/trust.js +93 -0
- package/dist/agents/skills/workspace.js +128 -0
- package/dist/agents/skills-clawhub.js +36 -11
- package/dist/agents/skills-marketplace-scan.js +49 -0
- package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
- package/dist/agents/tools/mining-tool.js +1 -1
- package/dist/agents/tools/session-status-tool.js +1 -1
- package/dist/agents/tools/wallet-action-tool.js +172 -36
- package/dist/agents/tools/wallet-skill-policy.js +65 -23
- package/dist/agents/tools/wallet-tool.js +7 -11
- package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
- package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
- package/dist/auto-reply/reply/directive-handling.model.js +2 -1
- package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
- package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
- package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
- package/dist/auto-reply/reply/model-selection.js +7 -4
- package/dist/auto-reply/reply/session-reset-model.js +4 -4
- package/dist/brand.js +1 -1
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/channel-web.js +2 -2
- package/dist/cli/gateway-cli/run.js +8 -0
- package/dist/cli/program/command-registry.js +1 -1
- package/dist/cli/program/register.onboard.js +2 -3
- package/dist/cli/program/register.wallet.js +16 -123
- package/dist/cli/providers-cli.js +1 -1
- package/dist/cli/sat-cli.js +129 -2
- package/dist/cli/update-cli/update-command.js +260 -11
- package/dist/commands/auth-choice.apply.api-providers.js +3 -3
- package/dist/commands/doctor-config-flow.js +1 -1
- package/dist/commands/doctor-legacy-config.js +2 -13
- package/dist/commands/model-picker.js +6 -6
- package/dist/commands/models/auth.js +1 -1
- package/dist/commands/onboard-auth.config-gateways.js +1 -1
- package/dist/commands/onboard-auth.credentials.js +32 -8
- package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
- package/dist/commands/onboard-non-interactive/local.js +5 -1
- package/dist/commands/wallet.js +568 -1911
- package/dist/config/config.js +2 -2
- package/dist/config/io.js +64 -2
- package/dist/config/schema.help.js +8 -7
- package/dist/config/schema.labels.js +5 -4
- package/dist/config/zod-schema.agent-runtime.js +1 -0
- package/dist/config/zod-schema.js +5 -0
- package/dist/control-ui/assets/{app-DLil9VO1.js → app-bR-fO5ty.js} +1511 -1555
- package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
- package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
- package/dist/control-ui/assets/{index-DdE_NIXo.js → index-ckCxHwjy.js} +3 -3
- package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
- package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
- package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
- package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
- package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
- package/dist/control-ui/index.html +1 -1
- package/dist/control-ui/version.json +1 -1
- package/dist/daemon/systemd-system.js +56 -50
- package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
- package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
- package/dist/extensions/sat-mining/src/sat-bond-layout.js +5 -2
- package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
- package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
- package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
- package/dist/federation/auto-connect.js +98 -8
- package/dist/federation/marketplace-capability-adapter.js +33 -7
- package/dist/federation/marketplace-delivery-outbox.js +196 -0
- package/dist/federation/marketplace-delivery.js +351 -215
- package/dist/federation/marketplace-direct-payment.js +272 -141
- package/dist/federation/marketplace-escrow.js +607 -249
- package/dist/federation/marketplace-settlement-ledger.js +352 -0
- package/dist/federation/offers.js +9 -7
- package/dist/federation/peer-auth-v2.js +529 -0
- package/dist/gateway/a2a-http.js +425 -96
- package/dist/gateway/a2a-settlement.js +259 -153
- package/dist/gateway/a2a-task-store.js +524 -0
- package/dist/gateway/auth.js +38 -2
- package/dist/gateway/config-reload.js +46 -33
- package/dist/gateway/durable-capacity.js +31 -0
- package/dist/gateway/federation-http.js +540 -135
- package/dist/gateway/federation-marketplace-run-store.js +397 -0
- package/dist/gateway/federation-marketplace.js +535 -230
- package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
- package/dist/gateway/security-path.js +1 -1
- package/dist/gateway/server-http.js +1084 -1205
- package/dist/gateway/server-methods/models.js +1 -1
- package/dist/gateway/server-methods/secrets.js +40 -0
- package/dist/gateway/server-methods.js +1 -2
- package/dist/gateway/server-reload-handlers.js +196 -66
- package/dist/gateway/server-runtime-config.js +1 -1
- package/dist/gateway/server-secrets-runtime.js +288 -0
- package/dist/gateway/server.impl.js +73 -42
- package/dist/gateway/sessions-patch.js +3 -5
- package/dist/index.js +4 -2
- package/dist/infra/fasedhub.js +64 -1
- package/dist/infra/local-source-paired-update.js +538 -0
- package/dist/infra/restart.js +41 -10
- package/dist/infra/update-runner.js +96 -0
- package/dist/light-update-status.js +11 -11
- package/dist/memory/backend-config.js +2 -1
- package/dist/memory/session-files.js +16 -2
- package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
- package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
- package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-Bwi2Oq03.js → audio-preflight-BYxAt775.js} +27 -27
- package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
- package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
- package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
- package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
- package/dist/plugin-sdk/channel-plugin-common.js +4 -4
- package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
- package/dist/plugin-sdk/{chunk-items-DzT4L4Ty.js → chunk-items-DIRHG2gJ.js} +1 -1
- package/dist/plugin-sdk/command-status.d.ts +2 -2
- package/dist/plugin-sdk/command-status.js +27 -27
- package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
- package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
- package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
- package/dist/plugin-sdk/{config-schema-BrkmtFxB.js → config-schema-BOT7kHb6.js} +6779 -3696
- package/dist/plugin-sdk/device-pair.d.ts +1 -1
- package/dist/plugin-sdk/discord.d.ts +6 -6
- package/dist/plugin-sdk/discord.js +19 -19
- package/dist/plugin-sdk/file-lock.js +35 -4
- package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
- package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
- package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
- package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
- package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
- package/dist/plugin-sdk/index.d.ts +21 -39
- package/dist/plugin-sdk/index.js +29 -29
- package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
- package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
- package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
- package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
- package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
- package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
- package/dist/plugin-sdk/{monitor-M_ta2lMc.js → monitor-BCFDaqJ-.js} +31 -31
- package/dist/plugin-sdk/{monitor-wYVncM9w.js → monitor-BgbjRiPM.js} +32 -32
- package/dist/plugin-sdk/{openresponses-http-B2GmeSt4.js → openresponses-http-DFf87SMM.js} +27 -27
- package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
- package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
- package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
- package/dist/plugin-sdk/{pairing-message-B3bHYGaw.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
- package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
- package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
- package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
- package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
- package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
- package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
- package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
- package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
- package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
- package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
- package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
- package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
- package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
- package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
- package/dist/plugin-sdk/sat-runtime.js +29 -29
- package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
- package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
- package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
- package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
- package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
- package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
- package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
- package/dist/plugin-sdk/{slack-CxE1mfzj.js → slack-BUTt8cxK.js} +39 -39
- package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
- package/dist/plugin-sdk/slack.d.ts +6 -6
- package/dist/plugin-sdk/slack.js +9 -9
- package/dist/plugin-sdk/{sticker-cache-CGzGWu00.js → sticker-cache-D0wHVqjt.js} +3 -3
- package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
- package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
- package/dist/plugin-sdk/{telegram-actions-DaEcYQ8r.js → telegram-actions-CC_qN4v0.js} +30 -30
- package/dist/plugin-sdk/telegram.d.ts +6 -6
- package/dist/plugin-sdk/telegram.js +8 -8
- package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
- package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
- package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
- package/dist/plugin-sdk/{types-vcJrC_uv.d.ts → types-C3avNytw.d.ts} +3 -4
- package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
- package/dist/plugin-sdk/{types-DFAW-n1J.d.ts → types-DWfbSAaM.d.ts} +2 -2
- package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
- package/dist/plugin-sdk/{web-CInY-lCv.js → web-BXsJ1vqe.js} +32 -32
- package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
- package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
- package/dist/plugin-sdk/whatsapp.d.ts +13 -13
- package/dist/plugin-sdk/whatsapp.js +12 -12
- package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
- package/dist/plugin-sdk/{zod-schema.providers-core-BEbNizPe.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
- package/dist/providers/refresh.js +1 -1
- package/dist/providers/registry.js +1 -1
- package/dist/secrets/apply.js +1 -1
- package/dist/secrets/runtime.js +1 -1
- package/dist/wallet/external-submission-ledger.js +376 -0
- package/dist/wallet/legacy-embedded-keystore.js +40 -0
- package/dist/wallet/local-socket-signer-archive.js +30 -0
- package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
- package/dist/wallet/local-socket-signer-policy.js +166 -0
- package/dist/wallet/local-socket-signer-protocol.js +561 -118
- package/dist/wallet/native-signer-wallet-id.js +25 -0
- package/dist/wallet/providers/alchemy-adapter.js +3 -0
- package/dist/wallet/providers/local-socket-signer-adapter.js +380 -105
- package/dist/wallet/providers/turnkey-adapter.js +292 -34
- package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
- package/dist/wallet/signer-network-admin.js +184 -0
- package/dist/wallet/signer-protocol-v2.generated.js +90 -0
- package/dist/wallet/solana-bond-signing.js +157 -69
- package/dist/wallet/solana-reviewed-transaction.js +375 -0
- package/dist/wallet/solana-swap.js +520 -64
- package/dist/wallet/solana-transaction-inspection.js +106 -4
- package/dist/wallet/solana-trigger.js +658 -320
- package/dist/wallet/turnkey-reviewed-state.js +370 -0
- package/dist/wallet/wallet-approval-auth.js +17 -28
- package/dist/wallet/wallet-atomic-state.js +53 -0
- package/dist/wallet/wallet-canary.js +0 -1
- package/dist/wallet/wallet-inbound-events.js +16 -11
- package/dist/wallet/wallet-policy-simulation.js +1 -1
- package/dist/wallet/wallet-policy.js +90 -36
- package/dist/wallet/wallet-provider-capabilities.js +28 -4
- package/dist/wallet/wallet-provider-registry.js +57 -44
- package/dist/wallet/wallet-provider-resolver.js +62 -63
- package/dist/wallet/wallet-runtime-config.js +28 -41
- package/dist/wallet/wallet-secrets-store.js +22 -0
- package/dist/wallet/wallet-send-approvals.js +1891 -589
- package/dist/wallet/wallet-send-execution-ledger.js +292 -0
- package/dist/wallet/wallet-settlement-links.js +14 -13
- package/dist/wallet/wallet-standard-review.js +353 -0
- package/dist/wallet/wallet-status.js +0 -9
- package/dist/wallet/wallet-task-ledger.js +16 -2
- package/dist/web/auto-reply/heartbeat-runner.js +1 -1
- package/dist/web/auto-reply/monitor.js +2 -2
- package/dist/wizard/host-security-capability.js +14 -23
- package/dist/wizard/onboarding.finalize.js +63 -184
- package/dist/wizard/onboarding.host-security.js +117 -1367
- package/dist/wizard/onboarding.js +172 -217
- package/dist/wizard/onboarding.wallet.js +232 -785
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +12 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +12 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +12 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/index.ts +498 -153
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
- package/extensions/sat-mining/src/audit-store.ts +50 -5
- package/extensions/sat-mining/src/claim-service.ts +85 -14
- package/extensions/sat-mining/src/config.ts +9 -0
- package/extensions/sat-mining/src/cycle-progress.ts +16 -8
- package/extensions/sat-mining/src/epoch-service.ts +9 -5
- package/extensions/sat-mining/src/gateway-runner.ts +21 -1
- package/extensions/sat-mining/src/protocol-contract.ts +45 -1
- package/extensions/sat-mining/src/recovery-service.ts +312 -0
- package/extensions/sat-mining/src/round-watcher.ts +391 -81
- package/extensions/sat-mining/src/rpc-read.ts +330 -89
- package/extensions/sat-mining/src/runtime.ts +14 -0
- package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
- package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
- package/extensions/sat-mining/src/solana-submit.ts +1199 -428
- package/extensions/sat-mining/src/submission-ledger.ts +433 -0
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +12 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +12 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +12 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +12 -0
- package/extensions/zalouser/package.json +1 -1
- package/install.sh +4955 -0
- package/package.json +23 -5
- package/scripts/fased-host-updater.mjs +1421 -0
- package/scripts/fased-host-updaterctl.mjs +186 -0
- package/scripts/fased-managed-service.sh +36 -0
- package/scripts/fased-managed-updater.mjs +3065 -156
- package/scripts/fased-signer-enroll-hosting.sh +275 -0
- package/scripts/fased-signer-network-hosting.sh +143 -0
- package/scripts/fased-signer-owner-policy.mjs +1430 -0
- package/scripts/fased-signer-policy-hosting.sh +50 -0
- package/scripts/fased-signer-policy-local.sh +26 -0
- package/scripts/hosted-release-manifest.mjs +185 -0
- package/scripts/install-fased-signerd.sh +183 -108
- package/scripts/install-managed-runtime.mjs +212 -10
- package/scripts/managed-runtime-layout.mjs +75 -3
- package/scripts/migrate-hosted-signer-v2.mjs +58 -0
- package/scripts/start-managed.sh +169 -360
- package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
- package/dist/plugin-sdk/tmp-fased-dir-CS6zsYEE.d.ts +0 -77
- package/dist/wallet/local-socket-signer-broker.js +0 -224
- package/dist/wallet/local-socket-signer-custody.js +0 -42
- package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
- package/dist/wallet/providers/privy-adapter.js +0 -80
- package/dist/wallet/wallet-custody.js +0 -1836
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { resolveStateDir } from "../config/paths.js";
|
|
2
|
+
import { resolveProcessScopedMap } from "../shared/process-scoped-map.js";
|
|
3
|
+
import { acquireFileLock, withFileLock } from "../plugin-sdk/file-lock.js";
|
|
4
|
+
import "../infra/file-lock.js";
|
|
5
|
+
import { serializeWalletState, writeWalletStateFileAtomically } from "../wallet/wallet-atomic-state.js";
|
|
6
|
+
import { emitDurableCapacityWarning } from "./durable-capacity.js";
|
|
7
|
+
import syncFs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
10
|
+
|
|
11
|
+
//#region src/gateway/a2a-task-store.ts
|
|
12
|
+
const TASK_STATUSES = new Set([
|
|
13
|
+
"queued",
|
|
14
|
+
"running",
|
|
15
|
+
"succeeded",
|
|
16
|
+
"failed",
|
|
17
|
+
"canceled"
|
|
18
|
+
]);
|
|
19
|
+
const LOCK_OPTIONS = {
|
|
20
|
+
retries: {
|
|
21
|
+
retries: 100,
|
|
22
|
+
factor: 1.15,
|
|
23
|
+
minTimeout: 10,
|
|
24
|
+
maxTimeout: 200,
|
|
25
|
+
randomize: true
|
|
26
|
+
},
|
|
27
|
+
stale: 3e4
|
|
28
|
+
};
|
|
29
|
+
const EXECUTION_LOCK_OPTIONS = {
|
|
30
|
+
retries: {
|
|
31
|
+
retries: 1,
|
|
32
|
+
factor: 1,
|
|
33
|
+
minTimeout: 10,
|
|
34
|
+
maxTimeout: 10,
|
|
35
|
+
randomize: false
|
|
36
|
+
},
|
|
37
|
+
stale: 3e4
|
|
38
|
+
};
|
|
39
|
+
const ACTIVE_EXECUTIONS = resolveProcessScopedMap(Symbol.for("fased.gateway.a2aTask.activeExecutions"));
|
|
40
|
+
const MAX_TASKS_PER_SENDER_PER_HOUR = 60;
|
|
41
|
+
const MAX_ACTIVE_TASKS_PER_SENDER = 8;
|
|
42
|
+
const MAX_RETAINED_TASKS = 5e3;
|
|
43
|
+
const MAX_TASK_FILES_ON_DISK = 1e4;
|
|
44
|
+
const MAX_PAYMENT_CHALLENGE_FILES = 1e4;
|
|
45
|
+
const MAX_PAYMENT_CLAIM_FILES = 1e5;
|
|
46
|
+
const TERMINAL_TASK_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
47
|
+
const PAYMENT_CHALLENGE_TTL_MS = 10 * 6e4;
|
|
48
|
+
function canonicalJson(value) {
|
|
49
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
50
|
+
return JSON.stringify(value);
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === "number") {
|
|
53
|
+
if (!Number.isFinite(value)) {
|
|
54
|
+
throw new Error("A2A task intent contains a non-finite number");
|
|
55
|
+
}
|
|
56
|
+
return JSON.stringify(value);
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
|
|
60
|
+
}
|
|
61
|
+
if (value && typeof value === "object") {
|
|
62
|
+
const record = value;
|
|
63
|
+
return `{${Object.keys(record).filter((key) => record[key] !== undefined).toSorted().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
64
|
+
}
|
|
65
|
+
throw new Error("A2A task intent contains an unsupported value");
|
|
66
|
+
}
|
|
67
|
+
function taskDigest(taskId) {
|
|
68
|
+
return createHash("sha256").update(taskId).digest("hex");
|
|
69
|
+
}
|
|
70
|
+
function taskDirectory(env) {
|
|
71
|
+
return path.join(resolveStateDir(env), "federation", "a2a-tasks");
|
|
72
|
+
}
|
|
73
|
+
function taskPath(taskId, env) {
|
|
74
|
+
return path.join(taskDirectory(env), `${taskDigest(taskId)}.json`);
|
|
75
|
+
}
|
|
76
|
+
function senderQuotaLockPath(senderHandle, env) {
|
|
77
|
+
const digest = createHash("sha256").update(senderHandle, "utf8").digest("hex");
|
|
78
|
+
return path.join(taskDirectory(env), ".sender-quotas", digest);
|
|
79
|
+
}
|
|
80
|
+
function taskCapacityLockPath(env) {
|
|
81
|
+
return path.join(taskDirectory(env), ".capacity");
|
|
82
|
+
}
|
|
83
|
+
function executionLockTarget(taskId, env) {
|
|
84
|
+
return path.join(taskDirectory(env), ".executions", taskDigest(taskId));
|
|
85
|
+
}
|
|
86
|
+
function paymentClaimPath(txRef, env) {
|
|
87
|
+
const digest = createHash("sha256").update(txRef, "utf8").digest("hex");
|
|
88
|
+
return path.join(taskDirectory(env), ".payment-claims", `${digest}.json`);
|
|
89
|
+
}
|
|
90
|
+
function paymentChallengeDirectory(env) {
|
|
91
|
+
return path.join(taskDirectory(env), ".payment-challenges");
|
|
92
|
+
}
|
|
93
|
+
function paymentChallengePath(taskId, env) {
|
|
94
|
+
return path.join(paymentChallengeDirectory(env), `${taskDigest(taskId)}.json`);
|
|
95
|
+
}
|
|
96
|
+
function paymentChallengeClaimLockPath(env) {
|
|
97
|
+
return path.join(paymentChallengeDirectory(env), ".claims");
|
|
98
|
+
}
|
|
99
|
+
function paymentClaimDirectory(env) {
|
|
100
|
+
return path.join(taskDirectory(env), ".payment-claims");
|
|
101
|
+
}
|
|
102
|
+
function paymentReferenceClaimLockPath(env) {
|
|
103
|
+
return path.join(paymentClaimDirectory(env), ".claims");
|
|
104
|
+
}
|
|
105
|
+
function listJsonLedgerFiles(directory) {
|
|
106
|
+
if (!syncFs.existsSync(directory)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
return syncFs.readdirSync(directory).filter((name) => /^[a-f0-9]{64}\.json$/u.test(name));
|
|
110
|
+
}
|
|
111
|
+
function requireJsonLedgerCapacity(directory, maximum, label) {
|
|
112
|
+
const used = listJsonLedgerFiles(directory).length;
|
|
113
|
+
emitDurableCapacityWarning(label, used, maximum);
|
|
114
|
+
if (used >= maximum) {
|
|
115
|
+
throw new Error(`${label} reached its safe on-disk record limit`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function pruneExpiredPaymentChallenges(env, nowMs) {
|
|
119
|
+
const directory = paymentChallengeDirectory(env);
|
|
120
|
+
for (const name of listJsonLedgerFiles(directory)) {
|
|
121
|
+
const filePath = path.join(directory, name);
|
|
122
|
+
try {
|
|
123
|
+
const raw = JSON.parse(syncFs.readFileSync(filePath, "utf8"));
|
|
124
|
+
if (!isRecord(raw) || typeof raw.taskId !== "string") {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const challenge = validatePaymentChallenge(raw, raw.taskId);
|
|
128
|
+
const expiredUnclaimed = challenge.status === "issued" && Date.parse(challenge.expiresAt) < nowMs;
|
|
129
|
+
const claimedAt = challenge.claimedAt ? Date.parse(challenge.claimedAt) : Number.NaN;
|
|
130
|
+
const orphanedClaim = challenge.status === "claimed" && !syncFs.existsSync(taskPath(challenge.taskId, env)) && Number.isFinite(claimedAt) && nowMs - claimedAt > TERMINAL_TASK_RETENTION_MS;
|
|
131
|
+
if (expiredUnclaimed || orphanedClaim) {
|
|
132
|
+
syncFs.unlinkSync(filePath);
|
|
133
|
+
}
|
|
134
|
+
} catch {}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function a2aTaskIntentDigest(intent) {
|
|
138
|
+
return `sha256:${createHash("sha256").update(canonicalJson(intent)).digest("hex")}`;
|
|
139
|
+
}
|
|
140
|
+
function isRecord(value) {
|
|
141
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
142
|
+
}
|
|
143
|
+
function isValidPaymentRecovery(value) {
|
|
144
|
+
if (!isRecord(value)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return (value.status === "refund-required" || value.status === "refunded" || value.status === "disputed") && typeof value.paymentTxRef === "string" && value.paymentTxRef.length > 0 && typeof value.reason === "string" && value.reason.length > 0 && typeof value.updatedAt === "string" && (value.refundTxRef === undefined || typeof value.refundTxRef === "string" && value.refundTxRef.length > 0) && (value.approvalRequestId === undefined || typeof value.approvalRequestId === "string" && value.approvalRequestId.length > 0);
|
|
148
|
+
}
|
|
149
|
+
function validatePaymentChallenge(value, expectedTaskId) {
|
|
150
|
+
if (!isRecord(value) || value.version !== 1 || value.taskId !== expectedTaskId || typeof value.challengeId !== "string" || !/^[0-9a-f]{64}$/u.test(value.challengeId) || typeof value.senderHandle !== "string" || !value.senderHandle.trim() || typeof value.offerId !== "string" || !value.offerId.trim() || typeof value.invoiceId !== "string" || !value.invoiceId.trim() || typeof value.receiptId !== "string" || !value.receiptId.trim() || typeof value.payerAddress !== "string" || !value.payerAddress.trim() || typeof value.payeeAddress !== "string" || !value.payeeAddress.trim() || typeof value.amount !== "number" || !Number.isSafeInteger(value.amount) || value.amount <= 0 || typeof value.currency !== "string" || !value.currency.trim() || !isRecord(value.asset) || value.asset.kind !== "native" && value.asset.kind !== "spl-token" || value.asset.address !== undefined && typeof value.asset.address !== "string" || typeof value.paymentMemo !== "string" || !/^fased:a2a-payment:v1:[0-9a-f]{64}$/u.test(value.paymentMemo) || value.status !== "issued" && value.status !== "claimed" || typeof value.issuedAt !== "string" || !Number.isFinite(Date.parse(value.issuedAt)) || typeof value.expiresAt !== "string" || !Number.isFinite(Date.parse(value.expiresAt)) || value.txRefDigest !== undefined && (typeof value.txRefDigest !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value.txRefDigest)) || value.claimedAt !== undefined && (typeof value.claimedAt !== "string" || !Number.isFinite(Date.parse(value.claimedAt)))) {
|
|
151
|
+
throw new Error("durable A2A payment challenge contains an invalid record");
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
function validateRecord(value, expectedTaskId) {
|
|
156
|
+
if (!isRecord(value) || value.version !== 2 || value.taskId !== expectedTaskId || typeof value.intentDigest !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value.intentDigest) || typeof value.accessTokenHash !== "string" || !/^[0-9a-f]{64}$/u.test(value.accessTokenHash) || typeof value.senderHandle !== "string" || typeof value.status !== "string" || !TASK_STATUSES.has(value.status) || typeof value.createdAt !== "string" || typeof value.updatedAt !== "string" || value.error !== undefined && typeof value.error !== "string" || value.paymentRecovery !== undefined && !isValidPaymentRecovery(value.paymentRecovery)) {
|
|
157
|
+
throw new Error("durable A2A task contains an invalid record");
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
function hashAccessToken(value) {
|
|
162
|
+
return createHash("sha256").update(`fased:a2a-task-access:v1:${value}`, "utf8").digest("hex");
|
|
163
|
+
}
|
|
164
|
+
function tokensMatch(expectedHash, token) {
|
|
165
|
+
const actual = Buffer.from(hashAccessToken(token), "hex");
|
|
166
|
+
const expected = Buffer.from(expectedHash, "hex");
|
|
167
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
168
|
+
}
|
|
169
|
+
function normalizeNewAccessToken(value) {
|
|
170
|
+
const token = value?.trim() ?? "";
|
|
171
|
+
if (!token) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
if (token.length < 43 || token.length > 256 || !/^[A-Za-z0-9_-]+$/u.test(token)) {
|
|
175
|
+
throw new Error("caller-provided A2A task access token must be a strong base64url secret");
|
|
176
|
+
}
|
|
177
|
+
return token;
|
|
178
|
+
}
|
|
179
|
+
function listDurableRecords(env) {
|
|
180
|
+
const dir = taskDirectory(env);
|
|
181
|
+
if (!syncFs.existsSync(dir)) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const records = [];
|
|
185
|
+
const taskFiles = syncFs.readdirSync(dir).filter((name) => /^[a-f0-9]{64}\.json$/u.test(name));
|
|
186
|
+
if (taskFiles.length > MAX_TASK_FILES_ON_DISK) {
|
|
187
|
+
throw new Error("A2A task store exceeds its safe on-disk record limit");
|
|
188
|
+
}
|
|
189
|
+
for (const name of taskFiles) {
|
|
190
|
+
try {
|
|
191
|
+
const raw = JSON.parse(syncFs.readFileSync(path.join(dir, name), "utf8"));
|
|
192
|
+
if (raw && typeof raw === "object" && typeof raw.taskId === "string") {
|
|
193
|
+
records.push(validateRecord(raw, raw.taskId));
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
return records;
|
|
198
|
+
}
|
|
199
|
+
function enforceSenderQuota(params) {
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const senderRecords = listDurableRecords(params.env).filter((record) => record.senderHandle === params.senderHandle);
|
|
202
|
+
const recent = senderRecords.filter((record) => now - Date.parse(record.createdAt) < 60 * 6e4);
|
|
203
|
+
if (recent.length >= MAX_TASKS_PER_SENDER_PER_HOUR) {
|
|
204
|
+
throw new Error("A2A sender hourly task quota exceeded");
|
|
205
|
+
}
|
|
206
|
+
const active = senderRecords.filter((record) => record.status === "queued" || record.status === "running");
|
|
207
|
+
if (active.length >= MAX_ACTIVE_TASKS_PER_SENDER) {
|
|
208
|
+
throw new Error("A2A sender active task quota exceeded");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function pruneDurableTasks(env) {
|
|
212
|
+
const now = Date.now();
|
|
213
|
+
const terminal = listDurableRecords(env).filter((record) => (record.status === "succeeded" || record.status === "failed" || record.status === "canceled") && record.paymentRecovery?.status !== "refund-required").toSorted((left, right) => Date.parse(left.updatedAt) - Date.parse(right.updatedAt));
|
|
214
|
+
const remove = terminal.filter((record, index) => now - Date.parse(record.updatedAt) > TERMINAL_TASK_RETENTION_MS || terminal.length - index > MAX_RETAINED_TASKS);
|
|
215
|
+
for (const record of remove) {
|
|
216
|
+
try {
|
|
217
|
+
syncFs.unlinkSync(taskPath(record.taskId, env));
|
|
218
|
+
} catch {}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function readDurableA2aPaymentChallenge(params) {
|
|
222
|
+
const env = params.env ?? process.env;
|
|
223
|
+
const taskId = params.taskId.trim();
|
|
224
|
+
if (!taskId) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const filePath = paymentChallengePath(taskId, env);
|
|
228
|
+
if (!syncFs.existsSync(filePath)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
return validatePaymentChallenge(JSON.parse(syncFs.readFileSync(filePath, "utf8")), taskId);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw new Error("durable A2A payment challenge is unreadable", { cause: error });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function issueDurableA2aPaymentChallenge(params) {
|
|
238
|
+
const env = params.env ?? process.env;
|
|
239
|
+
const taskId = params.taskId.trim();
|
|
240
|
+
const senderHandle = params.senderHandle.trim().toLowerCase();
|
|
241
|
+
if (!taskId || taskId.length > 240 || !senderHandle || !params.offerId.trim()) {
|
|
242
|
+
throw new Error("A2A payment challenge identity is invalid");
|
|
243
|
+
}
|
|
244
|
+
if (!Number.isSafeInteger(params.amount) || params.amount <= 0) {
|
|
245
|
+
throw new Error("A2A payment challenge amount is invalid");
|
|
246
|
+
}
|
|
247
|
+
const filePath = paymentChallengePath(taskId, env);
|
|
248
|
+
syncFs.mkdirSync(path.dirname(filePath), {
|
|
249
|
+
recursive: true,
|
|
250
|
+
mode: 448
|
|
251
|
+
});
|
|
252
|
+
return await withFileLock(paymentChallengeClaimLockPath(env), LOCK_OPTIONS, async () => {
|
|
253
|
+
return await withFileLock(filePath, LOCK_OPTIONS, async () => {
|
|
254
|
+
const existing = readDurableA2aPaymentChallenge({
|
|
255
|
+
taskId,
|
|
256
|
+
env
|
|
257
|
+
});
|
|
258
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
259
|
+
if (existing && Date.parse(existing.expiresAt) >= nowMs) {
|
|
260
|
+
if (existing.senderHandle !== senderHandle || existing.offerId !== params.offerId.trim() || existing.payerAddress !== params.payerAddress.trim() || existing.payeeAddress !== params.payeeAddress.trim() || existing.amount !== params.amount || existing.currency !== params.currency.trim().toUpperCase() || canonicalJson(existing.asset) !== canonicalJson(params.asset)) {
|
|
261
|
+
throw new Error("A2A taskId is already bound to different payment terms");
|
|
262
|
+
}
|
|
263
|
+
return existing;
|
|
264
|
+
}
|
|
265
|
+
if (existing?.status === "claimed") {
|
|
266
|
+
throw new Error("claimed A2A payment challenge cannot be replaced");
|
|
267
|
+
}
|
|
268
|
+
pruneExpiredPaymentChallenges(env, nowMs);
|
|
269
|
+
if (!existing) {
|
|
270
|
+
requireJsonLedgerCapacity(paymentChallengeDirectory(env), MAX_PAYMENT_CHALLENGE_FILES, "A2A payment challenge store");
|
|
271
|
+
}
|
|
272
|
+
const challengeId = randomBytes(32).toString("hex");
|
|
273
|
+
const issuedAt = new Date(nowMs).toISOString();
|
|
274
|
+
const record = {
|
|
275
|
+
version: 1,
|
|
276
|
+
challengeId,
|
|
277
|
+
taskId,
|
|
278
|
+
senderHandle,
|
|
279
|
+
offerId: params.offerId.trim(),
|
|
280
|
+
invoiceId: `invoice-${challengeId.slice(0, 32)}`,
|
|
281
|
+
receiptId: `receipt-${challengeId.slice(32)}`,
|
|
282
|
+
payerAddress: params.payerAddress.trim(),
|
|
283
|
+
payeeAddress: params.payeeAddress.trim(),
|
|
284
|
+
amount: params.amount,
|
|
285
|
+
currency: params.currency.trim().toUpperCase(),
|
|
286
|
+
asset: params.asset,
|
|
287
|
+
paymentMemo: `fased:a2a-payment:v1:${challengeId}`,
|
|
288
|
+
status: "issued",
|
|
289
|
+
issuedAt,
|
|
290
|
+
expiresAt: new Date(nowMs + PAYMENT_CHALLENGE_TTL_MS).toISOString()
|
|
291
|
+
};
|
|
292
|
+
writeWalletStateFileAtomically(filePath, serializeWalletState(record));
|
|
293
|
+
return record;
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async function claimDurableA2aPaymentChallenge(params) {
|
|
298
|
+
const env = params.env ?? process.env;
|
|
299
|
+
const taskId = params.taskId.trim();
|
|
300
|
+
const txRefDigest = `sha256:${createHash("sha256").update(params.txRef.trim(), "utf8").digest("hex")}`;
|
|
301
|
+
return await withFileLock(paymentChallengeClaimLockPath(env), LOCK_OPTIONS, async () => {
|
|
302
|
+
const challenge = readDurableA2aPaymentChallenge({
|
|
303
|
+
taskId,
|
|
304
|
+
env
|
|
305
|
+
});
|
|
306
|
+
if (!challenge || challenge.challengeId !== params.challengeId.trim() || challenge.senderHandle !== params.senderHandle.trim().toLowerCase() || challenge.payerAddress !== params.payerAddress.trim()) {
|
|
307
|
+
throw new Error("A2A payment challenge identity does not match the verified settlement");
|
|
308
|
+
}
|
|
309
|
+
const challengeFiles = syncFs.existsSync(paymentChallengeDirectory(env)) ? syncFs.readdirSync(paymentChallengeDirectory(env)).filter((name) => /^[a-f0-9]{64}\.json$/u.test(name)) : [];
|
|
310
|
+
for (const name of challengeFiles) {
|
|
311
|
+
const raw = JSON.parse(syncFs.readFileSync(path.join(paymentChallengeDirectory(env), name), "utf8"));
|
|
312
|
+
if (isRecord(raw) && raw.txRefDigest === txRefDigest && raw.taskId !== taskId) {
|
|
313
|
+
throw new Error("payment transaction was already claimed by a different A2A task");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (challenge.status === "claimed") {
|
|
317
|
+
if (challenge.txRefDigest !== txRefDigest) {
|
|
318
|
+
throw new Error("A2A payment challenge was already claimed by a different transaction");
|
|
319
|
+
}
|
|
320
|
+
return challenge;
|
|
321
|
+
}
|
|
322
|
+
const claimed = {
|
|
323
|
+
...challenge,
|
|
324
|
+
status: "claimed",
|
|
325
|
+
txRefDigest,
|
|
326
|
+
claimedAt: new Date().toISOString()
|
|
327
|
+
};
|
|
328
|
+
writeWalletStateFileAtomically(paymentChallengePath(taskId, env), serializeWalletState(claimed));
|
|
329
|
+
return claimed;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function authorizeDurableA2aTask(params) {
|
|
333
|
+
const record = readDurableA2aTask({
|
|
334
|
+
taskId: params.taskId,
|
|
335
|
+
env: params.env
|
|
336
|
+
});
|
|
337
|
+
const token = params.accessToken.trim();
|
|
338
|
+
if (!record || !token || !tokensMatch(record.accessTokenHash, token)) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
return record;
|
|
342
|
+
}
|
|
343
|
+
function readDurableA2aTask(params) {
|
|
344
|
+
const taskId = params.taskId.trim();
|
|
345
|
+
if (!taskId) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
const filePath = taskPath(taskId, params.env ?? process.env);
|
|
349
|
+
if (!syncFs.existsSync(filePath)) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
return validateRecord(JSON.parse(syncFs.readFileSync(filePath, "utf8")), taskId);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
throw new Error("durable A2A task is unreadable; refusing to replace it", { cause: error });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function writeRecord(record, env) {
|
|
359
|
+
syncFs.mkdirSync(taskDirectory(env), {
|
|
360
|
+
recursive: true,
|
|
361
|
+
mode: 448
|
|
362
|
+
});
|
|
363
|
+
writeWalletStateFileAtomically(taskPath(record.taskId, env), serializeWalletState(record));
|
|
364
|
+
}
|
|
365
|
+
async function claimDurableA2aPaymentReference(params) {
|
|
366
|
+
const env = params.env ?? process.env;
|
|
367
|
+
const taskId = params.taskId.trim();
|
|
368
|
+
const txRef = params.txRef.trim();
|
|
369
|
+
if (!taskId || !txRef) {
|
|
370
|
+
throw new Error("A2A payment claim requires taskId and txRef");
|
|
371
|
+
}
|
|
372
|
+
const claimPath = paymentClaimPath(txRef, env);
|
|
373
|
+
await withFileLock(paymentReferenceClaimLockPath(env), LOCK_OPTIONS, async () => {
|
|
374
|
+
await withFileLock(claimPath, LOCK_OPTIONS, async () => {
|
|
375
|
+
if (syncFs.existsSync(claimPath)) {
|
|
376
|
+
const existing = JSON.parse(syncFs.readFileSync(claimPath, "utf8"));
|
|
377
|
+
if (!isRecord(existing) || existing.taskId !== taskId) {
|
|
378
|
+
throw new Error("payment transaction was already claimed by a different A2A task");
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const claimDir = path.dirname(claimPath);
|
|
383
|
+
syncFs.mkdirSync(claimDir, {
|
|
384
|
+
recursive: true,
|
|
385
|
+
mode: 448
|
|
386
|
+
});
|
|
387
|
+
requireJsonLedgerCapacity(claimDir, MAX_PAYMENT_CLAIM_FILES, "A2A payment transaction claim store");
|
|
388
|
+
writeWalletStateFileAtomically(claimPath, serializeWalletState({
|
|
389
|
+
version: 1,
|
|
390
|
+
taskId,
|
|
391
|
+
txRefDigest: `sha256:${createHash("sha256").update(txRef, "utf8").digest("hex")}`,
|
|
392
|
+
createdAt: new Date().toISOString()
|
|
393
|
+
}));
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
async function reserveDurableA2aTask(params) {
|
|
398
|
+
const env = params.env ?? process.env;
|
|
399
|
+
const taskId = params.taskId.trim();
|
|
400
|
+
if (!taskId || taskId.length > 240) {
|
|
401
|
+
throw new Error("A2A taskId is invalid");
|
|
402
|
+
}
|
|
403
|
+
const intentDigest = a2aTaskIntentDigest({
|
|
404
|
+
taskId,
|
|
405
|
+
senderHandle: params.senderHandle.trim(),
|
|
406
|
+
input: params.input,
|
|
407
|
+
marketplacePayment: params.marketplacePayment
|
|
408
|
+
});
|
|
409
|
+
syncFs.mkdirSync(taskDirectory(env), {
|
|
410
|
+
recursive: true,
|
|
411
|
+
mode: 448
|
|
412
|
+
});
|
|
413
|
+
const senderHandle = params.senderHandle.trim();
|
|
414
|
+
return await withFileLock(taskCapacityLockPath(env), LOCK_OPTIONS, async () => {
|
|
415
|
+
pruneDurableTasks(env);
|
|
416
|
+
return await withFileLock(senderQuotaLockPath(senderHandle, env), LOCK_OPTIONS, async () => {
|
|
417
|
+
return await withFileLock(taskPath(taskId, env), LOCK_OPTIONS, async () => {
|
|
418
|
+
const existing = readDurableA2aTask({
|
|
419
|
+
taskId,
|
|
420
|
+
env
|
|
421
|
+
});
|
|
422
|
+
if (existing) {
|
|
423
|
+
if (existing.intentDigest !== intentDigest) {
|
|
424
|
+
throw new Error("A2A taskId is already bound to a different immutable task intent");
|
|
425
|
+
}
|
|
426
|
+
if (!params.accessToken || !tokensMatch(existing.accessTokenHash, params.accessToken)) {
|
|
427
|
+
throw new Error("A2A taskId already exists and requires its task access token");
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
record: existing,
|
|
431
|
+
created: false
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
enforceSenderQuota({
|
|
435
|
+
senderHandle,
|
|
436
|
+
env
|
|
437
|
+
});
|
|
438
|
+
requireJsonLedgerCapacity(taskDirectory(env), MAX_TASK_FILES_ON_DISK, "A2A task store");
|
|
439
|
+
const now = new Date().toISOString();
|
|
440
|
+
const accessToken = normalizeNewAccessToken(params.accessToken) ?? randomBytes(32).toString("base64url");
|
|
441
|
+
const record = {
|
|
442
|
+
version: 2,
|
|
443
|
+
taskId,
|
|
444
|
+
intentDigest,
|
|
445
|
+
accessTokenHash: hashAccessToken(accessToken),
|
|
446
|
+
senderHandle,
|
|
447
|
+
input: params.input,
|
|
448
|
+
...params.marketplacePayment !== undefined ? { marketplacePayment: params.marketplacePayment } : {},
|
|
449
|
+
status: "queued",
|
|
450
|
+
createdAt: now,
|
|
451
|
+
updatedAt: now
|
|
452
|
+
};
|
|
453
|
+
writeRecord(record, env);
|
|
454
|
+
return {
|
|
455
|
+
record,
|
|
456
|
+
created: true,
|
|
457
|
+
accessToken
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
async function updateDurableA2aTask(params) {
|
|
464
|
+
const env = params.env ?? process.env;
|
|
465
|
+
const taskId = params.taskId.trim();
|
|
466
|
+
return await withFileLock(taskPath(taskId, env), LOCK_OPTIONS, async () => {
|
|
467
|
+
const record = readDurableA2aTask({
|
|
468
|
+
taskId,
|
|
469
|
+
env
|
|
470
|
+
});
|
|
471
|
+
if (!record) {
|
|
472
|
+
throw new Error("durable A2A task does not exist");
|
|
473
|
+
}
|
|
474
|
+
if (params.expectedStatuses && !params.expectedStatuses.includes(record.status)) {
|
|
475
|
+
throw new Error(`durable A2A task is ${record.status}; expected ${params.expectedStatuses.join(" or ")}`);
|
|
476
|
+
}
|
|
477
|
+
if (params.status) {
|
|
478
|
+
record.status = params.status;
|
|
479
|
+
}
|
|
480
|
+
if (params.output !== undefined) {
|
|
481
|
+
record.output = params.output;
|
|
482
|
+
}
|
|
483
|
+
if (params.error !== undefined) {
|
|
484
|
+
record.error = params.error;
|
|
485
|
+
}
|
|
486
|
+
if (params.settlement !== undefined) {
|
|
487
|
+
record.settlement = params.settlement;
|
|
488
|
+
}
|
|
489
|
+
if (params.paymentRecovery !== undefined) {
|
|
490
|
+
record.paymentRecovery = params.paymentRecovery;
|
|
491
|
+
}
|
|
492
|
+
record.updatedAt = new Date().toISOString();
|
|
493
|
+
writeRecord(record, env);
|
|
494
|
+
return record;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
async function claimDurableA2aTaskExecution(params) {
|
|
498
|
+
const env = params.env ?? process.env;
|
|
499
|
+
const taskId = params.taskId.trim();
|
|
500
|
+
const key = `${taskDirectory(env)}\0${taskId}`;
|
|
501
|
+
if (ACTIVE_EXECUTIONS.has(key)) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
ACTIVE_EXECUTIONS.set(key, true);
|
|
505
|
+
let lock;
|
|
506
|
+
try {
|
|
507
|
+
lock = await acquireFileLock(executionLockTarget(taskId, env), EXECUTION_LOCK_OPTIONS);
|
|
508
|
+
} catch {
|
|
509
|
+
ACTIVE_EXECUTIONS.delete(key);
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
let released = false;
|
|
513
|
+
return async () => {
|
|
514
|
+
if (released) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
released = true;
|
|
518
|
+
ACTIVE_EXECUTIONS.delete(key);
|
|
519
|
+
await lock.release();
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
//#endregion
|
|
524
|
+
export { authorizeDurableA2aTask, claimDurableA2aPaymentChallenge, claimDurableA2aTaskExecution, issueDurableA2aPaymentChallenge, readDurableA2aPaymentChallenge, readDurableA2aTask, reserveDurableA2aTask, updateDurableA2aTask };
|
package/dist/gateway/auth.js
CHANGED
|
@@ -54,6 +54,19 @@ function isLocalDirectRequest(req, trustedProxies, allowRealIpFallback) {
|
|
|
54
54
|
}
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns true only when the peer itself is loopback and the request carries
|
|
59
|
+
* no proxy-origin headers. This stricter predicate is required for auth mode
|
|
60
|
+
* "none"; configured proxies must never turn an unauthenticated request into
|
|
61
|
+
* a local one.
|
|
62
|
+
*/
|
|
63
|
+
function isDirectLoopbackRequest(req, trustedProxies) {
|
|
64
|
+
if (!req) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const remoteAddr = req.socket?.remoteAddress;
|
|
68
|
+
return isLoopbackAddress(remoteAddr) && !isTrustedProxyAddress(remoteAddr, trustedProxies) && !hasForwardedProxyHeaders(req);
|
|
69
|
+
}
|
|
57
70
|
function getTailscaleUser(req) {
|
|
58
71
|
if (!req) {
|
|
59
72
|
return null;
|
|
@@ -81,7 +94,16 @@ function hasForwardedProxyHeaders(req) {
|
|
|
81
94
|
if (!req) {
|
|
82
95
|
return false;
|
|
83
96
|
}
|
|
84
|
-
return
|
|
97
|
+
return [
|
|
98
|
+
"forwarded",
|
|
99
|
+
"via",
|
|
100
|
+
"x-forwarded-for",
|
|
101
|
+
"x-forwarded-host",
|
|
102
|
+
"x-forwarded-port",
|
|
103
|
+
"x-forwarded-proto",
|
|
104
|
+
"x-forwarded-server",
|
|
105
|
+
"x-real-ip"
|
|
106
|
+
].some((header) => Object.prototype.hasOwnProperty.call(req.headers, header));
|
|
85
107
|
}
|
|
86
108
|
function isTailscaleProxyRequest(req) {
|
|
87
109
|
if (!req) {
|
|
@@ -270,6 +292,20 @@ async function authorizeGatewayConnect(params) {
|
|
|
270
292
|
};
|
|
271
293
|
}
|
|
272
294
|
}
|
|
295
|
+
if (auth.mode === "none") {
|
|
296
|
+
if (!auth.allowTailscale && isDirectLoopbackRequest(req, trustedProxies)) {
|
|
297
|
+
limiter?.reset(ip, rateLimitScope);
|
|
298
|
+
return {
|
|
299
|
+
ok: true,
|
|
300
|
+
method: "none"
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
304
|
+
return {
|
|
305
|
+
ok: false,
|
|
306
|
+
reason: "unauthorized"
|
|
307
|
+
};
|
|
308
|
+
}
|
|
273
309
|
let sessionAuthResolved;
|
|
274
310
|
const trySessionTokenAuth = async () => {
|
|
275
311
|
if (sessionAuthResolved !== undefined) {
|
|
@@ -384,4 +420,4 @@ async function authorizeGatewayConnect(params) {
|
|
|
384
420
|
}
|
|
385
421
|
|
|
386
422
|
//#endregion
|
|
387
|
-
export { assertGatewayAuthConfigured, authorizeGatewayConnect, isLocalDirectRequest, resolveGatewayAuth };
|
|
423
|
+
export { assertGatewayAuthConfigured, authorizeGatewayConnect, isDirectLoopbackRequest, isLocalDirectRequest, resolveGatewayAuth };
|