@bolloon/bolloon-agent 0.4.28 → 0.4.29
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/bin/bolloon.cjs +18 -148
- package/bin/bolloon.cmd +6 -3
- package/dist/agents/pi-sdk-tools.js +3 -1
- package/dist/agents/pi-sdk.js +4 -1
- package/dist/cli/update-commands.js +349 -0
- package/dist/cli-entry.js +41 -55
- package/dist/electron-build/agents/agent-gateway.js +169 -0
- package/dist/electron-build/agents/agent-gateway.js.map +1 -0
- package/dist/electron-build/agents/agent-identity-store.js +212 -0
- package/dist/electron-build/agents/agent-identity-store.js.map +1 -0
- package/dist/electron-build/agents/agent-identity.js +196 -0
- package/dist/electron-build/agents/agent-identity.js.map +1 -0
- package/dist/electron-build/agents/agent-manifest-protocol.js +151 -0
- package/dist/electron-build/agents/agent-manifest-protocol.js.map +1 -0
- package/dist/electron-build/agents/agent-registry.js +217 -0
- package/dist/electron-build/agents/agent-registry.js.map +1 -0
- package/dist/electron-build/agents/agent-reputation.js +60 -0
- package/dist/electron-build/agents/agent-reputation.js.map +1 -0
- package/dist/electron-build/agents/agent-service-client.js +186 -0
- package/dist/electron-build/agents/agent-service-client.js.map +1 -0
- package/dist/electron-build/agents/browser-cdp.js +765 -0
- package/dist/electron-build/agents/browser-cdp.js.map +1 -0
- package/dist/electron-build/agents/computer-use.js +341 -0
- package/dist/electron-build/agents/computer-use.js.map +1 -0
- package/dist/electron-build/agents/constraint-layer.js +210 -0
- package/dist/electron-build/agents/constraint-layer.js.map +1 -0
- package/dist/electron-build/agents/decision-store.js +292 -0
- package/dist/electron-build/agents/decision-store.js.map +1 -0
- package/dist/electron-build/agents/deny-pipeline.js +125 -0
- package/dist/electron-build/agents/deny-pipeline.js.map +1 -0
- package/dist/electron-build/agents/economic-policy.js +172 -0
- package/dist/electron-build/agents/economic-policy.js.map +1 -0
- package/dist/electron-build/agents/error-classifier.js +125 -0
- package/dist/electron-build/agents/error-classifier.js.map +1 -0
- package/dist/electron-build/agents/execution-supervisor.js +539 -0
- package/dist/electron-build/agents/execution-supervisor.js.map +1 -0
- package/dist/electron-build/agents/external-events.js +171 -0
- package/dist/electron-build/agents/external-events.js.map +1 -0
- package/dist/electron-build/agents/gateway-join.js +315 -0
- package/dist/electron-build/agents/gateway-join.js.map +1 -0
- package/dist/electron-build/agents/gateway-network.js +505 -0
- package/dist/electron-build/agents/gateway-network.js.map +1 -0
- package/dist/electron-build/agents/goal-criteria.js +133 -0
- package/dist/electron-build/agents/goal-criteria.js.map +1 -0
- package/dist/electron-build/agents/goal-resume.js +364 -0
- package/dist/electron-build/agents/goal-resume.js.map +1 -0
- package/dist/electron-build/agents/goal-store.js +587 -0
- package/dist/electron-build/agents/goal-store.js.map +1 -0
- package/dist/electron-build/agents/intent-classifier.js +104 -0
- package/dist/electron-build/agents/intent-classifier.js.map +1 -0
- package/dist/electron-build/agents/iroh-secret.js +70 -0
- package/dist/electron-build/agents/iroh-secret.js.map +1 -0
- package/dist/electron-build/agents/loop-review.js +101 -0
- package/dist/electron-build/agents/loop-review.js.map +1 -0
- package/dist/electron-build/agents/memory-recall.js +154 -0
- package/dist/electron-build/agents/memory-recall.js.map +1 -0
- package/dist/electron-build/agents/p2p-document-tools.js +328 -0
- package/dist/electron-build/agents/p2p-document-tools.js.map +1 -0
- package/dist/electron-build/agents/parse-tool-call.js +564 -0
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -0
- package/dist/electron-build/agents/patch-tool.js +205 -0
- package/dist/electron-build/agents/patch-tool.js.map +1 -0
- package/dist/electron-build/agents/payment-approval.js +183 -0
- package/dist/electron-build/agents/payment-approval.js.map +1 -0
- package/dist/electron-build/agents/payment-gate.js +139 -0
- package/dist/electron-build/agents/payment-gate.js.map +1 -0
- package/dist/electron-build/agents/permission-mode.js +124 -0
- package/dist/electron-build/agents/permission-mode.js.map +1 -0
- package/dist/electron-build/agents/pi-harness.js +268 -0
- package/dist/electron-build/agents/pi-harness.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk-session-factory.js +117 -0
- package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk-session-manager.js +335 -0
- package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk-tools.js +4042 -0
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk-types.js +17 -0
- package/dist/electron-build/agents/pi-sdk-types.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk.js +3325 -0
- package/dist/electron-build/agents/pi-sdk.js.map +1 -0
- package/dist/electron-build/agents/plan-store.js +212 -0
- package/dist/electron-build/agents/plan-store.js.map +1 -0
- package/dist/electron-build/agents/pre-tool-validator.js +200 -0
- package/dist/electron-build/agents/pre-tool-validator.js.map +1 -0
- package/dist/electron-build/agents/process-runner.js +148 -0
- package/dist/electron-build/agents/process-runner.js.map +1 -0
- package/dist/electron-build/agents/react-loop.js +131 -0
- package/dist/electron-build/agents/react-loop.js.map +1 -0
- package/dist/electron-build/agents/resource-store.js +180 -0
- package/dist/electron-build/agents/resource-store.js.map +1 -0
- package/dist/electron-build/agents/resource-token.js +118 -0
- package/dist/electron-build/agents/resource-token.js.map +1 -0
- package/dist/electron-build/agents/resource-wallet.js +84 -0
- package/dist/electron-build/agents/resource-wallet.js.map +1 -0
- package/dist/electron-build/agents/run-store.js +854 -0
- package/dist/electron-build/agents/run-store.js.map +1 -0
- package/dist/electron-build/agents/runner-resolver.js +267 -0
- package/dist/electron-build/agents/runner-resolver.js.map +1 -0
- package/dist/electron-build/agents/session-store.js +294 -0
- package/dist/electron-build/agents/session-store.js.map +1 -0
- package/dist/electron-build/agents/shell-guard.js +465 -0
- package/dist/electron-build/agents/shell-guard.js.map +1 -0
- package/dist/electron-build/agents/shell-tool.js +102 -0
- package/dist/electron-build/agents/shell-tool.js.map +1 -0
- package/dist/electron-build/agents/skill-loader.js +216 -0
- package/dist/electron-build/agents/skill-loader.js.map +1 -0
- package/dist/electron-build/agents/skill-readiness.js +174 -0
- package/dist/electron-build/agents/skill-readiness.js.map +1 -0
- package/dist/electron-build/agents/skill-share.js +394 -0
- package/dist/electron-build/agents/skill-share.js.map +1 -0
- package/dist/electron-build/agents/skill-supervisor-link.js +75 -0
- package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -0
- package/dist/electron-build/agents/skill-writer.js +347 -0
- package/dist/electron-build/agents/skill-writer.js.map +1 -0
- package/dist/electron-build/agents/skills-manager.js +763 -0
- package/dist/electron-build/agents/skills-manager.js.map +1 -0
- package/dist/electron-build/agents/supervisor-host.js +294 -0
- package/dist/electron-build/agents/supervisor-host.js.map +1 -0
- package/dist/electron-build/agents/task/local-seller.js +188 -0
- package/dist/electron-build/agents/task/local-seller.js.map +1 -0
- package/dist/electron-build/agents/task/report-card.js +152 -0
- package/dist/electron-build/agents/task/report-card.js.map +1 -0
- package/dist/electron-build/agents/task/resource-advisor.js +222 -0
- package/dist/electron-build/agents/task/resource-advisor.js.map +1 -0
- package/dist/electron-build/agents/task/task-budget.js +111 -0
- package/dist/electron-build/agents/task/task-budget.js.map +1 -0
- package/dist/electron-build/agents/task/task-runner.js +670 -0
- package/dist/electron-build/agents/task/task-runner.js.map +1 -0
- package/dist/electron-build/agents/task-state.js +266 -0
- package/dist/electron-build/agents/task-state.js.map +1 -0
- package/dist/electron-build/agents/tool-registry.js +141 -0
- package/dist/electron-build/agents/tool-registry.js.map +1 -0
- package/dist/electron-build/agents/treasury-bridge.js +165 -0
- package/dist/electron-build/agents/treasury-bridge.js.map +1 -0
- package/dist/electron-build/agents/user-questions.js +223 -0
- package/dist/electron-build/agents/user-questions.js.map +1 -0
- package/dist/electron-build/agents/verify-recipe.js +90 -0
- package/dist/electron-build/agents/verify-recipe.js.map +1 -0
- package/dist/electron-build/agents/workflow-engine.js +222 -0
- package/dist/electron-build/agents/workflow-engine.js.map +1 -0
- package/dist/electron-build/agents/workflow-pivot-loop.js +988 -0
- package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -0
- package/dist/electron-build/agents/write-staging.js +133 -0
- package/dist/electron-build/agents/write-staging.js.map +1 -0
- package/dist/electron-build/agents/x402/goal-run-bridge.js +115 -0
- package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -0
- package/dist/electron-build/agents/x402/milestone-settlement.js +163 -0
- package/dist/electron-build/agents/x402/milestone-settlement.js.map +1 -0
- package/dist/electron-build/agents/x402/paid-info-protocol.js +274 -0
- package/dist/electron-build/agents/x402/paid-info-protocol.js.map +1 -0
- package/dist/electron-build/agents/x402/paid-info-store.js +409 -0
- package/dist/electron-build/agents/x402/paid-info-store.js.map +1 -0
- package/dist/electron-build/agents/x402/paid-info-tools.js +346 -0
- package/dist/electron-build/agents/x402/paid-info-tools.js.map +1 -0
- package/dist/electron-build/agents/x402/payment-recovery.js +330 -0
- package/dist/electron-build/agents/x402/payment-recovery.js.map +1 -0
- package/dist/electron-build/agents/x402/resource-contract.js +533 -0
- package/dist/electron-build/agents/x402/resource-contract.js.map +1 -0
- package/dist/electron-build/agents/x402/settlement-state.js +432 -0
- package/dist/electron-build/agents/x402/settlement-state.js.map +1 -0
- package/dist/electron-build/agents/x402/trade.js +294 -0
- package/dist/electron-build/agents/x402/trade.js.map +1 -0
- package/dist/electron-build/agents/x402/transaction-protocol.js +109 -0
- package/dist/electron-build/agents/x402/transaction-protocol.js.map +1 -0
- package/dist/electron-build/agents/x402/transaction-store.js +404 -0
- package/dist/electron-build/agents/x402/transaction-store.js.map +1 -0
- package/dist/electron-build/agents/x402/x402Pay.js +377 -0
- package/dist/electron-build/agents/x402/x402Pay.js.map +1 -0
- package/dist/electron-build/bollharness-integration/context-router-judgment.js +269 -0
- package/dist/electron-build/bollharness-integration/context-router-judgment.js.map +1 -0
- package/dist/electron-build/bollharness-integration/context-router.js +584 -0
- package/dist/electron-build/bollharness-integration/context-router.js.map +1 -0
- package/dist/electron-build/bollharness-integration/gate-state-machine.js +362 -0
- package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -0
- package/dist/electron-build/bollharness-integration/gate-transition-hooks.js +136 -0
- package/dist/electron-build/bollharness-integration/gate-transition-hooks.js.map +1 -0
- package/dist/electron-build/bollharness-integration/guard-checker.js +400 -0
- package/dist/electron-build/bollharness-integration/guard-checker.js.map +1 -0
- package/dist/electron-build/bollharness-integration/index.js +123 -0
- package/dist/electron-build/bollharness-integration/index.js.map +1 -0
- package/dist/electron-build/bollharness-integration/integration.js +531 -0
- package/dist/electron-build/bollharness-integration/integration.js.map +1 -0
- package/dist/electron-build/bollharness-integration/skill-adapter.js +608 -0
- package/dist/electron-build/bollharness-integration/skill-adapter.js.map +1 -0
- package/dist/electron-build/bootstrap/bootstrap.js +173 -0
- package/dist/electron-build/bootstrap/bootstrap.js.map +1 -0
- package/dist/electron-build/bootstrap/context-collector.js +358 -0
- package/dist/electron-build/bootstrap/context-collector.js.map +1 -0
- package/dist/electron-build/bootstrap/context-hierarchy.js +262 -0
- package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -0
- package/dist/electron-build/bootstrap/context-manager.js +211 -0
- package/dist/electron-build/bootstrap/context-manager.js.map +1 -0
- package/dist/electron-build/bootstrap/context-os.js +277 -0
- package/dist/electron-build/bootstrap/context-os.js.map +1 -0
- package/dist/electron-build/bootstrap/event-log.js +203 -0
- package/dist/electron-build/bootstrap/event-log.js.map +1 -0
- package/dist/electron-build/bootstrap/exhaust-scrubber.js +277 -0
- package/dist/electron-build/bootstrap/exhaust-scrubber.js.map +1 -0
- package/dist/electron-build/bootstrap/lifecycle-hooks.js +282 -0
- package/dist/electron-build/bootstrap/lifecycle-hooks.js.map +1 -0
- package/dist/electron-build/bootstrap/memory-compressor.js +377 -0
- package/dist/electron-build/bootstrap/memory-compressor.js.map +1 -0
- package/dist/electron-build/bootstrap/persona-loader.js +219 -0
- package/dist/electron-build/bootstrap/persona-loader.js.map +1 -0
- package/dist/electron-build/bootstrap/project-context.js +162 -0
- package/dist/electron-build/bootstrap/project-context.js.map +1 -0
- package/dist/electron-build/bootstrap/project-state.js +205 -0
- package/dist/electron-build/bootstrap/project-state.js.map +1 -0
- package/dist/electron-build/bootstrap/session-window.js +162 -0
- package/dist/electron-build/bootstrap/session-window.js.map +1 -0
- package/dist/electron-build/bootstrap/vector-index.js +291 -0
- package/dist/electron-build/bootstrap/vector-index.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js +22 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/clobShared.js +86 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/clobShared.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/createOrder.js +37 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/createOrder.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getMarket.js +20 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getMarket.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getOrders.js +29 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getOrders.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js +28 -0
- package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/SafeSDK/deploySafe.js +10 -0
- package/dist/electron-build/constraint-runtime/src/tools/SafeSDK/deploySafe.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/autoPay.js +68 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/autoPay.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/createWallet.js +14 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/createWallet.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/getBalance.js +17 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/getBalance.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/importWallet.js +59 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/importWallet.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/sendTransaction.js +29 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/sendTransaction.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/signMessage.js +14 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/signMessage.js.map +1 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/transferToken.js +29 -0
- package/dist/electron-build/constraint-runtime/src/tools/WalletTools/transferToken.js.map +1 -0
- package/dist/electron-build/constraints/index.js +11 -0
- package/dist/electron-build/constraints/index.js.map +1 -0
- package/dist/electron-build/context-compaction/auto-compact.js +182 -0
- package/dist/electron-build/context-compaction/auto-compact.js.map +1 -0
- package/dist/electron-build/context-compaction/budget-gate.js +32 -0
- package/dist/electron-build/context-compaction/budget-gate.js.map +1 -0
- package/dist/electron-build/context-compaction/budget-reduce.js +39 -0
- package/dist/electron-build/context-compaction/budget-reduce.js.map +1 -0
- package/dist/electron-build/context-compaction/context-collapse.js +71 -0
- package/dist/electron-build/context-compaction/context-collapse.js.map +1 -0
- package/dist/electron-build/context-compaction/index.js +38 -0
- package/dist/electron-build/context-compaction/index.js.map +1 -0
- package/dist/electron-build/context-compaction/microcompact.js +55 -0
- package/dist/electron-build/context-compaction/microcompact.js.map +1 -0
- package/dist/electron-build/context-compaction/pipeline.js +128 -0
- package/dist/electron-build/context-compaction/pipeline.js.map +1 -0
- package/dist/electron-build/context-compaction/snip.js +50 -0
- package/dist/electron-build/context-compaction/snip.js.map +1 -0
- package/dist/electron-build/context-compaction/token-estimator.js +40 -0
- package/dist/electron-build/context-compaction/token-estimator.js.map +1 -0
- package/dist/electron-build/context-compaction/types.js +21 -0
- package/dist/electron-build/context-compaction/types.js.map +1 -0
- package/dist/electron-build/cron/tick-lock.js +247 -0
- package/dist/electron-build/cron/tick-lock.js.map +1 -0
- package/dist/electron-build/documents/reader.js +131 -0
- package/dist/electron-build/documents/reader.js.map +1 -0
- package/dist/electron-build/documents/store.js +220 -0
- package/dist/electron-build/documents/store.js.map +1 -0
- package/dist/electron-build/external-engines/delegate-handle.js +76 -0
- package/dist/electron-build/external-engines/delegate-handle.js.map +1 -0
- package/dist/electron-build/external-engines/delegate.js +225 -0
- package/dist/electron-build/external-engines/delegate.js.map +1 -0
- package/dist/electron-build/external-engines/discovery.js +448 -0
- package/dist/electron-build/external-engines/discovery.js.map +1 -0
- package/dist/electron-build/external-engines/types.js +13 -0
- package/dist/electron-build/external-engines/types.js.map +1 -0
- package/dist/electron-build/hooks/hooks-engine.js +367 -0
- package/dist/electron-build/hooks/hooks-engine.js.map +1 -0
- package/dist/electron-build/judgeness/reflect.js +100 -0
- package/dist/electron-build/judgeness/reflect.js.map +1 -0
- package/dist/electron-build/judgeness/store.js +536 -0
- package/dist/electron-build/judgeness/store.js.map +1 -0
- package/dist/electron-build/judgeness/types.js +21 -0
- package/dist/electron-build/judgeness/types.js.map +1 -0
- package/dist/electron-build/llm/config-store.js +551 -0
- package/dist/electron-build/llm/config-store.js.map +1 -0
- package/dist/electron-build/llm/error-lessons.js +76 -0
- package/dist/electron-build/llm/error-lessons.js.map +1 -0
- package/dist/electron-build/llm/pi-ai.js +830 -0
- package/dist/electron-build/llm/pi-ai.js.map +1 -0
- package/dist/electron-build/llm/system-prompt/registry.js +318 -0
- package/dist/electron-build/llm/system-prompt/registry.js.map +1 -0
- package/dist/electron-build/llm/system-prompt/strip-hibsml.js +55 -0
- package/dist/electron-build/llm/system-prompt/strip-hibsml.js.map +1 -0
- package/dist/electron-build/locales/en.js +32 -0
- package/dist/electron-build/locales/en.js.map +1 -0
- package/dist/electron-build/locales/zh.js +30 -0
- package/dist/electron-build/locales/zh.js.map +1 -0
- package/dist/electron-build/lsp/lsp-manager.js +327 -0
- package/dist/electron-build/lsp/lsp-manager.js.map +1 -0
- package/dist/electron-build/lsp/lsp-tools.js +226 -0
- package/dist/electron-build/lsp/lsp-tools.js.map +1 -0
- package/dist/electron-build/migration/external-agent-migrator.js +451 -0
- package/dist/electron-build/migration/external-agent-migrator.js.map +1 -0
- package/dist/electron-build/network/agent-network.js +775 -0
- package/dist/electron-build/network/agent-network.js.map +1 -0
- package/dist/electron-build/network/goal-event-bridge.js +62 -0
- package/dist/electron-build/network/goal-event-bridge.js.map +1 -0
- package/dist/electron-build/network/iroh-integration.js +117 -0
- package/dist/electron-build/network/iroh-integration.js.map +1 -0
- package/dist/electron-build/network/iroh-transport.js +550 -0
- package/dist/electron-build/network/iroh-transport.js.map +1 -0
- package/dist/electron-build/network/p2p.js +1103 -0
- package/dist/electron-build/network/p2p.js.map +1 -0
- package/dist/electron-build/network/storage/adapters/json-adapter.js +416 -0
- package/dist/electron-build/network/storage/adapters/json-adapter.js.map +1 -0
- package/dist/electron-build/network/storage/types.js +18 -0
- package/dist/electron-build/network/storage/types.js.map +1 -0
- package/dist/electron-build/orbitdb/agent-tools.js +402 -0
- package/dist/electron-build/orbitdb/agent-tools.js.map +1 -0
- package/dist/electron-build/orbitdb/cid-database.js +284 -0
- package/dist/electron-build/orbitdb/cid-database.js.map +1 -0
- package/dist/electron-build/orbitdb/context-store.js +99 -0
- package/dist/electron-build/orbitdb/context-store.js.map +1 -0
- package/dist/electron-build/orbitdb/ipfs-node.js +120 -0
- package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -0
- package/dist/electron-build/orbitdb/kanban-store.js +299 -0
- package/dist/electron-build/orbitdb/kanban-store.js.map +1 -0
- package/dist/electron-build/orbitdb/task-store.js +155 -0
- package/dist/electron-build/orbitdb/task-store.js.map +1 -0
- package/dist/electron-build/orbitdb/trajectory-store.js +194 -0
- package/dist/electron-build/orbitdb/trajectory-store.js.map +1 -0
- package/dist/electron-build/orbitdb/ui-cid.js +77 -0
- package/dist/electron-build/orbitdb/ui-cid.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-a2ui/index.js +89 -0
- package/dist/electron-build/pi-ecosystem-a2ui/index.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-goals/index.js +512 -0
- package/dist/electron-build/pi-ecosystem-goals/index.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js +325 -0
- package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/causal-judge.js +492 -0
- package/dist/electron-build/pi-ecosystem-judgment/causal-judge.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/cleanup.js +167 -0
- package/dist/electron-build/pi-ecosystem-judgment/cleanup.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/decision.js +321 -0
- package/dist/electron-build/pi-ecosystem-judgment/decision.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/detect-hook.js +175 -0
- package/dist/electron-build/pi-ecosystem-judgment/detect-hook.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/distill-prompt.js +232 -0
- package/dist/electron-build/pi-ecosystem-judgment/distill-prompt.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/distillation.js +304 -0
- package/dist/electron-build/pi-ecosystem-judgment/distillation.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/evolve-judgment.js +176 -0
- package/dist/electron-build/pi-ecosystem-judgment/evolve-judgment.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/human-value-pipeline.js +65 -0
- package/dist/electron-build/pi-ecosystem-judgment/human-value-pipeline.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/human-value-store.js +808 -0
- package/dist/electron-build/pi-ecosystem-judgment/human-value-store.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/index.js +508 -0
- package/dist/electron-build/pi-ecosystem-judgment/index.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/injection-gate.js +334 -0
- package/dist/electron-build/pi-ecosystem-judgment/injection-gate.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/monitor-gate.js +228 -0
- package/dist/electron-build/pi-ecosystem-judgment/monitor-gate.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/value-injection.js +598 -0
- package/dist/electron-build/pi-ecosystem-judgment/value-injection.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-mcp/index.js +643 -0
- package/dist/electron-build/pi-ecosystem-mcp/index.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-mcp/ui-tools.js +116 -0
- package/dist/electron-build/pi-ecosystem-mcp/ui-tools.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-subagents/index.js +352 -0
- package/dist/electron-build/pi-ecosystem-subagents/index.js.map +1 -0
- package/dist/electron-build/security/builtin-guards.js +133 -0
- package/dist/electron-build/security/builtin-guards.js.map +1 -0
- package/dist/electron-build/security/context-router-tool.js +111 -0
- package/dist/electron-build/security/context-router-tool.js.map +1 -0
- package/dist/electron-build/security/input-scanner.js +263 -0
- package/dist/electron-build/security/input-scanner.js.map +1 -0
- package/dist/electron-build/security/react-harness.js +148 -0
- package/dist/electron-build/security/react-harness.js.map +1 -0
- package/dist/electron-build/security/tool-gate.js +287 -0
- package/dist/electron-build/security/tool-gate.js.map +1 -0
- package/dist/electron-build/setup/setup-store.js +648 -0
- package/dist/electron-build/setup/setup-store.js.map +1 -0
- package/dist/electron-build/social/channels/ChannelManager.js +444 -0
- package/dist/electron-build/social/channels/ChannelManager.js.map +1 -0
- package/dist/electron-build/social/channels/DiapChannelBridge.js +451 -0
- package/dist/electron-build/social/channels/DiapChannelBridge.js.map +1 -0
- package/dist/electron-build/social/channels/InterestMatcher.js +135 -0
- package/dist/electron-build/social/channels/InterestMatcher.js.map +1 -0
- package/dist/electron-build/social/channels/types.js +21 -0
- package/dist/electron-build/social/channels/types.js.map +1 -0
- package/dist/electron-build/social/global-shared-context.js +368 -0
- package/dist/electron-build/social/global-shared-context.js.map +1 -0
- package/dist/electron-build/social/heartbeat.js +882 -0
- package/dist/electron-build/social/heartbeat.js.map +1 -0
- package/dist/electron-build/storage/did-catalog-bridge.js +314 -0
- package/dist/electron-build/storage/did-catalog-bridge.js.map +1 -0
- package/dist/electron-build/storage/did-catalog.js +272 -0
- package/dist/electron-build/storage/did-catalog.js.map +1 -0
- package/dist/electron-build/utils/auto-update.js +140 -558
- package/dist/electron-build/utils/auto-update.js.map +1 -1
- package/dist/electron-build/utils/module-context.js +98 -0
- package/dist/electron-build/utils/module-context.js.map +1 -0
- package/dist/electron-build/utils/runtime-bootstrap.js +745 -0
- package/dist/electron-build/utils/runtime-bootstrap.js.map +1 -0
- package/dist/electron-build/utils/update-manager.js +770 -0
- package/dist/electron-build/utils/update-manager.js.map +1 -0
- package/dist/electron-build/utils/update-state.js +406 -0
- package/dist/electron-build/utils/update-state.js.map +1 -0
- package/dist/electron-build/utils/version-info.js +562 -0
- package/dist/electron-build/utils/version-info.js.map +1 -0
- package/dist/electron-build/web/i18n.js +146 -0
- package/dist/electron-build/web/i18n.js.map +1 -0
- package/dist/electron-build/web/routes-x402-info.js +232 -0
- package/dist/electron-build/web/routes-x402-info.js.map +1 -0
- package/dist/electron-build/web/server-storage.js +390 -0
- package/dist/electron-build/web/server-storage.js.map +1 -0
- package/dist/electron-build/web/server-types.js +52 -0
- package/dist/electron-build/web/server-types.js.map +1 -0
- package/dist/electron-build/web/task-deps.js +41 -0
- package/dist/electron-build/web/task-deps.js.map +1 -0
- package/dist/index.js +28 -27
- package/dist/llm/system-prompt/registry.js +6 -3
- package/dist/utils/auto-update.js +138 -525
- package/dist/utils/module-context.js +59 -0
- package/dist/utils/runtime-bootstrap.js +682 -0
- package/dist/utils/update-cli.js +82 -0
- package/dist/utils/update-health.js +401 -0
- package/dist/utils/update-manager.js +716 -0
- package/dist/utils/update-state.js +353 -0
- package/dist/utils/version-info.js +510 -0
- package/package.json +1 -1
- package/scripts/build-cli.js +32 -151
- package/scripts/postinstall.js +153 -76
|
@@ -0,0 +1,1103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.p2pNetwork = exports.P2PNetwork = exports.RequestResponseManager = exports.CIRCUIT_RELAY_HOP_PROTOCOL = void 0;
|
|
37
|
+
const libp2p_1 = require("libp2p");
|
|
38
|
+
const tcp_1 = require("@libp2p/tcp");
|
|
39
|
+
const websockets_1 = require("@libp2p/websockets");
|
|
40
|
+
const multiaddr_1 = require("@multiformats/multiaddr");
|
|
41
|
+
const circuit_relay_v2_1 = require("@libp2p/circuit-relay-v2");
|
|
42
|
+
const autonat_1 = require("@libp2p/autonat");
|
|
43
|
+
const upnp_nat_1 = require("@libp2p/upnp-nat");
|
|
44
|
+
const fs = __importStar(require("fs/promises"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const PEER_STORE_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'peer-store.json');
|
|
47
|
+
const RECONNECT_DELAY_MS = 5000;
|
|
48
|
+
const MAX_RECONNECT_ATTEMPTS = 3;
|
|
49
|
+
/**
|
|
50
|
+
* 2026-09-11: 桌面端当 circuit relay v2 服务器 (relay v2 server)。
|
|
51
|
+
*
|
|
52
|
+
* 为什么必须: 手机在 iOS WKWebView 里**不能 listen 任何公网/局域网地址** ——
|
|
53
|
+
* 它唯一能被别人拨入的途径就是「向中继预约 → 拿到 <relay>/p2p-circuit/p2p/<手机> 地址」。
|
|
54
|
+
* 没有中继时手机 getMultiaddrs() 为空 (只能拨出, 不能被拨)。
|
|
55
|
+
*
|
|
56
|
+
* 协议常量 = identify 会广播的服务协议 (手机端 topology 看到它就发起预约)。
|
|
57
|
+
*/
|
|
58
|
+
exports.CIRCUIT_RELAY_HOP_PROTOCOL = circuit_relay_v2_1.RELAY_V2_HOP_CODEC;
|
|
59
|
+
/** 允许的中继预约数 (每个预约 = 一台像手机这样的「不能 listen 的对等端」) */
|
|
60
|
+
const RELAY_MAX_RESERVATIONS = 64;
|
|
61
|
+
/**
|
|
62
|
+
* 预约有效期 (单位 **毫秒**; @libp2p/circuit-relay-v2 v4 的 reservations.reservationTtl
|
|
63
|
+
* 是 number, 不是 '2H' 这种字符串 —— 传字符串会让 new Date(Date.now()+NaN) 直接失效)。
|
|
64
|
+
*/
|
|
65
|
+
const RELAY_RESERVATION_TTL_MS = 2 * 60 * 60 * 1000;
|
|
66
|
+
/** 过期预约清理间隔 (默认 5 分钟) */
|
|
67
|
+
const RELAY_RESERVATION_CLEAR_INTERVAL_MS = 5 * 60 * 1000;
|
|
68
|
+
class RequestResponseManager {
|
|
69
|
+
pendingRequests = new Map();
|
|
70
|
+
responseHandlers = new Map();
|
|
71
|
+
requestTimeoutMs = 30000;
|
|
72
|
+
cleanupInterval = null;
|
|
73
|
+
constructor() {
|
|
74
|
+
this.startCleanup();
|
|
75
|
+
}
|
|
76
|
+
startCleanup() {
|
|
77
|
+
this.cleanupInterval = setInterval(() => {
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
for (const [id, request] of this.pendingRequests) {
|
|
80
|
+
if (now - request.timestamp > this.requestTimeoutMs) {
|
|
81
|
+
clearTimeout(request.timeout);
|
|
82
|
+
request.reject(new Error(`Request ${id} timed out`));
|
|
83
|
+
this.pendingRequests.delete(id);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}, 10000);
|
|
87
|
+
}
|
|
88
|
+
async sendRequest(peerId, type, payload, sendFn, onResponse) {
|
|
89
|
+
const requestId = crypto.randomUUID();
|
|
90
|
+
const data = new TextEncoder().encode(`REQ:${requestId}|${type}:${payload}`);
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
this.pendingRequests.delete(requestId);
|
|
94
|
+
reject(new Error(`Request ${requestId} timed out after ${this.requestTimeoutMs}ms`));
|
|
95
|
+
}, this.requestTimeoutMs);
|
|
96
|
+
this.pendingRequests.set(requestId, {
|
|
97
|
+
id: requestId,
|
|
98
|
+
type,
|
|
99
|
+
payload,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
resolve,
|
|
102
|
+
reject,
|
|
103
|
+
timeout
|
|
104
|
+
});
|
|
105
|
+
sendFn(peerId, data).catch(err => {
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
this.pendingRequests.delete(requestId);
|
|
108
|
+
reject(err);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
handleResponse(requestId, responseData) {
|
|
113
|
+
const request = this.pendingRequests.get(requestId);
|
|
114
|
+
if (request) {
|
|
115
|
+
clearTimeout(request.timeout);
|
|
116
|
+
request.resolve(responseData);
|
|
117
|
+
this.pendingRequests.delete(requestId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
registerResponseHandler(type, handler) {
|
|
121
|
+
this.responseHandlers.set(type, handler);
|
|
122
|
+
}
|
|
123
|
+
getResponseHandler(type) {
|
|
124
|
+
return this.responseHandlers.get(type);
|
|
125
|
+
}
|
|
126
|
+
setRequestTimeout(ms) {
|
|
127
|
+
this.requestTimeoutMs = ms;
|
|
128
|
+
}
|
|
129
|
+
getPendingCount() {
|
|
130
|
+
return this.pendingRequests.size;
|
|
131
|
+
}
|
|
132
|
+
shutdown() {
|
|
133
|
+
if (this.cleanupInterval) {
|
|
134
|
+
clearInterval(this.cleanupInterval);
|
|
135
|
+
}
|
|
136
|
+
for (const request of this.pendingRequests.values()) {
|
|
137
|
+
clearTimeout(request.timeout);
|
|
138
|
+
request.reject(new Error('Shutting down'));
|
|
139
|
+
}
|
|
140
|
+
this.pendingRequests.clear();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.RequestResponseManager = RequestResponseManager;
|
|
144
|
+
class P2PNetwork {
|
|
145
|
+
node = null;
|
|
146
|
+
messageHandlers = new Map();
|
|
147
|
+
offlineMessages = new Map();
|
|
148
|
+
persistentPeers = new Map();
|
|
149
|
+
reconnectTimers = new Map();
|
|
150
|
+
ownDid = null;
|
|
151
|
+
peerStorePath;
|
|
152
|
+
natStatus = { reachable: false };
|
|
153
|
+
relayServerAddr = null;
|
|
154
|
+
/** 2026-09-11: 中继服务是否真的起来 (identify 在广播 hop 协议) */
|
|
155
|
+
relayServerEnabled = false;
|
|
156
|
+
relayMaxReservations = RELAY_MAX_RESERVATIONS;
|
|
157
|
+
requestResponseManager = new RequestResponseManager();
|
|
158
|
+
pendingResponseHandlers = new Map();
|
|
159
|
+
pendingRequests = new Map();
|
|
160
|
+
requestTimeoutMs = 30000;
|
|
161
|
+
// 新增: 消息存储层
|
|
162
|
+
messageStore = null;
|
|
163
|
+
offlineDeliveryInterval = null;
|
|
164
|
+
// 2026-08-15: 手机 data.* 同步提供者 (如 LLM 配置)。由上层 (server/测试) 注入, 网络层不 import config-store.
|
|
165
|
+
dataProviders = new Map();
|
|
166
|
+
constructor() {
|
|
167
|
+
this.peerStorePath = PEER_STORE_PATH;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 注册 data.* 协议提供者 (手机同步请求 → 返回 payload 字符串, null = 不响应)。
|
|
171
|
+
* 例: registerDataProvider('data.llm-config', async () => JSON.stringify(llmConfig))
|
|
172
|
+
*/
|
|
173
|
+
registerDataProvider(type, fn) {
|
|
174
|
+
this.dataProviders.set(type, fn);
|
|
175
|
+
}
|
|
176
|
+
async enablePersistence() {
|
|
177
|
+
try {
|
|
178
|
+
const { JsonMessageStore } = await Promise.resolve().then(() => __importStar(require('./storage/adapters/json-adapter.js')));
|
|
179
|
+
const baseDir = path.join(process.env.HOME || '/tmp', '.bolloon', 'messages-libp2p');
|
|
180
|
+
this.messageStore = new JsonMessageStore({ baseDir });
|
|
181
|
+
await this.messageStore.initialize();
|
|
182
|
+
this.startOfflineDeliveryLoop();
|
|
183
|
+
console.log('[P2P] Persistence enabled');
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.warn('[P2P] Failed to enable persistence:', e);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
startOfflineDeliveryLoop() {
|
|
190
|
+
if (!this.messageStore)
|
|
191
|
+
return;
|
|
192
|
+
this.offlineDeliveryInterval = setInterval(async () => {
|
|
193
|
+
for (const peerId of this.getPeers()) {
|
|
194
|
+
const offlineMsgs = await this.messageStore.getOfflineMessages(peerId);
|
|
195
|
+
for (const msg of offlineMsgs) {
|
|
196
|
+
if (msg.retryCount >= 10) {
|
|
197
|
+
await this.messageStore.dequeueOfflineMessage(msg.id);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const payload = Uint8Array.from(atob(msg.payload), c => c.charCodeAt(0));
|
|
202
|
+
const success = await this.sendMessageDirect(peerId, msg.type, payload);
|
|
203
|
+
if (success) {
|
|
204
|
+
await this.messageStore.dequeueOfflineMessage(msg.id);
|
|
205
|
+
console.log(`[P2P] Delivered offline message to ${peerId.substring(0, 12)}...`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
await this.messageStore.incrementOfflineRetry(msg.id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}, 5000);
|
|
214
|
+
}
|
|
215
|
+
async sendMessageDirect(peerId, type, payload) {
|
|
216
|
+
if (!this.node)
|
|
217
|
+
return false;
|
|
218
|
+
try {
|
|
219
|
+
const data = typeof payload === 'string'
|
|
220
|
+
? new TextEncoder().encode(`${type}:${payload}`)
|
|
221
|
+
: payload;
|
|
222
|
+
const ma = (0, multiaddr_1.multiaddr)(`/p2p/${peerId}`);
|
|
223
|
+
// libp2p 3.x: dialProtocol 返回 Stream 本体 (非 {stream})
|
|
224
|
+
const stream = await this.node.dialProtocol(ma, '/agent/message');
|
|
225
|
+
stream.send(data);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async createNode(config) {
|
|
233
|
+
this.ownDid = config?.ownDid || null;
|
|
234
|
+
const enableRelay = config?.enableRelay ?? true;
|
|
235
|
+
const enableRelayServer = config?.enableRelayServer ?? true;
|
|
236
|
+
const enableAutoNat = config?.enableAutoNat ?? true;
|
|
237
|
+
const enableUPnP = config?.enableUPnP ?? true;
|
|
238
|
+
const transports = [(0, tcp_1.tcp)()];
|
|
239
|
+
// 2026-08-15: 加 websockets 传输 — 让手机端 (WebView) 能通过 wss/ws 连桌面节点
|
|
240
|
+
try {
|
|
241
|
+
transports.push((0, websockets_1.webSockets)());
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
console.warn(`[P2P] Failed to add websockets transport:`, e);
|
|
245
|
+
}
|
|
246
|
+
const services = {};
|
|
247
|
+
// 2026-08-15: relay 传输需要 identify 服务 (libp2p 3.x 依赖检查)
|
|
248
|
+
try {
|
|
249
|
+
const { identify } = await Promise.resolve().then(() => __importStar(require('@libp2p/identify')));
|
|
250
|
+
services.identify = identify();
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
console.warn(`[P2P] Failed to setup identify:`, e);
|
|
254
|
+
}
|
|
255
|
+
// 2026-08-15: 加密 + 多路复用 (与手机端 websockets 节点互通必需)
|
|
256
|
+
let connectionEncrypters = [];
|
|
257
|
+
let streamMuxers = [];
|
|
258
|
+
try {
|
|
259
|
+
const { noise } = await Promise.resolve().then(() => __importStar(require('@chainsafe/libp2p-noise')));
|
|
260
|
+
connectionEncrypters = [noise()];
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
console.warn(`[P2P] Failed to setup noise encrypter:`, e);
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const { yamux } = await Promise.resolve().then(() => __importStar(require('@chainsafe/libp2p-yamux')));
|
|
267
|
+
streamMuxers = [yamux()];
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
console.warn(`[P2P] Failed to setup yamux muxer:`, e);
|
|
271
|
+
}
|
|
272
|
+
if (enableRelay) {
|
|
273
|
+
try {
|
|
274
|
+
const relayTransport = (0, circuit_relay_v2_1.circuitRelayTransport)();
|
|
275
|
+
transports.push(relayTransport);
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
console.warn(`[P2P] Failed to add circuit relay transport:`, e);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// 2026-09-11: 中继服务器 (relay v2 server) —— 手机(WebView)唯一的入站途径。
|
|
282
|
+
// 注意: relay v2 服务器必须 **不是** relayed 连接上的对端才有意义, 且需要 identify 广播 hop 协议。
|
|
283
|
+
this.relayMaxReservations = RELAY_MAX_RESERVATIONS;
|
|
284
|
+
if (enableRelayServer) {
|
|
285
|
+
try {
|
|
286
|
+
services.circuitRelay = (0, circuit_relay_v2_1.circuitRelayServer)({
|
|
287
|
+
reservations: {
|
|
288
|
+
maxReservations: RELAY_MAX_RESERVATIONS,
|
|
289
|
+
reservationTtl: RELAY_RESERVATION_TTL_MS,
|
|
290
|
+
reservationClearInterval: RELAY_RESERVATION_CLEAR_INTERVAL_MS,
|
|
291
|
+
// 不套用默认 data/duration 上限 (默认仅 128KB / 2min) —— 那会把手机的
|
|
292
|
+
// bitswap/大消息一次性掐断; 个人自建中继不需要这层限流。
|
|
293
|
+
applyDefaultLimit: false,
|
|
294
|
+
},
|
|
295
|
+
hopTimeout: 30_000,
|
|
296
|
+
maxInboundHopStreams: 64,
|
|
297
|
+
maxOutboundHopStreams: 64,
|
|
298
|
+
maxOutboundStopStreams: 128,
|
|
299
|
+
});
|
|
300
|
+
console.log(`[P2P] Circuit relay server: maxReservations=${RELAY_MAX_RESERVATIONS} ` +
|
|
301
|
+
`reservationTtl=${RELAY_RESERVATION_TTL_MS}ms applyDefaultLimit=false protocol=${circuit_relay_v2_1.RELAY_V2_HOP_CODEC}`);
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
console.warn(`[P2P] Failed to setup circuit relay server:`, e);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (enableAutoNat) {
|
|
308
|
+
try {
|
|
309
|
+
services.autonat = (0, autonat_1.autoNAT)();
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
console.warn(`[P2P] Failed to setup AutoNAT:`, e);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (enableUPnP) {
|
|
316
|
+
try {
|
|
317
|
+
services.upnpNAT = (0, upnp_nat_1.uPnPNAT)();
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
console.warn(`[P2P] Failed to setup UPnP:`, e);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
this.node = await (0, libp2p_1.createLibp2p)({
|
|
324
|
+
addresses: {
|
|
325
|
+
listen: ['/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0/ws']
|
|
326
|
+
},
|
|
327
|
+
transports,
|
|
328
|
+
connectionEncrypters,
|
|
329
|
+
streamMuxers,
|
|
330
|
+
services
|
|
331
|
+
});
|
|
332
|
+
this.node.addEventListener('peer-relay-registry', (evt) => {
|
|
333
|
+
if (evt.detail?.relayAddr) {
|
|
334
|
+
this.relayServerAddr = evt.detail.relayAddr;
|
|
335
|
+
console.log(`[P2P] Relay address available: ${this.relayServerAddr}`);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
// 中继事件日志: 谁预约上了 / 预约过期 —— 手机连不上时一眼看出问题在哪
|
|
339
|
+
try {
|
|
340
|
+
const relaySvc = this.getRelayService();
|
|
341
|
+
relaySvc?.addEventListener?.('relay:reservation', (evt) => {
|
|
342
|
+
console.log(`[P2P] Relay reservation taken by ${evt?.detail?.addr?.toString?.() ?? 'unknown'}`);
|
|
343
|
+
});
|
|
344
|
+
relaySvc?.addEventListener?.('relay:advert:error', (evt) => {
|
|
345
|
+
console.warn(`[P2P] Relay advert error: ${evt?.detail?.message ?? evt?.detail}`);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
console.warn(`[P2P] Failed to attach relay event listeners:`, e);
|
|
350
|
+
}
|
|
351
|
+
await this.node.start();
|
|
352
|
+
// ★ 复验: 中继服务真的起来了吗 —— 唯一判据是 identify 会广播的协议表里有 hop 协议
|
|
353
|
+
this.verifyRelayService(enableRelayServer);
|
|
354
|
+
const peerId = this.node.peerId.toString();
|
|
355
|
+
const multiaddrs = this.node.getMultiaddrs().map((addr) => addr.toString());
|
|
356
|
+
if (config?.relayPeers && config.relayPeers.length > 0) {
|
|
357
|
+
for (const relayAddr of config.relayPeers) {
|
|
358
|
+
try {
|
|
359
|
+
const ma = (0, multiaddr_1.multiaddr)(relayAddr);
|
|
360
|
+
await this.node.dial(ma);
|
|
361
|
+
console.log(`[P2P] Connected to relay peer: ${relayAddr}`);
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
console.warn(`[P2P] Failed to connect to relay peer ${relayAddr}:`, e);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
await this.loadPersistentPeers();
|
|
369
|
+
if (config?.bootstrapPeers) {
|
|
370
|
+
await this.connectToBootstrapPeers(config.bootstrapPeers);
|
|
371
|
+
}
|
|
372
|
+
await this.reconnectPersistentPeers();
|
|
373
|
+
this.checkNatStatus();
|
|
374
|
+
this.setupMessageHandler();
|
|
375
|
+
return {
|
|
376
|
+
peerId,
|
|
377
|
+
multiaddrs,
|
|
378
|
+
relayAddr: this.relayServerAddr || undefined,
|
|
379
|
+
relay: this.getRelayServiceInfo(),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/** 中继服务实例 (libp2p 的 services 是普通对象, key = createNode 里 services.circuitRelay) */
|
|
383
|
+
getRelayService() {
|
|
384
|
+
const s = this.node?.services;
|
|
385
|
+
if (!s)
|
|
386
|
+
return null;
|
|
387
|
+
return s.circuitRelay ?? s['circuit-relay'] ?? s['circuitRelayServer'] ?? null;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* 复验中继服务: 只认「identify 在广播的协议表里有 hop 协议」——
|
|
391
|
+
* 手机端的 relay discovery 拓扑就是靠 identify 里的这个协议发现中继并预约的。
|
|
392
|
+
* 不在 → 中继对手机**不可用**, 必须出声 (别静默假装成功)。
|
|
393
|
+
*/
|
|
394
|
+
verifyRelayService(requested) {
|
|
395
|
+
let protocols = [];
|
|
396
|
+
try {
|
|
397
|
+
protocols = this.node?.getProtocols?.() ?? [];
|
|
398
|
+
}
|
|
399
|
+
catch { /* 忽略 */ }
|
|
400
|
+
const hasHop = protocols.includes(circuit_relay_v2_1.RELAY_V2_HOP_CODEC);
|
|
401
|
+
const svc = this.getRelayService();
|
|
402
|
+
this.relayServerEnabled = !!svc && hasHop;
|
|
403
|
+
if (this.relayServerEnabled) {
|
|
404
|
+
console.log(`[P2P] Circuit relay server ACTIVE — identify 广播 ${circuit_relay_v2_1.RELAY_V2_HOP_CODEC} ` +
|
|
405
|
+
`(services.circuitRelay=${!!svc}, protocols=${protocols.length}) → 手机可向本节点预约 /p2p-circuit`);
|
|
406
|
+
}
|
|
407
|
+
else if (requested) {
|
|
408
|
+
console.warn(`[P2P] Circuit relay server NOT active — hop 协议未注册 (services.circuitRelay=${!!svc}, ` +
|
|
409
|
+
`hasHop=${hasHop}) → 手机将拿不到 /p2p-circuit 地址`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/** 中继服务运行时状态 (供 /api/p2p/mobile-connect 与诊断用) */
|
|
413
|
+
getRelayServiceInfo() {
|
|
414
|
+
const svc = this.getRelayService();
|
|
415
|
+
let protocols = [];
|
|
416
|
+
try {
|
|
417
|
+
protocols = this.node?.getProtocols?.() ?? [];
|
|
418
|
+
}
|
|
419
|
+
catch { /* 忽略 */ }
|
|
420
|
+
const hasHop = protocols.includes(circuit_relay_v2_1.RELAY_V2_HOP_CODEC);
|
|
421
|
+
let reservations = 0;
|
|
422
|
+
try {
|
|
423
|
+
reservations = svc?.reservations?.size ?? 0;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
reservations = 0;
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
enabled: !!svc && hasHop,
|
|
430
|
+
protocol: circuit_relay_v2_1.RELAY_V2_HOP_CODEC,
|
|
431
|
+
reservations,
|
|
432
|
+
maxReservations: this.relayMaxReservations,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* 手机端(WebView)连桌面用的 ws 多地址 —— 手机不能 listen, 只能主动拨入,
|
|
437
|
+
* 所以必须把桌面的 /ws 地址给手机 (WebSocket 传输, 见 start() 的 listen)。
|
|
438
|
+
*
|
|
439
|
+
* 注意 (2026-09-11 修): 不能再用 `endsWith('/ws')` —— libp2p 的
|
|
440
|
+
* `getMultiaddrs()` 会在末尾追加 `/p2p/<PeerId>`, 实际形态是
|
|
441
|
+
* `/ip4/192.168.1.5/tcp/8765/ws/p2p/12D3Koo…`, 用 endsWith 永远筛出空数组
|
|
442
|
+
* (→ /api/p2p/mobile-connect 返回空 wsAddrs → 手机拿不到任何可拨地址)。
|
|
443
|
+
* 正确做法: 看 multiaddr 组件里有没有 ws/wss 段。
|
|
444
|
+
*/
|
|
445
|
+
getWsMultiaddrs() {
|
|
446
|
+
if (!this.node)
|
|
447
|
+
return [];
|
|
448
|
+
try {
|
|
449
|
+
return this.node
|
|
450
|
+
.getMultiaddrs()
|
|
451
|
+
.map((a) => a.toString())
|
|
452
|
+
.filter((a) => {
|
|
453
|
+
const parts = a.split('/');
|
|
454
|
+
return parts.includes('ws') || parts.includes('wss');
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/** 桌面 P2P 节点 ID (手机端识别对端) */
|
|
462
|
+
getNodePeerId() {
|
|
463
|
+
try {
|
|
464
|
+
return this.node ? this.node.peerId.toString() : '';
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return '';
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* 2026-09-11: 手机可用来「预约中继」的 ws 地址 —— 就是 /ws listen 地址, 但**保证带
|
|
472
|
+
* `/p2p/<桌面PeerId>`** (circuit-relay 预约必须知道中继的 PeerID, 否则手机没法
|
|
473
|
+
* listen `<relay>/p2p-circuit`)。
|
|
474
|
+
*/
|
|
475
|
+
getRelayAddrs() {
|
|
476
|
+
const peerId = this.getNodePeerId();
|
|
477
|
+
if (!peerId)
|
|
478
|
+
return this.getWsMultiaddrs();
|
|
479
|
+
return this.getWsMultiaddrs().map((a) => (a.includes('/p2p/') ? a : `${a}/p2p/${peerId}`));
|
|
480
|
+
}
|
|
481
|
+
async checkNatStatus() {
|
|
482
|
+
if (!this.node)
|
|
483
|
+
return;
|
|
484
|
+
try {
|
|
485
|
+
const connections = this.node.getConnections();
|
|
486
|
+
if (connections.length > 0) {
|
|
487
|
+
for (const conn of connections) {
|
|
488
|
+
const addr = conn.remoteAddr?.toString();
|
|
489
|
+
if (addr && addr.includes('127.0.0.1'))
|
|
490
|
+
continue;
|
|
491
|
+
this.natStatus = {
|
|
492
|
+
reachable: true,
|
|
493
|
+
type: 'unknown',
|
|
494
|
+
externalAddr: addr
|
|
495
|
+
};
|
|
496
|
+
console.log(`[P2P] NAT status: reachable, external address: ${addr}`);
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch (e) {
|
|
502
|
+
console.warn(`[P2P] NAT status check failed:`, e);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
getNatStatus() {
|
|
506
|
+
return this.natStatus;
|
|
507
|
+
}
|
|
508
|
+
getRelayAddress() {
|
|
509
|
+
return this.relayServerAddr;
|
|
510
|
+
}
|
|
511
|
+
async createRelayReservation() {
|
|
512
|
+
if (!this.node)
|
|
513
|
+
return null;
|
|
514
|
+
try {
|
|
515
|
+
const relayService = this.node.services.get('circuit-relay');
|
|
516
|
+
if (!relayService) {
|
|
517
|
+
console.log(`[P2P] Circuit relay service not available`);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const reservation = await relayService.reserve();
|
|
521
|
+
if (reservation) {
|
|
522
|
+
this.relayServerAddr = `/p2p/${this.node.peerId.toString()}/p2p-circuit`;
|
|
523
|
+
console.log(`[P2P] Created relay reservation: ${this.relayServerAddr}`);
|
|
524
|
+
return this.relayServerAddr;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
catch (e) {
|
|
528
|
+
console.warn(`[P2P] Failed to create relay reservation:`, e);
|
|
529
|
+
}
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
async dialViaRelay(relayAddr, targetPeerId) {
|
|
533
|
+
if (!this.node)
|
|
534
|
+
return false;
|
|
535
|
+
try {
|
|
536
|
+
const ma = (0, multiaddr_1.multiaddr)(`${relayAddr}/p2p/${targetPeerId}`);
|
|
537
|
+
await this.node.dial(ma);
|
|
538
|
+
console.log(`[P2P] Dialed ${targetPeerId} via relay ${relayAddr}`);
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
catch (e) {
|
|
542
|
+
console.warn(`[P2P] Failed to dial via relay:`, e);
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
async loadPersistentPeers() {
|
|
547
|
+
try {
|
|
548
|
+
const data = await fs.readFile(this.peerStorePath, 'utf-8');
|
|
549
|
+
const peers = JSON.parse(data);
|
|
550
|
+
for (const peer of peers) {
|
|
551
|
+
this.persistentPeers.set(peer.peerId, peer);
|
|
552
|
+
}
|
|
553
|
+
console.log(`[P2P] Loaded ${peers.length} persistent peers`);
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
console.log(`[P2P] No existing peer store found, starting fresh`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async savePersistentPeers() {
|
|
560
|
+
try {
|
|
561
|
+
const dir = path.dirname(this.peerStorePath);
|
|
562
|
+
await fs.mkdir(dir, { recursive: true });
|
|
563
|
+
const peers = Array.from(this.persistentPeers.values());
|
|
564
|
+
await fs.writeFile(this.peerStorePath, JSON.stringify(peers, null, 2));
|
|
565
|
+
}
|
|
566
|
+
catch (e) {
|
|
567
|
+
console.warn(`[P2P] Failed to save peer store:`, e);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
addPersistentPeer(peerInfo) {
|
|
571
|
+
peerInfo.lastConnected = Date.now();
|
|
572
|
+
this.persistentPeers.set(peerInfo.peerId, peerInfo);
|
|
573
|
+
this.savePersistentPeers();
|
|
574
|
+
}
|
|
575
|
+
removePersistentPeer(peerId) {
|
|
576
|
+
this.persistentPeers.delete(peerId);
|
|
577
|
+
this.savePersistentPeers();
|
|
578
|
+
}
|
|
579
|
+
getPersistentPeers() {
|
|
580
|
+
return Array.from(this.persistentPeers.values());
|
|
581
|
+
}
|
|
582
|
+
async reconnectPersistentPeers() {
|
|
583
|
+
const now = Date.now();
|
|
584
|
+
for (const [peerId, peerInfo] of this.persistentPeers) {
|
|
585
|
+
if (peerInfo.lastAttempt && now - peerInfo.lastAttempt < RECONNECT_DELAY_MS) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (peerInfo.multiaddrs && peerInfo.multiaddrs.length > 0) {
|
|
589
|
+
await this.attemptReconnect(peerId, peerInfo);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async attemptReconnect(peerId, peerInfo, attempt = 0) {
|
|
594
|
+
if (!this.node)
|
|
595
|
+
return;
|
|
596
|
+
try {
|
|
597
|
+
const hasConn = this.node.getConnections?.(peerInfo.peerId);
|
|
598
|
+
if (hasConn && hasConn.length > 0) {
|
|
599
|
+
console.log(`[P2P] Already connected to ${peerId}`);
|
|
600
|
+
await this.deliverOfflineMessages(peerId);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
catch { }
|
|
605
|
+
console.log(`[P2P] Reconnecting to ${peerId} (attempt ${attempt + 1})...`);
|
|
606
|
+
for (const addr of peerInfo.multiaddrs) {
|
|
607
|
+
try {
|
|
608
|
+
const ma = (0, multiaddr_1.multiaddr)(addr);
|
|
609
|
+
await this.node.dial(ma);
|
|
610
|
+
peerInfo.lastConnected = Date.now();
|
|
611
|
+
peerInfo.lastAttempt = undefined;
|
|
612
|
+
this.persistentPeers.set(peerId, peerInfo);
|
|
613
|
+
await this.savePersistentPeers();
|
|
614
|
+
console.log(`[P2P] Reconnected to ${peerId} at ${addr}`);
|
|
615
|
+
await this.deliverOfflineMessages(peerId);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
catch (e) {
|
|
619
|
+
console.warn(`[P2P] Failed to reconnect to ${addr}:`, e);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
peerInfo.lastAttempt = Date.now();
|
|
623
|
+
this.persistentPeers.set(peerId, peerInfo);
|
|
624
|
+
if (attempt < MAX_RECONNECT_ATTEMPTS - 1) {
|
|
625
|
+
const timer = setTimeout(() => {
|
|
626
|
+
this.reconnectTimers.delete(peerId);
|
|
627
|
+
this.attemptReconnect(peerId, peerInfo, attempt + 1);
|
|
628
|
+
}, RECONNECT_DELAY_MS * (attempt + 1));
|
|
629
|
+
this.reconnectTimers.set(peerId, timer);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async deliverOfflineMessages(peerId) {
|
|
633
|
+
const messages = this.offlineMessages.get(peerId) || [];
|
|
634
|
+
this.offlineMessages.delete(peerId);
|
|
635
|
+
for (const data of messages) {
|
|
636
|
+
try {
|
|
637
|
+
await this.sendRawMessage(peerId, data);
|
|
638
|
+
}
|
|
639
|
+
catch (e) {
|
|
640
|
+
console.warn(`[P2P] Failed to deliver offline message to ${peerId}:`, e);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (messages.length > 0) {
|
|
644
|
+
console.log(`[P2P] Delivered ${messages.length} offline messages to ${peerId}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
async connectToBootstrapPeers(peers) {
|
|
648
|
+
if (!this.node)
|
|
649
|
+
return;
|
|
650
|
+
for (const addr of peers) {
|
|
651
|
+
try {
|
|
652
|
+
const ma = (0, multiaddr_1.multiaddr)(addr);
|
|
653
|
+
await this.node.dial(ma);
|
|
654
|
+
console.log(`[P2P] Connected to bootstrap peer: ${addr}`);
|
|
655
|
+
const peerId = ma.getPeerId?.() || ma.peerId;
|
|
656
|
+
if (peerId) {
|
|
657
|
+
this.addPersistentPeer({ peerId, multiaddrs: [addr] });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
catch (e) {
|
|
661
|
+
console.warn(`[P2P] Failed to connect to bootstrap peer ${addr}:`, e);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
setupMessageHandler() {
|
|
666
|
+
if (!this.node)
|
|
667
|
+
return;
|
|
668
|
+
const network = this;
|
|
669
|
+
this.node.handle('/agent/message', async (stream, connection) => {
|
|
670
|
+
try {
|
|
671
|
+
const chunks = [];
|
|
672
|
+
for await (const chunk of stream) {
|
|
673
|
+
chunks.push(chunk instanceof Uint8Array ? chunk : chunk.subarray());
|
|
674
|
+
}
|
|
675
|
+
const data = new Uint8Array(chunks.reduce((acc, c) => acc + c.length, 0));
|
|
676
|
+
let offset = 0;
|
|
677
|
+
for (const chunk of chunks) {
|
|
678
|
+
data.set(chunk, offset);
|
|
679
|
+
offset += chunk.length;
|
|
680
|
+
}
|
|
681
|
+
const fromPeerId = connection.remotePeer.toString();
|
|
682
|
+
const messageStr = new TextDecoder().decode(data);
|
|
683
|
+
const colonIdx = messageStr.indexOf(':');
|
|
684
|
+
const didMarker = 'DID:';
|
|
685
|
+
let did;
|
|
686
|
+
let type = 'message';
|
|
687
|
+
let payload = '';
|
|
688
|
+
let requestId = undefined;
|
|
689
|
+
if (messageStr.startsWith(didMarker)) {
|
|
690
|
+
const didEndIdx = messageStr.indexOf('|');
|
|
691
|
+
if (didEndIdx > 0) {
|
|
692
|
+
did = messageStr.substring(didMarker.length, didEndIdx);
|
|
693
|
+
const afterDid = messageStr.substring(didEndIdx + 1);
|
|
694
|
+
const payloadColonIdx = afterDid.indexOf(':');
|
|
695
|
+
if (payloadColonIdx > 0) {
|
|
696
|
+
type = afterDid.substring(0, payloadColonIdx);
|
|
697
|
+
payload = afterDid.substring(payloadColonIdx + 1);
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
type = afterDid;
|
|
701
|
+
payload = '';
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
type = 'message';
|
|
706
|
+
payload = messageStr.substring(didMarker.length);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
else if (messageStr.startsWith('REQ:')) {
|
|
710
|
+
// Request message format: REQ:<requestId>|<optional DID>|type:payload
|
|
711
|
+
const reqMatch = messageStr.match(/^REQ:([^|]+)\|(.*)$/);
|
|
712
|
+
if (reqMatch) {
|
|
713
|
+
requestId = reqMatch[1];
|
|
714
|
+
const afterReq = reqMatch[2];
|
|
715
|
+
// Check if it has DID prefix
|
|
716
|
+
if (afterReq.startsWith('DID:')) {
|
|
717
|
+
const didEndIdx = afterReq.indexOf('|');
|
|
718
|
+
if (didEndIdx > 0) {
|
|
719
|
+
did = afterReq.substring(4, didEndIdx);
|
|
720
|
+
const afterDid = afterReq.substring(didEndIdx + 1);
|
|
721
|
+
const payloadColonIdx = afterDid.indexOf(':');
|
|
722
|
+
if (payloadColonIdx > 0) {
|
|
723
|
+
type = afterDid.substring(0, payloadColonIdx);
|
|
724
|
+
payload = afterDid.substring(payloadColonIdx + 1);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
type = afterDid;
|
|
728
|
+
payload = '';
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
const payloadColonIdx = afterReq.indexOf(':');
|
|
734
|
+
if (payloadColonIdx > 0) {
|
|
735
|
+
type = afterReq.substring(0, payloadColonIdx);
|
|
736
|
+
payload = afterReq.substring(payloadColonIdx + 1);
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
type = afterReq;
|
|
740
|
+
payload = '';
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
type = 'message';
|
|
746
|
+
payload = messageStr.substring(4);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
else if (messageStr.startsWith('RESP:')) {
|
|
750
|
+
// Response message format: RESP:<requestId>|type:payload
|
|
751
|
+
const respMatch = messageStr.match(/^RESP:([^|]+)\|(.*)$/);
|
|
752
|
+
if (respMatch) {
|
|
753
|
+
requestId = respMatch[1];
|
|
754
|
+
const afterResp = respMatch[2];
|
|
755
|
+
const payloadColonIdx = afterResp.indexOf(':');
|
|
756
|
+
if (payloadColonIdx > 0) {
|
|
757
|
+
type = afterResp.substring(0, payloadColonIdx);
|
|
758
|
+
payload = afterResp.substring(payloadColonIdx + 1);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
type = afterResp;
|
|
762
|
+
payload = '';
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
type = 'response';
|
|
767
|
+
payload = messageStr.substring(5);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
type = colonIdx > 0 ? messageStr.substring(0, colonIdx) : messageStr;
|
|
772
|
+
payload = colonIdx > 0 ? messageStr.substring(colonIdx + 1) : '';
|
|
773
|
+
}
|
|
774
|
+
if (!network.persistentPeers.has(fromPeerId)) {
|
|
775
|
+
network.addPersistentPeer({
|
|
776
|
+
peerId: fromPeerId,
|
|
777
|
+
multiaddrs: connection.remoteAddr ? [connection.remoteAddr.toString()] : [],
|
|
778
|
+
did
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
const existing = network.persistentPeers.get(fromPeerId);
|
|
783
|
+
if (did && !existing.did) {
|
|
784
|
+
existing.did = did;
|
|
785
|
+
network.persistentPeers.set(fromPeerId, existing);
|
|
786
|
+
network.savePersistentPeers();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const handler = network.messageHandlers.get(type);
|
|
790
|
+
if (handler) {
|
|
791
|
+
handler(data, fromPeerId, did);
|
|
792
|
+
}
|
|
793
|
+
else if (type.startsWith('data.') && network.dataProviders.has(type)) {
|
|
794
|
+
// 2026-08-15: data.* 提供者 (如 data.llm-config) — 由上层注册, 返回 payload 回给请求方
|
|
795
|
+
try {
|
|
796
|
+
const provider = network.dataProviders.get(type);
|
|
797
|
+
const replyPayload = await provider(payload, fromPeerId, did);
|
|
798
|
+
if (replyPayload != null) {
|
|
799
|
+
// 回 data.llm-config.reply 语义: <type>.reply (不带 DID 前缀, 手机端解析按 type:payload 兼容)
|
|
800
|
+
const replyType = type + '.reply';
|
|
801
|
+
const encoded = new TextEncoder().encode(`${replyType}:${replyPayload}`);
|
|
802
|
+
console.log(`[P2P] data provider ${type} → reply ${replyType} (${replyPayload.length}B) to ${fromPeerId.slice(0, 10)}`);
|
|
803
|
+
await network.sendRawMessage(fromPeerId, encoded);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
catch (e) {
|
|
807
|
+
console.error(`[P2P] data provider ${type} error:`, e);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
else if (type === 'RESP' && requestId) {
|
|
811
|
+
// Handle response message - resolve the pending request
|
|
812
|
+
const pending = network.pendingResponses.get(requestId);
|
|
813
|
+
if (pending) {
|
|
814
|
+
clearTimeout(pending.timeout);
|
|
815
|
+
pending.resolve(payload);
|
|
816
|
+
network.pendingResponses.delete(requestId);
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
console.log(`[P2P] Response for unknown request ${requestId}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
else if (type === 'REQ' && requestId) {
|
|
823
|
+
// Handle request message - store for later response
|
|
824
|
+
network.storePendingRequest(requestId, { type: payload.split(':')[0] || 'request', payload, fromPeerId, did });
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
network.storeOfflineMessage(fromPeerId, data);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
catch (e) {
|
|
831
|
+
console.error(`[P2P] Message handler error:`, e);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
async sendRawMessage(peerId, data) {
|
|
836
|
+
if (!this.node)
|
|
837
|
+
throw new Error('Node not initialized');
|
|
838
|
+
// libp2p 3.x: 优先复用已建立连接 (手机经 ws 连接, 不能反向 tcp dial)
|
|
839
|
+
const conns = this.node.getConnections?.() || [];
|
|
840
|
+
for (const c of conns) {
|
|
841
|
+
if (c.remotePeer.toString() === peerId) {
|
|
842
|
+
// dialProtocol 返回 Stream 本体 (非 {stream})
|
|
843
|
+
const stream = await this.node.dialProtocol(c.remotePeer, '/agent/message');
|
|
844
|
+
stream.send(data);
|
|
845
|
+
await stream.close().catch(() => { });
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const ma = (0, multiaddr_1.multiaddr)(`/p2p/${peerId}`);
|
|
850
|
+
// libp2p 3.x: dialProtocol 返回 Stream 本体 (非 {stream})
|
|
851
|
+
const stream = await this.node.dialProtocol(ma, '/agent/message');
|
|
852
|
+
stream.send(data);
|
|
853
|
+
}
|
|
854
|
+
storeOfflineMessage(peerId, data) {
|
|
855
|
+
const messages = this.offlineMessages.get(peerId) || [];
|
|
856
|
+
messages.push(data);
|
|
857
|
+
this.offlineMessages.set(peerId, messages);
|
|
858
|
+
console.log(`[P2P] Stored offline message for ${peerId}, total: ${messages.length}`);
|
|
859
|
+
}
|
|
860
|
+
getOfflineMessages(peerId) {
|
|
861
|
+
const messages = this.offlineMessages.get(peerId) || [];
|
|
862
|
+
this.offlineMessages.delete(peerId);
|
|
863
|
+
return messages;
|
|
864
|
+
}
|
|
865
|
+
storePendingRequest(requestId, info) {
|
|
866
|
+
this.pendingRequests.set(requestId, {
|
|
867
|
+
...info,
|
|
868
|
+
requestId,
|
|
869
|
+
timestamp: Date.now()
|
|
870
|
+
});
|
|
871
|
+
console.log(`[P2P] Stored pending request ${requestId} from ${info.fromPeerId}`);
|
|
872
|
+
}
|
|
873
|
+
getPendingRequest(requestId) {
|
|
874
|
+
return this.pendingRequests.get(requestId);
|
|
875
|
+
}
|
|
876
|
+
removePendingRequest(requestId) {
|
|
877
|
+
this.pendingRequests.delete(requestId);
|
|
878
|
+
}
|
|
879
|
+
getPendingRequests() {
|
|
880
|
+
return Array.from(this.pendingRequests.values());
|
|
881
|
+
}
|
|
882
|
+
onMessage(type, handler) {
|
|
883
|
+
this.messageHandlers.set(type, handler);
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Send a message and wait for a response (request-response pattern)
|
|
887
|
+
*/
|
|
888
|
+
async sendRequest(peerId, type, payload, timeoutMs = 30000) {
|
|
889
|
+
if (!this.node) {
|
|
890
|
+
throw new Error('Node not initialized');
|
|
891
|
+
}
|
|
892
|
+
const requestId = crypto.randomUUID();
|
|
893
|
+
let data;
|
|
894
|
+
if (this.ownDid) {
|
|
895
|
+
data = new TextEncoder().encode(`REQ:${requestId}|DID:${this.ownDid}|${type}:${payload}`);
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
data = new TextEncoder().encode(`REQ:${requestId}|${type}:${payload}`);
|
|
899
|
+
}
|
|
900
|
+
return new Promise((resolve, reject) => {
|
|
901
|
+
const timeout = setTimeout(() => {
|
|
902
|
+
this.pendingResponses.delete(requestId);
|
|
903
|
+
reject(new Error(`Request to ${peerId} timed out after ${timeoutMs}ms`));
|
|
904
|
+
}, timeoutMs);
|
|
905
|
+
// Store the pending response handler
|
|
906
|
+
this.pendingResponses.set(requestId, {
|
|
907
|
+
resolve: (response) => {
|
|
908
|
+
clearTimeout(timeout);
|
|
909
|
+
resolve(response);
|
|
910
|
+
},
|
|
911
|
+
reject: (err) => {
|
|
912
|
+
clearTimeout(timeout);
|
|
913
|
+
reject(err);
|
|
914
|
+
},
|
|
915
|
+
timeout
|
|
916
|
+
});
|
|
917
|
+
this.sendRawMessage(peerId, data).catch(err => {
|
|
918
|
+
clearTimeout(timeout);
|
|
919
|
+
this.pendingResponses.delete(requestId);
|
|
920
|
+
reject(err);
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
pendingResponses = new Map();
|
|
925
|
+
/**
|
|
926
|
+
* Register a handler for responses (used by the receiving side)
|
|
927
|
+
*/
|
|
928
|
+
onResponse(type, handler) {
|
|
929
|
+
// Store as pendingResponseHandlers-shaped wrapper. Extra args (did, requestId) are not
|
|
930
|
+
// available in pendingResponseHandlers signature, so ignore them when invoked.
|
|
931
|
+
this.pendingResponseHandlers.set(type, (responseData, from) => {
|
|
932
|
+
handler(responseData, from, undefined, undefined);
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Send a response back to a peer
|
|
937
|
+
*/
|
|
938
|
+
async sendResponse(peerId, requestId, type, responsePayload) {
|
|
939
|
+
if (!this.node) {
|
|
940
|
+
throw new Error('Node not initialized');
|
|
941
|
+
}
|
|
942
|
+
const data = new TextEncoder().encode(`RESP:${requestId}|${type}:${responsePayload}`);
|
|
943
|
+
try {
|
|
944
|
+
await this.sendRawMessage(peerId, data);
|
|
945
|
+
}
|
|
946
|
+
catch (e) {
|
|
947
|
+
console.warn(`[P2P] Failed to send response to ${peerId}:`, e);
|
|
948
|
+
throw e;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Handle incoming request messages and route to appropriate handlers
|
|
953
|
+
*/
|
|
954
|
+
handleRequest(type, payload, requestId, fromPeerId, did) {
|
|
955
|
+
const handler = this.messageHandlers.get(type);
|
|
956
|
+
if (handler) {
|
|
957
|
+
// Forward raw payload; callers register with onMessage() and adapt as needed.
|
|
958
|
+
handler(new TextEncoder().encode(payload), fromPeerId, did);
|
|
959
|
+
}
|
|
960
|
+
// Check if there's a response handler registered
|
|
961
|
+
const responseHandler = this.pendingResponseHandlers.get(type);
|
|
962
|
+
if (responseHandler) {
|
|
963
|
+
responseHandler(payload, fromPeerId);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Register a pending response handler for a specific message type
|
|
968
|
+
*/
|
|
969
|
+
registerResponseHandler(type, handler) {
|
|
970
|
+
this.pendingResponseHandlers.set(type, handler);
|
|
971
|
+
}
|
|
972
|
+
async sendMessage(peerId, type, payload) {
|
|
973
|
+
if (!this.node)
|
|
974
|
+
throw new Error('Node not initialized');
|
|
975
|
+
let data;
|
|
976
|
+
if (this.ownDid) {
|
|
977
|
+
data = new TextEncoder().encode(`DID:${this.ownDid}|${type}:${payload}`);
|
|
978
|
+
}
|
|
979
|
+
else {
|
|
980
|
+
data = new TextEncoder().encode(`${type}:${payload}`);
|
|
981
|
+
}
|
|
982
|
+
try {
|
|
983
|
+
await this.sendRawMessage(peerId, data);
|
|
984
|
+
}
|
|
985
|
+
catch (e) {
|
|
986
|
+
console.warn(`[P2P] Failed to send to ${peerId}, storing offline`);
|
|
987
|
+
this.storeOfflineMessage(peerId, data);
|
|
988
|
+
this.scheduleReconnect(peerId);
|
|
989
|
+
// 如果有持久化存储,也存入离线队列
|
|
990
|
+
if (this.messageStore) {
|
|
991
|
+
const payloadBase64 = btoa(String.fromCharCode(...data));
|
|
992
|
+
this.messageStore.enqueueOfflineMessage({
|
|
993
|
+
targetNodeId: peerId,
|
|
994
|
+
type,
|
|
995
|
+
payload: payloadBase64,
|
|
996
|
+
createdAt: Date.now(),
|
|
997
|
+
transport: 'libp2p',
|
|
998
|
+
retryCount: 0,
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
scheduleReconnect(peerId) {
|
|
1004
|
+
if (this.reconnectTimers.has(peerId))
|
|
1005
|
+
return;
|
|
1006
|
+
const peerInfo = this.persistentPeers.get(peerId);
|
|
1007
|
+
if (!peerInfo || !peerInfo.multiaddrs || peerInfo.multiaddrs.length === 0) {
|
|
1008
|
+
console.log(`[P2P] No stored addresses for ${peerId}, cannot reconnect`);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
console.log(`[P2P] Scheduling reconnect for ${peerId} in ${RECONNECT_DELAY_MS}ms`);
|
|
1012
|
+
const timer = setTimeout(() => {
|
|
1013
|
+
this.reconnectTimers.delete(peerId);
|
|
1014
|
+
if (this.node) {
|
|
1015
|
+
this.attemptReconnect(peerId, peerInfo);
|
|
1016
|
+
}
|
|
1017
|
+
}, RECONNECT_DELAY_MS);
|
|
1018
|
+
this.reconnectTimers.set(peerId, timer);
|
|
1019
|
+
}
|
|
1020
|
+
async broadcast(type, payload) {
|
|
1021
|
+
if (!this.node)
|
|
1022
|
+
throw new Error('Node not initialized');
|
|
1023
|
+
const peers = this.node.getPeers();
|
|
1024
|
+
let data;
|
|
1025
|
+
if (this.ownDid) {
|
|
1026
|
+
data = new TextEncoder().encode(`DID:${this.ownDid}|${type}:${payload}`);
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
data = new TextEncoder().encode(`${type}:${payload}`);
|
|
1030
|
+
}
|
|
1031
|
+
for (const peer of peers) {
|
|
1032
|
+
const peerIdStr = peer.toString();
|
|
1033
|
+
try {
|
|
1034
|
+
await this.sendRawMessage(peerIdStr, data);
|
|
1035
|
+
}
|
|
1036
|
+
catch (e) {
|
|
1037
|
+
console.warn(`[P2P] Failed to broadcast to ${peerIdStr}:`, e);
|
|
1038
|
+
this.scheduleReconnect(peerIdStr);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
getPeers() {
|
|
1043
|
+
if (!this.node)
|
|
1044
|
+
return [];
|
|
1045
|
+
return this.node.getPeers().map((p) => p.toString());
|
|
1046
|
+
}
|
|
1047
|
+
getConnectedPeers() {
|
|
1048
|
+
if (!this.node)
|
|
1049
|
+
return [];
|
|
1050
|
+
const result = [];
|
|
1051
|
+
for (const peer of this.node.getPeers()) {
|
|
1052
|
+
const peerIdStr = peer.toString();
|
|
1053
|
+
const peerInfo = this.persistentPeers.get(peerIdStr);
|
|
1054
|
+
result.push({
|
|
1055
|
+
peerId: peerIdStr,
|
|
1056
|
+
did: peerInfo?.did,
|
|
1057
|
+
name: peerInfo?.name
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
setOwnDid(did) {
|
|
1063
|
+
this.ownDid = did;
|
|
1064
|
+
}
|
|
1065
|
+
getNode() {
|
|
1066
|
+
return this.node;
|
|
1067
|
+
}
|
|
1068
|
+
async shutdown() {
|
|
1069
|
+
for (const timer of this.reconnectTimers.values()) {
|
|
1070
|
+
clearTimeout(timer);
|
|
1071
|
+
}
|
|
1072
|
+
this.reconnectTimers.clear();
|
|
1073
|
+
if (this.offlineDeliveryInterval) {
|
|
1074
|
+
clearInterval(this.offlineDeliveryInterval);
|
|
1075
|
+
}
|
|
1076
|
+
if (this.messageStore) {
|
|
1077
|
+
await this.messageStore.shutdown();
|
|
1078
|
+
this.messageStore = null;
|
|
1079
|
+
}
|
|
1080
|
+
if (this.node) {
|
|
1081
|
+
await this.node.stop();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* 获取消息历史
|
|
1086
|
+
*/
|
|
1087
|
+
async getMessageHistory(options) {
|
|
1088
|
+
if (!this.messageStore)
|
|
1089
|
+
return [];
|
|
1090
|
+
return this.messageStore.getMessages(options);
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* 获取待投递消息数量
|
|
1094
|
+
*/
|
|
1095
|
+
async getPendingOfflineCount() {
|
|
1096
|
+
if (!this.messageStore)
|
|
1097
|
+
return 0;
|
|
1098
|
+
return this.messageStore.getPendingOfflineCount();
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
exports.P2PNetwork = P2PNetwork;
|
|
1102
|
+
exports.p2pNetwork = new P2PNetwork();
|
|
1103
|
+
//# sourceMappingURL=p2p.js.map
|