@bolloon/bolloon-agent 0.4.27 → 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/execution-supervisor.js +21 -0
- package/dist/agents/pi-sdk-tools.js +3 -1
- package/dist/agents/pi-sdk.js +4 -1
- package/dist/agents/task/local-seller.js +149 -0
- package/dist/agents/task/report-card.js +146 -0
- package/dist/agents/task/resource-advisor.js +182 -0
- package/dist/agents/task/task-budget.js +103 -0
- package/dist/agents/task/task-runner.js +618 -0
- package/dist/agents/x402/goal-run-bridge.js +10 -6
- package/dist/agents/x402/resource-contract.js +12 -1
- package/dist/cli/update-commands.js +349 -0
- package/dist/cli-entry.js +133 -53
- 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,716 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* update-manager.ts — 更新系统的**唯一决策者** (Phase 2/4/5, 2026-09-19)
|
|
3
|
+
*
|
|
4
|
+
* 职责只有三件 (不多做):
|
|
5
|
+
* ① 识别当前安装 (`detectInstallation`)
|
|
6
|
+
* ② 查询目标版本 (npm registry, 唯一稳定渠道)
|
|
7
|
+
* ③ 形成更新计划 (`buildUpdatePlan`) / 执行更新 (`applyUpdate`)
|
|
8
|
+
*
|
|
9
|
+
* 它**不**直接改用户配置, **不**默认自动安装, **不**碰 `~/.bolloon` 的用户数据
|
|
10
|
+
* (goals / runs / transactions / skills / config 一律不动)。
|
|
11
|
+
*
|
|
12
|
+
* 渠道决定 (2026-09-19, 冻结): **npm 是唯一稳定发行渠道**; GitHub 只作为源码与发布记录。
|
|
13
|
+
* 因此安装脚本不再优先查 GitHub Releases, 版本解析也只有一份 (本模块)。
|
|
14
|
+
*
|
|
15
|
+
* 与计划的刻意偏差 (如实记录, 见 docs/wiki/update-protocol.md §5):
|
|
16
|
+
* "安装到临时位置 → 原子切换" 落成 **"临时位置下载并校验 tarball → 交给 npm 完成替换 → 验证可启动 → 失败回滚"**。
|
|
17
|
+
* 理由: 手工把整棵 node_modules (949 个包) 复制/切换一遍, 比 npm 自己的替换更危险也更容易半更新;
|
|
18
|
+
* 真正要保的性质 ("不能删掉旧版本后才发现新版本起不来") 由**切换后验证 + 失败回滚**保证, 这两步是真跑的。
|
|
19
|
+
*/
|
|
20
|
+
import * as os from 'os';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import * as fs from 'fs';
|
|
23
|
+
import * as fsp from 'fs/promises';
|
|
24
|
+
import * as https from 'https';
|
|
25
|
+
import * as http from 'http';
|
|
26
|
+
import { spawnSync } from 'child_process';
|
|
27
|
+
import { PKG_NAME, CONSTRAINT_PKG_NAME, NPM_REGISTRY_BASE, resolveUpdateChannel, distTagForChannel, detectInstallation, readPackageAt, } from './version-info.js';
|
|
28
|
+
import { readUpdateState, writeUpdateState, appendUpdateHistory, readUpdateHistory, acquireUpdateLock, releaseUpdateLock, readUpdateLock, lockIsStale, readUpdatePrefs, } from './update-state.js';
|
|
29
|
+
// ── 版本比较 (唯一一份) ─────────────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* 从子进程 stdout 里取 JSON —— **不要**按"行首是否 { "过滤:
|
|
32
|
+
* `JSON.stringify(x, null, 2)` 是**多行**的, 只留第一行 `{` 会解析失败
|
|
33
|
+
* (真跑抓到过: 更新后验证永远判失败 → 每次都"回滚").
|
|
34
|
+
*/
|
|
35
|
+
export function parseJsonFromStdout(stdout) {
|
|
36
|
+
const s = String(stdout || '');
|
|
37
|
+
const start = s.indexOf('{');
|
|
38
|
+
if (start < 0)
|
|
39
|
+
return null;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(s.slice(start));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// 尾部可能有非 JSON 输出: 退一步只取到最后一个 }
|
|
45
|
+
const end = s.lastIndexOf('}');
|
|
46
|
+
if (end > start) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(s.slice(start, end + 1));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function parseVersion(v) {
|
|
58
|
+
const clean = String(v || '').trim().replace(/^v/, '').split('-')[0];
|
|
59
|
+
const parts = clean.split('.').map((p) => parseInt(p.replace(/\D.*$/, ''), 10));
|
|
60
|
+
return parts.map((n) => (Number.isFinite(n) ? n : 0));
|
|
61
|
+
}
|
|
62
|
+
/** -1 = a<b, 0 = 相等, 1 = a>b (只比数值段, 忽略预发布标签) */
|
|
63
|
+
export function compareVersions(a, b) {
|
|
64
|
+
const pa = parseVersion(a);
|
|
65
|
+
const pb = parseVersion(b);
|
|
66
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
67
|
+
const x = pa[i] ?? 0;
|
|
68
|
+
const y = pb[i] ?? 0;
|
|
69
|
+
if (x < y)
|
|
70
|
+
return -1;
|
|
71
|
+
if (x > y)
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
export function isKnownVersion(v) {
|
|
77
|
+
return !!v && v !== 'unknown' && /^\d/.test(String(v).trim().replace(/^v/, ''));
|
|
78
|
+
}
|
|
79
|
+
function httpGetJson(url, timeoutMs = 10000) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const client = url.startsWith('https') ? https : http;
|
|
82
|
+
const req = client.get(url, { timeout: timeoutMs, headers: { Accept: 'application/json' } }, (res) => {
|
|
83
|
+
let data = '';
|
|
84
|
+
res.on('data', (c) => { data += c; });
|
|
85
|
+
res.on('end', () => resolve({ status: res.statusCode || 0, body: data }));
|
|
86
|
+
});
|
|
87
|
+
req.on('error', reject);
|
|
88
|
+
req.on('timeout', () => { req.destroy(new Error('timeout')); });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const OFFLINE_CODES = new Set(['ENOTFOUND', 'EAI_AGAIN', 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'EHOSTUNREACH', 'ENETUNREACH', 'ENETDOWN', 'EAI_FAIL', 'UND_ERR_CONNECT_TIMEOUT']);
|
|
92
|
+
/** 网络类错误 → offline; 其它 (HTTP 5xx / 解析失败 / 包不存在) → registry_unavailable。 */
|
|
93
|
+
export function classifyRegistryError(err, httpStatus) {
|
|
94
|
+
const code = String(err?.code || '');
|
|
95
|
+
if (OFFLINE_CODES.has(code) || /timeout|timed out|ENOTFOUND|getaddrinfo|network|socket hang up|超时/i.test(String(err?.message || ''))) {
|
|
96
|
+
return { kind: 'offline', detail: `${code || err?.message || '网络不可达'}` };
|
|
97
|
+
}
|
|
98
|
+
if (httpStatus && httpStatus >= 500)
|
|
99
|
+
return { kind: 'registry_unavailable', detail: `registry 返回 HTTP ${httpStatus}` };
|
|
100
|
+
if (httpStatus === 404)
|
|
101
|
+
return { kind: 'registry_unavailable', detail: `registry 上没有 ${PKG_NAME} (HTTP 404)` };
|
|
102
|
+
if (httpStatus && httpStatus >= 400)
|
|
103
|
+
return { kind: 'registry_unavailable', detail: `registry 返回 HTTP ${httpStatus}` };
|
|
104
|
+
return { kind: 'registry_unavailable', detail: code || err?.message || '未知错误' };
|
|
105
|
+
}
|
|
106
|
+
export async function queryRegistryDoc(pkg = PKG_NAME, timeoutMs = 10000) {
|
|
107
|
+
const url = `${NPM_REGISTRY_BASE}/${encodeURIComponent(pkg).replace('%40', '@')}`;
|
|
108
|
+
try {
|
|
109
|
+
const { status, body } = await httpGetJson(url, timeoutMs);
|
|
110
|
+
if (status < 200 || status >= 300)
|
|
111
|
+
return { ok: false, ...classifyRegistryError(null, status) };
|
|
112
|
+
let raw;
|
|
113
|
+
try {
|
|
114
|
+
raw = JSON.parse(body);
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
return { ok: false, kind: 'registry_unavailable', detail: 'registry 返回了无法解析的内容' };
|
|
118
|
+
}
|
|
119
|
+
const distTags = (raw?.['dist-tags'] || {});
|
|
120
|
+
const versions = Object.keys(raw?.versions || {});
|
|
121
|
+
if (!distTags.latest && versions.length === 0) {
|
|
122
|
+
return { ok: false, kind: 'registry_unavailable', detail: 'registry 上没有可用版本' };
|
|
123
|
+
}
|
|
124
|
+
const gitHeads = {};
|
|
125
|
+
for (const v of versions) {
|
|
126
|
+
const gh = raw.versions[v]?.gitHead;
|
|
127
|
+
if (typeof gh === 'string' && gh)
|
|
128
|
+
gitHeads[v] = gh;
|
|
129
|
+
}
|
|
130
|
+
return { ok: true, doc: { latest: distTags.latest || versions[versions.length - 1], distTags, versions, gitHeads } };
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
return { ok: false, ...classifyRegistryError(e) };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 检查更新。**这是唯一的检查入口** —— CLI / 启动后台 / Python 检查器 / 安装脚本都走它。
|
|
138
|
+
*
|
|
139
|
+
* 结论优先级 (刻意定死, 防"模糊行为"):
|
|
140
|
+
* 1. 读不到本地版本 → local_version_unknown (绝不默认 0.0.0 后继续)
|
|
141
|
+
* 2. 安装方式不支持自动更新 → unsupported_installation (仍会带出 latestVersion)
|
|
142
|
+
* 3. 显式跳过 / 节流 → check_skipped (结论来自缓存)
|
|
143
|
+
* 4. 网络不可达 → offline (绝不显示"已是最新")
|
|
144
|
+
* 5. registry 不可用 / 包不存在 → registry_unavailable
|
|
145
|
+
* 6. latest > current → update_available
|
|
146
|
+
* 7. 否则 → up_to_date
|
|
147
|
+
*/
|
|
148
|
+
export async function checkForUpdate(opts = {}) {
|
|
149
|
+
const home = opts.home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
150
|
+
const prefs = await readUpdatePrefs({ home, env: opts.env });
|
|
151
|
+
const channel = resolveUpdateChannel(opts.channel || prefs.channel);
|
|
152
|
+
const install = opts.installation || detectInstallation({ home });
|
|
153
|
+
const pkg = readPackageAt(install.packageRoot);
|
|
154
|
+
const current = isKnownVersion(pkg?.version) ? String(pkg?.version) : 'unknown';
|
|
155
|
+
const now = new Date().toISOString();
|
|
156
|
+
const state = await readUpdateState(home);
|
|
157
|
+
const persist = opts.persist !== false;
|
|
158
|
+
const base = {
|
|
159
|
+
currentVersion: current,
|
|
160
|
+
channel,
|
|
161
|
+
source: install.updateSource,
|
|
162
|
+
installMethod: install.method,
|
|
163
|
+
checkedAt: now,
|
|
164
|
+
fromCache: false,
|
|
165
|
+
};
|
|
166
|
+
const commit = async (r) => {
|
|
167
|
+
if (persist) {
|
|
168
|
+
await writeUpdateState({
|
|
169
|
+
currentVersion: r.currentVersion,
|
|
170
|
+
latestVersion: r.latestVersion,
|
|
171
|
+
channel: r.channel,
|
|
172
|
+
installMethod: r.installMethod,
|
|
173
|
+
installDir: install.installDir,
|
|
174
|
+
entryPath: install.entryPath,
|
|
175
|
+
nodeVersion: process.version.replace(/^v/, ''),
|
|
176
|
+
platform: os.platform(),
|
|
177
|
+
arch: os.arch(),
|
|
178
|
+
lastCheckAt: r.checkedAt,
|
|
179
|
+
lastCheckStatus: r.status,
|
|
180
|
+
lastCheckReason: r.reason,
|
|
181
|
+
}, home);
|
|
182
|
+
}
|
|
183
|
+
return r;
|
|
184
|
+
};
|
|
185
|
+
// 1. 本地版本读不到 —— 不猜
|
|
186
|
+
if (!isKnownVersion(current)) {
|
|
187
|
+
return commit({ ...base, status: 'local_version_unknown', latestVersion: null, reason: `读不到 ${install.packageRoot}/package.json 的版本` });
|
|
188
|
+
}
|
|
189
|
+
// 3. 显式跳过 / 节流
|
|
190
|
+
const throttled = !opts.force && state.lastCheckAt
|
|
191
|
+
? (Date.now() - Date.parse(state.lastCheckAt)) < prefs.checkIntervalHours * 3600_000
|
|
192
|
+
: false;
|
|
193
|
+
const useCache = !!opts.offline || throttled;
|
|
194
|
+
if (useCache && state.lastCheckStatus) {
|
|
195
|
+
// 语义: 本次**没有真的检查** → 结论就是 check_skipped, 上次的结论原样带出来
|
|
196
|
+
// (绝不把缓存里的 update_available/up_to_date 冒充成"刚查出来的")
|
|
197
|
+
const cached = state.lastCheckStatus;
|
|
198
|
+
const r = await commit({
|
|
199
|
+
...base, fromCache: true,
|
|
200
|
+
status: 'check_skipped',
|
|
201
|
+
cachedStatus: cached,
|
|
202
|
+
latestVersion: state.latestVersion,
|
|
203
|
+
reason: opts.offline
|
|
204
|
+
? `离线模式: 使用缓存结论 (${cached})`
|
|
205
|
+
: `距上次检查不足 ${prefs.checkIntervalHours}h, 使用缓存结论 (${cached})`,
|
|
206
|
+
checkedAt: state.lastCheckAt || now,
|
|
207
|
+
});
|
|
208
|
+
if (persist)
|
|
209
|
+
await writeUpdateState({ lastCheckReason: r.reason }, home);
|
|
210
|
+
return r;
|
|
211
|
+
}
|
|
212
|
+
// 2. 安装方式不支持自动更新 —— 仍把 latest / 可回滚性带出来, 但结论就是"不支持"
|
|
213
|
+
const registry = opts.registry || await queryRegistryDoc(PKG_NAME);
|
|
214
|
+
const latest = registry.ok ? (registry.doc.distTags[distTagForChannel(channel)] || registry.doc.latest) : null;
|
|
215
|
+
const targetPublishedOf = (v) => v && registry.ok ? registry.doc.versions.includes(v) : undefined;
|
|
216
|
+
const rollbackSupportedOf = registry.ok ? registry.doc.versions.includes(current) : undefined;
|
|
217
|
+
if (install.method === 'development' || install.method === 'unknown' || install.method === 'release-binary') {
|
|
218
|
+
return commit({
|
|
219
|
+
...base, status: 'unsupported_installation', latestVersion: latest,
|
|
220
|
+
reason: `${install.reason}; 这种安装方式不支持自动更新`,
|
|
221
|
+
targetPublished: targetPublishedOf(latest),
|
|
222
|
+
rollbackSupported: rollbackSupportedOf,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
// 4/5. registry 拿不到 —— 绝不显示"已是最新"
|
|
226
|
+
if (!registry.ok) {
|
|
227
|
+
return commit({
|
|
228
|
+
...base,
|
|
229
|
+
status: registry.kind === 'offline' ? 'offline' : 'registry_unavailable',
|
|
230
|
+
latestVersion: null,
|
|
231
|
+
reason: registry.detail,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const targetPublished = !!latest && registry.doc.versions.includes(latest);
|
|
235
|
+
const rollbackSupported = rollbackSupportedOf === true;
|
|
236
|
+
// 6/7. 比较
|
|
237
|
+
if (latest && compareVersions(current, latest) < 0) {
|
|
238
|
+
return commit({
|
|
239
|
+
...base, status: 'update_available', latestVersion: latest, targetPublished, rollbackSupported,
|
|
240
|
+
reason: `当前 ${current} → 目标 ${latest}`,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return commit({ ...base, status: 'up_to_date', latestVersion: latest, targetPublished, rollbackSupported });
|
|
244
|
+
}
|
|
245
|
+
const WILL_NOT_TOUCH = [
|
|
246
|
+
'~/.bolloon/config.json',
|
|
247
|
+
'~/.bolloon/goals/',
|
|
248
|
+
'~/.bolloon/runs/',
|
|
249
|
+
'~/.bolloon/transactions/',
|
|
250
|
+
'~/.bolloon/skills/',
|
|
251
|
+
'~/.bolloon/sessions/',
|
|
252
|
+
'~/.bolloon/identity/',
|
|
253
|
+
];
|
|
254
|
+
/** 磁盘余量 (字节)。statfs 不可用时返回 null (不假装知道)。 */
|
|
255
|
+
export function freeBytesAt(dir) {
|
|
256
|
+
try {
|
|
257
|
+
const st = fs.statfsSync?.(dir);
|
|
258
|
+
if (!st)
|
|
259
|
+
return null;
|
|
260
|
+
return Number(st.bavail) * Number(st.bsize);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export const MIN_FREE_BYTES = 300 * 1024 * 1024;
|
|
267
|
+
/** 安装类风险: 与"机器上有没有在跑的任务"无关, 任何计划都要评估 (测试也不许跳过)。 */
|
|
268
|
+
async function collectInstallRisk(install) {
|
|
269
|
+
const out = [];
|
|
270
|
+
out.push({
|
|
271
|
+
id: 'install_method', label: '安装方式支持自动更新', ok: install.method === 'npm-global', blocking: true,
|
|
272
|
+
detail: install.method === 'npm-global'
|
|
273
|
+
? `npm 全局安装 (${install.installDir})`
|
|
274
|
+
: `${install.method}: ${install.reason} (请用对应方式更新: git pull + npm run build:all / 项目的包管理)`,
|
|
275
|
+
});
|
|
276
|
+
out.push({
|
|
277
|
+
id: 'install_dir_writable', label: '安装目录可写', ok: install.writable, blocking: true,
|
|
278
|
+
detail: install.writable ? install.installDir : `${install.installDir} 不可写 (需要 sudo 或改 npm prefix)`,
|
|
279
|
+
});
|
|
280
|
+
const free = freeBytesAt(install.installDir);
|
|
281
|
+
out.push({
|
|
282
|
+
id: 'disk_space', label: '磁盘空间充足', ok: free === null ? true : free >= MIN_FREE_BYTES,
|
|
283
|
+
blocking: free === null ? false : free < MIN_FREE_BYTES,
|
|
284
|
+
detail: free === null ? '无法读取磁盘余量 (跳过)' : `${(free / 1024 / 1024 / 1024).toFixed(2)} GiB 可用 (需 ≥ ${(MIN_FREE_BYTES / 1024 / 1024).toFixed(0)} MiB)`,
|
|
285
|
+
});
|
|
286
|
+
const lock = readUpdateLock();
|
|
287
|
+
out.push({
|
|
288
|
+
id: 'update_lock', label: '没有其它更新进程', ok: !lock || lockIsStale(lock), blocking: !!lock && !lockIsStale(lock),
|
|
289
|
+
detail: !lock ? '无更新锁'
|
|
290
|
+
: lockIsStale(lock) ? `存在陈旧锁 (pid ${lock.pid}, ${lock.at}) — 可回收`
|
|
291
|
+
: `另一个更新进程持有锁 (pid ${lock.pid}, ${lock.at})`,
|
|
292
|
+
});
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
295
|
+
/** 负载类风险: Supervisor / Goal / Run / 支付 (非阻塞提醒, 但会把默认策略变成"等 Run 结束")。 */
|
|
296
|
+
async function collectWorkloadRisk() {
|
|
297
|
+
const out = [];
|
|
298
|
+
try {
|
|
299
|
+
const { supervisorStatePath } = await import('../agents/supervisor-host.js');
|
|
300
|
+
let sup = null;
|
|
301
|
+
try {
|
|
302
|
+
sup = JSON.parse(await fsp.readFile(supervisorStatePath(), 'utf8'));
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
sup = null;
|
|
306
|
+
}
|
|
307
|
+
const alive = sup && typeof sup.pid === 'number' ? (() => { try {
|
|
308
|
+
process.kill(sup.pid, 0);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
return e?.code === 'EPERM';
|
|
313
|
+
} })() : false;
|
|
314
|
+
const running = !!sup && alive && !sup.stoppedAt;
|
|
315
|
+
out.push({
|
|
316
|
+
id: 'supervisor_running', label: 'Supervisor 未在运行', ok: !running, blocking: false, advisory: true,
|
|
317
|
+
detail: running ? `Supervisor 正在运行 (pid ${sup.pid}, owner ${sup.owner}, 最近 tick ${sup.lastTickAt || '未知'})` : '无运行中的 Supervisor 宿主',
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
out.push({ id: 'supervisor_running', label: 'Supervisor 未在运行', ok: true, blocking: false, advisory: true, detail: '无法读取 supervisor.json (按未运行处理)' });
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
const { listGoals } = await import('../agents/goal-store.js');
|
|
325
|
+
const goals = await listGoals({ status: ['open', 'active', 'recovering', 'retry_wait', 'awaiting_external', 'stalled'], limit: 50 });
|
|
326
|
+
out.push({
|
|
327
|
+
id: 'active_goals', label: '没有进行中的 Goal', ok: goals.length === 0, blocking: false, advisory: true,
|
|
328
|
+
detail: goals.length === 0 ? '无进行中 Goal' : `${goals.length} 个未收尾 Goal (${goals.slice(0, 3).map((g) => `${g.goalId}:${g.status}`).join(', ')}${goals.length > 3 ? ', …' : ''})`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
out.push({ id: 'active_goals', label: '没有进行中的 Goal', ok: true, blocking: false, advisory: true, detail: '无法读取 goals (按无处理)' });
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const { listRuns } = await import('../agents/run-store.js');
|
|
336
|
+
const runs = await listRuns({ status: ['queued', 'running', 'recovering', 'paused', 'awaiting_external', 'interrupted', 'stalled'], limit: 50 });
|
|
337
|
+
out.push({
|
|
338
|
+
id: 'active_runs', label: '没有进行中的 Run', ok: runs.length === 0, blocking: false, advisory: true,
|
|
339
|
+
detail: runs.length === 0 ? '无进行中 Run' : `${runs.length} 个未收尾 Run (${runs.slice(0, 3).map((r) => `${r.runId}:${r.status}`).join(', ')}${runs.length > 3 ? ', …' : ''})`,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
out.push({ id: 'active_runs', label: '没有进行中的 Run', ok: true, blocking: false, advisory: true, detail: '无法读取 runs (按无处理)' });
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
const { pendingTransactions } = await import('../agents/x402/transaction-store.js');
|
|
347
|
+
const pend = await pendingTransactions();
|
|
348
|
+
const paying = pend.filter((t) => ['paying', 'payment_required'].includes(t.status));
|
|
349
|
+
out.push({
|
|
350
|
+
id: 'payment_in_flight', label: '没有支付中的交易', ok: paying.length === 0, blocking: false, advisory: true,
|
|
351
|
+
detail: paying.length === 0
|
|
352
|
+
? (pend.length ? `${pend.length} 笔待收尾交易 (无支付中, 可继续)` : '无待收尾交易')
|
|
353
|
+
: `${paying.length} 笔支付中/待付交易 (${paying.slice(0, 3).map((t) => t.id || t.transactionId).join(', ')}) — 更新前应先对账`,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
out.push({ id: 'payment_in_flight', label: '没有支付中的交易', ok: true, blocking: false, advisory: true, detail: '无法读取 transactions (按无处理)' });
|
|
358
|
+
}
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
export async function buildUpdatePlan(opts = {}) {
|
|
362
|
+
const home = opts.home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
363
|
+
const install = opts.installation || detectInstallation({ home });
|
|
364
|
+
const check = await checkForUpdate({ ...opts, home, persist: false, installation: install });
|
|
365
|
+
const risk = [
|
|
366
|
+
...await collectInstallRisk(install),
|
|
367
|
+
...(opts.workloadRiskOverride || await collectWorkloadRisk()),
|
|
368
|
+
];
|
|
369
|
+
// registry 侧风险项来自检查结论
|
|
370
|
+
risk.push({
|
|
371
|
+
id: 'registry_reachable',
|
|
372
|
+
label: 'registry 可达',
|
|
373
|
+
ok: check.status !== 'offline' && check.status !== 'registry_unavailable',
|
|
374
|
+
blocking: check.status === 'offline' || check.status === 'registry_unavailable' || check.status === 'local_version_unknown',
|
|
375
|
+
detail: check.status === 'offline' ? `离线: ${check.reason}`
|
|
376
|
+
: check.status === 'registry_unavailable' ? `registry 不可用: ${check.reason}`
|
|
377
|
+
: 'npm registry 可达',
|
|
378
|
+
});
|
|
379
|
+
risk.push({
|
|
380
|
+
id: 'target_published',
|
|
381
|
+
label: '目标版本真实存在',
|
|
382
|
+
ok: check.targetPublished !== false,
|
|
383
|
+
blocking: !!check.latestVersion && check.targetPublished === false,
|
|
384
|
+
detail: check.targetPublished === false
|
|
385
|
+
? `registry 上没有 ${PKG_NAME}@${check.latestVersion} — 不执行更新`
|
|
386
|
+
: check.targetPublished === true ? `已确认 ${check.latestVersion} 可下载` : '未检查 (离线)',
|
|
387
|
+
});
|
|
388
|
+
risk.push({
|
|
389
|
+
id: 'rollback_supported',
|
|
390
|
+
label: '当前版本可回滚',
|
|
391
|
+
ok: check.rollbackSupported !== false,
|
|
392
|
+
blocking: false,
|
|
393
|
+
detail: check.rollbackSupported === false
|
|
394
|
+
? `registry 上找不到当前版本 ${check.currentVersion} — 更新失败将无法用 npm 回滚`
|
|
395
|
+
: check.rollbackSupported === true ? `registry 上存在 ${check.currentVersion} (可回滚)` : '未检查 (离线)',
|
|
396
|
+
});
|
|
397
|
+
const blockers = risk.filter((r) => !r.ok && r.blocking).map((r) => `${r.label}: ${r.detail}`);
|
|
398
|
+
const advisories = risk.filter((r) => !r.ok && !r.blocking).map((r) => `${r.label}: ${r.detail}`);
|
|
399
|
+
const target = check.latestVersion && compareVersions(check.currentVersion, check.latestVersion) < 0
|
|
400
|
+
? check.latestVersion
|
|
401
|
+
: null;
|
|
402
|
+
return {
|
|
403
|
+
ok: blockers.length === 0 && !!target,
|
|
404
|
+
currentVersion: check.currentVersion,
|
|
405
|
+
targetVersion: target,
|
|
406
|
+
channel: check.channel,
|
|
407
|
+
installMethod: check.installMethod,
|
|
408
|
+
source: check.source,
|
|
409
|
+
installDir: install.installDir,
|
|
410
|
+
needsRestart: !!target,
|
|
411
|
+
willUpdate: target && install.method === 'npm-global' ? [PKG_NAME, CONSTRAINT_PKG_NAME] : [],
|
|
412
|
+
willNotTouch: WILL_NOT_TOUCH,
|
|
413
|
+
risk, blockers, advisories,
|
|
414
|
+
strategies: ['now', 'wait', 'cancel'],
|
|
415
|
+
defaultStrategy: advisories.length > 0 ? 'wait' : 'now',
|
|
416
|
+
check,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
export function renderUpdatePlan(plan) {
|
|
420
|
+
const L = [];
|
|
421
|
+
L.push(`当前版本: ${plan.currentVersion}`);
|
|
422
|
+
L.push(`目标版本: ${plan.targetVersion || '(无可用更新)'}`);
|
|
423
|
+
L.push(`安装方式: ${plan.installMethod}`);
|
|
424
|
+
L.push(`更新通道: ${plan.channel} (来源: ${plan.source})`);
|
|
425
|
+
L.push(`安装目录: ${plan.installDir}`);
|
|
426
|
+
L.push('');
|
|
427
|
+
L.push('将更新:');
|
|
428
|
+
if (plan.willUpdate.length === 0)
|
|
429
|
+
L.push(' (无 — 当前没有可执行的更新)');
|
|
430
|
+
for (const p of plan.willUpdate)
|
|
431
|
+
L.push(` ${p}`);
|
|
432
|
+
L.push('');
|
|
433
|
+
L.push('不会修改:');
|
|
434
|
+
for (const p of plan.willNotTouch)
|
|
435
|
+
L.push(` ${p}`);
|
|
436
|
+
L.push('');
|
|
437
|
+
L.push(`需要重启: ${plan.needsRestart ? '是' : '否'}`);
|
|
438
|
+
L.push(`风险检查: ${plan.blockers.length === 0 ? '通过' : `未通过 (${plan.blockers.length} 项阻塞)`}`);
|
|
439
|
+
for (const r of plan.risk)
|
|
440
|
+
L.push(` ${r.ok ? '✓' : r.blocking ? '✗' : '!'} ${r.label}: ${r.detail}`);
|
|
441
|
+
if (plan.blockers.length) {
|
|
442
|
+
L.push('');
|
|
443
|
+
L.push('阻塞项 (修好才能更新):');
|
|
444
|
+
for (const b of plan.blockers)
|
|
445
|
+
L.push(` - ${b}`);
|
|
446
|
+
}
|
|
447
|
+
if (plan.advisories.length) {
|
|
448
|
+
L.push('');
|
|
449
|
+
L.push('提醒 (不阻塞, 但建议先处理):');
|
|
450
|
+
for (const a of plan.advisories)
|
|
451
|
+
L.push(` - ${a}`);
|
|
452
|
+
L.push(` 建议策略: 等当前 Run 结束后更新 (bolloon update --now --wait)`);
|
|
453
|
+
}
|
|
454
|
+
L.push('');
|
|
455
|
+
L.push('可选: 立即更新 (bolloon update --now) / 等当前 Run 结束 (bolloon update --now --wait) / 取消');
|
|
456
|
+
L.push(`默认: ${plan.defaultStrategy === 'wait' ? '等待当前 Run 结束后更新' : '立即更新'}`);
|
|
457
|
+
return L.join('\n');
|
|
458
|
+
}
|
|
459
|
+
/** 进行中的阶段 (落盘时看到它 = 上次更新被中断; 与 update-state 的 IN_FLIGHT_RUN_STATUSES 同义) */
|
|
460
|
+
export const IN_FLIGHT_STAGES = ['planned', 'downloading', 'staged', 'switching', 'verifying'];
|
|
461
|
+
/** npm 默认只重试 2 次/10s —— 真网络抖动会让一次干净安装失败 (本机 ECONNRESET 实测)。 */
|
|
462
|
+
export const NPM_FETCH_RETRY_FLAGS = [
|
|
463
|
+
'--fetch-retries=5', '--fetch-retry-mintimeout=10000', '--fetch-retry-maxtimeout=120000',
|
|
464
|
+
];
|
|
465
|
+
function defaultRunNpm(args, cwd) {
|
|
466
|
+
const finalArgs = args[0] === 'pack' || args[0] === 'install' ? [...args, ...NPM_FETCH_RETRY_FLAGS] : args;
|
|
467
|
+
const r = spawnSync('npm', finalArgs, { cwd, encoding: 'utf-8', timeout: 900_000, maxBuffer: 32 * 1024 * 1024 });
|
|
468
|
+
return { code: r.status ?? 1, stdout: r.stdout || '', stderr: r.stderr || '' };
|
|
469
|
+
}
|
|
470
|
+
/** 校验一个解压后的包目录: 版本对得上 + 入口存在。 */
|
|
471
|
+
export function validateStagedPackage(dir, target) {
|
|
472
|
+
const pkg = readPackageAt(dir);
|
|
473
|
+
if (!pkg)
|
|
474
|
+
return { ok: false, detail: `${dir}/package.json 读不到` };
|
|
475
|
+
if (pkg.version !== target)
|
|
476
|
+
return { ok: false, detail: `临时目录版本 ${pkg.version} ≠ 目标 ${target}` };
|
|
477
|
+
const entry = path.join(dir, 'dist', 'cli-entry.js');
|
|
478
|
+
if (!fs.existsSync(entry))
|
|
479
|
+
return { ok: false, detail: '包内缺少 dist/cli-entry.js' };
|
|
480
|
+
return { ok: true, detail: `版本 ${pkg.version} + 入口存在` };
|
|
481
|
+
}
|
|
482
|
+
/** 读"磁盘上真的装了什么版本" (不看进程内存), 供切换后验证。 */
|
|
483
|
+
export function installedVersionOnDisk(installRoot) {
|
|
484
|
+
return readPackageAt(installRoot)?.version ?? null;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* 执行更新。
|
|
488
|
+
*
|
|
489
|
+
* 流水线: 计划(检查+锁) → 下载 tarball 到临时目录并校验 → 切换(npm 替换) → 验证 → 成功/回滚。
|
|
490
|
+
* 任何一步失败: 清理临时目录、保留旧版本、写失败原因、释放锁、用户继续用旧版本。
|
|
491
|
+
*/
|
|
492
|
+
export async function applyUpdate(opts = {}) {
|
|
493
|
+
const started = Date.now();
|
|
494
|
+
const home = opts.home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
495
|
+
const install = opts.installation || detectInstallation({ home });
|
|
496
|
+
/** 落盘"进行中"的阶段 —— 这样进程被 SIGKILL 时盘上会留下证据 (doctor 报"上次更新异常中断") */
|
|
497
|
+
const markInFlight = async (s, detail) => {
|
|
498
|
+
if (!IN_FLIGHT_STAGES.includes(s))
|
|
499
|
+
return;
|
|
500
|
+
try {
|
|
501
|
+
await writeUpdateState({
|
|
502
|
+
lastUpdate: { at: new Date().toISOString(), from, to, status: s, reason: detail },
|
|
503
|
+
}, home);
|
|
504
|
+
}
|
|
505
|
+
catch { /* 阶段留痕写失败不能影响更新本身 */ }
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* 报阶段 —— **必须 await**: 先在盘上落下"进行中"的阶段, 再去做那件危险的事
|
|
509
|
+
* (否则进程在写状态之前被杀, 盘上就没有"上次更新中断"的证据)。
|
|
510
|
+
*/
|
|
511
|
+
const stage = async (s, detail) => {
|
|
512
|
+
try {
|
|
513
|
+
opts.onStage?.(s, detail);
|
|
514
|
+
}
|
|
515
|
+
catch { /* 回调失败不影响流程 */ }
|
|
516
|
+
await markInFlight(s, detail);
|
|
517
|
+
};
|
|
518
|
+
const plan = await buildUpdatePlan({
|
|
519
|
+
home, env: opts.env, force: true, installation: install, registry: opts.registry,
|
|
520
|
+
workloadRiskOverride: opts.workloadRiskOverride,
|
|
521
|
+
});
|
|
522
|
+
const from = plan.currentVersion;
|
|
523
|
+
const to = plan.targetVersion || plan.currentVersion;
|
|
524
|
+
const record = async (r, historyStatus) => {
|
|
525
|
+
const rec = { at: new Date().toISOString(), from: r.from, to: r.to, status: historyStatus, durationMs: r.durationMs, reason: r.reason };
|
|
526
|
+
// 记账失败**不能**把"更新其实成功了"变成向上抛异常 (真被测试 teardown 竞态抓到过:
|
|
527
|
+
// HOME 在写到一半时消失 → rename ENOENT)。失败就如实记进 reason, 结果照常返回。
|
|
528
|
+
try {
|
|
529
|
+
await appendUpdateHistory(rec, home);
|
|
530
|
+
await writeUpdateState({
|
|
531
|
+
lastUpdate: rec,
|
|
532
|
+
lastFailure: r.ok ? null : { at: rec.at, stage: r.failedAt || r.stage, reason: r.reason || '未知原因' },
|
|
533
|
+
needsRestart: r.ok ? r.needsRestart : false,
|
|
534
|
+
currentVersion: r.from,
|
|
535
|
+
}, home);
|
|
536
|
+
}
|
|
537
|
+
catch (e) {
|
|
538
|
+
const note = `状态记录写入失败: ${e?.message || e}`;
|
|
539
|
+
return { ...r, reason: r.reason ? `${r.reason}; ${note}` : note };
|
|
540
|
+
}
|
|
541
|
+
return r;
|
|
542
|
+
};
|
|
543
|
+
if (!plan.targetVersion) {
|
|
544
|
+
await stage('blocked', '没有可执行的更新');
|
|
545
|
+
return record({ stage: 'blocked', ok: false, from, to, durationMs: Date.now() - started, reason: plan.blockers[0] || '已是最新, 无需更新', needsRestart: false }, 'blocked');
|
|
546
|
+
}
|
|
547
|
+
if (plan.blockers.length > 0) {
|
|
548
|
+
await stage('blocked', plan.blockers.join('; '));
|
|
549
|
+
return record({ stage: 'blocked', ok: false, from, to, durationMs: Date.now() - started, reason: `阻塞: ${plan.blockers.join('; ')}`, needsRestart: false }, 'blocked');
|
|
550
|
+
}
|
|
551
|
+
if (opts.strategy === 'wait' && plan.advisories.length > 0) {
|
|
552
|
+
await stage('blocked', '按策略等待当前 Run 结束');
|
|
553
|
+
const reason = `等待当前 Run 结束后更新 (${plan.advisories.length} 项提醒); 待收尾后重新执行 bolloon update --now`;
|
|
554
|
+
await appendUpdateHistory({ at: new Date().toISOString(), from, to, status: 'planned', reason }, home);
|
|
555
|
+
return { stage: 'blocked', ok: false, from, to, durationMs: Date.now() - started, reason, needsRestart: false };
|
|
556
|
+
}
|
|
557
|
+
// 锁 (第二个进程不许同时更新)
|
|
558
|
+
const lock = await acquireUpdateLock({ home, reason: `${from} → ${to}` });
|
|
559
|
+
if (!lock.ok) {
|
|
560
|
+
const held = lock.heldBy;
|
|
561
|
+
await stage('blocked', `已有更新进程 (pid ${held?.pid})`);
|
|
562
|
+
return record({ stage: 'blocked', ok: false, from, to, durationMs: Date.now() - started, reason: `已有更新进程持有锁 (pid ${held?.pid}, ${held?.at})`, needsRestart: false }, 'blocked');
|
|
563
|
+
}
|
|
564
|
+
const tmpRoot = await fsp.mkdtemp(path.join(os.tmpdir(), 'bolloon-update-'));
|
|
565
|
+
const runNpm = opts.runNpm || defaultRunNpm;
|
|
566
|
+
let stagedTarball = null;
|
|
567
|
+
try {
|
|
568
|
+
// 1. 下载 tarball 到临时目录 (不动现有安装)
|
|
569
|
+
await stage('downloading', `npm pack ${PKG_NAME}@${to}`);
|
|
570
|
+
const packed = runNpm(['pack', `${PKG_NAME}@${to}`, '--pack-destination', tmpRoot, '--loglevel=error'], tmpRoot);
|
|
571
|
+
if (packed.code !== 0) {
|
|
572
|
+
throw Object.assign(new Error(`下载 ${PKG_NAME}@${to} 失败: ${(packed.stderr || packed.stdout).trim().slice(0, 400)}`), { stage: 'downloading' });
|
|
573
|
+
}
|
|
574
|
+
const tarballs = (await fsp.readdir(tmpRoot)).filter((f) => f.endsWith('.tgz'));
|
|
575
|
+
if (tarballs.length === 0)
|
|
576
|
+
throw Object.assign(new Error('npm pack 没有产出 tarball'), { stage: 'downloading' });
|
|
577
|
+
stagedTarball = path.join(tmpRoot, tarballs[0]);
|
|
578
|
+
// 2. 解压 + 校验包内容 (版本 + 入口)
|
|
579
|
+
await stage('staged', `解压并校验 ${path.basename(stagedTarball)}`);
|
|
580
|
+
const extractDir = path.join(tmpRoot, 'extract');
|
|
581
|
+
await fsp.mkdir(extractDir, { recursive: true });
|
|
582
|
+
const tar = spawnSync('tar', ['-xzf', stagedTarball, '-C', extractDir], { encoding: 'utf-8', timeout: 120_000 });
|
|
583
|
+
if (tar.status !== 0)
|
|
584
|
+
throw Object.assign(new Error(`解压失败: ${(tar.stderr || '').slice(0, 300)}`), { stage: 'staged' });
|
|
585
|
+
const stagedCheck = validateStagedPackage(path.join(extractDir, 'package'), to);
|
|
586
|
+
if (!stagedCheck.ok)
|
|
587
|
+
throw Object.assign(new Error(`目标包校验失败: ${stagedCheck.detail}`), { stage: 'staged' });
|
|
588
|
+
// 3. 切换 (npm 完成替换; 旧版本在替换成功前不会消失)
|
|
589
|
+
await stage('switching', `npm install -g ${PKG_NAME}@${to}`);
|
|
590
|
+
const installed = runNpm(['install', '-g', `${PKG_NAME}@${to}`, '--no-fund', '--no-audit', '--loglevel=error'], os.tmpdir());
|
|
591
|
+
if (installed.code !== 0) {
|
|
592
|
+
throw Object.assign(new Error(`npm 安装失败: ${(installed.stderr || installed.stdout).trim().slice(0, 400)}`), { stage: 'switching' });
|
|
593
|
+
}
|
|
594
|
+
// 4. 验证: 磁盘版本 + 新入口真能启动 + 健康检查
|
|
595
|
+
await stage('verifying', '核对磁盘版本并试启新入口');
|
|
596
|
+
const verify = opts.verifyInstall || defaultVerifyInstall;
|
|
597
|
+
const ok = await verify(to, home);
|
|
598
|
+
if (!ok)
|
|
599
|
+
throw Object.assign(new Error(`切换后验证失败: 新版本 ${to} 未能正确启动`), { stage: 'verifying' });
|
|
600
|
+
await stage('succeeded', `已更新到 ${to}`);
|
|
601
|
+
const out = {
|
|
602
|
+
stage: 'succeeded', ok: true, from, to, durationMs: Date.now() - started, needsRestart: true, stagedTarball,
|
|
603
|
+
};
|
|
604
|
+
await record(out, 'succeeded');
|
|
605
|
+
return out;
|
|
606
|
+
}
|
|
607
|
+
catch (e) {
|
|
608
|
+
const failureStage = e?.stage || 'failed';
|
|
609
|
+
const reason = e?.message || String(e);
|
|
610
|
+
await stage(failureStage, reason);
|
|
611
|
+
// 失败处理: 保留旧版本 → 能回滚就回滚 → 写原因 → 释放锁
|
|
612
|
+
let finalStage = 'failed';
|
|
613
|
+
let finalReason = reason;
|
|
614
|
+
try {
|
|
615
|
+
const nowVersion = installedVersionOnDisk(install.packageRoot);
|
|
616
|
+
if (install.method === 'npm-global' && nowVersion && nowVersion !== from && nowVersion !== to) {
|
|
617
|
+
await stage('rolled_back', `检测到半更新 (磁盘版本 ${nowVersion}), 回滚到 ${from}`);
|
|
618
|
+
const rb = runNpm(['install', '-g', `${PKG_NAME}@${from}`, '--no-fund', '--no-audit', '--loglevel=error'], os.tmpdir());
|
|
619
|
+
const after = installedVersionOnDisk(install.packageRoot);
|
|
620
|
+
if (rb.code === 0 && after === from) {
|
|
621
|
+
finalStage = 'rolled_back';
|
|
622
|
+
finalReason = `${reason}; 已回滚到 ${from}`;
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
finalReason = `${reason}; 回滚也未成功 (磁盘版本 ${after || 'unknown'}) — 请手动: npm install -g ${PKG_NAME}@${from}`;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
else if (nowVersion === to && failureStage === 'verifying') {
|
|
629
|
+
// 装上了但验证没过: 保留新版本但如实报失败, 并给出回滚指令
|
|
630
|
+
finalReason = `${reason}; 磁盘版本已是 ${to}, 如需回退: npm install -g ${PKG_NAME}@${from}`;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
catch (e2) {
|
|
634
|
+
finalReason = `${reason}; 回滚过程出错: ${e2?.message || e2}`;
|
|
635
|
+
}
|
|
636
|
+
const out = {
|
|
637
|
+
stage: finalStage, failedAt: failureStage, ok: false, from, to, durationMs: Date.now() - started,
|
|
638
|
+
reason: finalReason, needsRestart: false, stagedTarball,
|
|
639
|
+
};
|
|
640
|
+
await record(out, finalStage === 'rolled_back' ? 'rolled_back' : 'failed');
|
|
641
|
+
return out;
|
|
642
|
+
}
|
|
643
|
+
finally {
|
|
644
|
+
// 临时目录清理 (成功/失败都清)
|
|
645
|
+
try {
|
|
646
|
+
await fsp.rm(tmpRoot, { recursive: true, force: true });
|
|
647
|
+
}
|
|
648
|
+
catch { /* 忽略 */ }
|
|
649
|
+
await releaseUpdateLock(home);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
async function defaultVerifyInstall(target, home) {
|
|
653
|
+
const install = detectInstallation({ home });
|
|
654
|
+
const onDisk = installedVersionOnDisk(install.packageRoot);
|
|
655
|
+
if (onDisk !== target)
|
|
656
|
+
return false;
|
|
657
|
+
const entry = path.join(install.packageRoot, 'dist', 'cli-entry.js');
|
|
658
|
+
if (!fs.existsSync(entry))
|
|
659
|
+
return false;
|
|
660
|
+
const r = spawnSync(process.execPath, [entry, '--version', '--json'], { encoding: 'utf-8', timeout: 120_000, maxBuffer: 8 * 1024 * 1024 });
|
|
661
|
+
if (r.status !== 0)
|
|
662
|
+
return false;
|
|
663
|
+
const parsed = parseJsonFromStdout(String(r.stdout || ''));
|
|
664
|
+
return parsed?.packageVersion === target;
|
|
665
|
+
}
|
|
666
|
+
export async function readUpdateStatus(opts = {}) {
|
|
667
|
+
const home = opts.home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
668
|
+
const st = await readUpdateState(home);
|
|
669
|
+
const prefs = await readUpdatePrefs({ home, env: opts.env });
|
|
670
|
+
const lock = readUpdateLock(home);
|
|
671
|
+
const install = detectInstallation({ home });
|
|
672
|
+
const pkg = readPackageAt(install.packageRoot);
|
|
673
|
+
return {
|
|
674
|
+
currentVersion: isKnownVersion(pkg?.version) ? String(pkg?.version) : st.currentVersion,
|
|
675
|
+
latestVersion: st.latestVersion,
|
|
676
|
+
channel: prefs.channel,
|
|
677
|
+
installMethod: install.method,
|
|
678
|
+
installDir: install.installDir,
|
|
679
|
+
entryPath: install.entryPath,
|
|
680
|
+
lastCheckAt: st.lastCheckAt,
|
|
681
|
+
lastCheckStatus: st.lastCheckStatus,
|
|
682
|
+
lastCheckReason: st.lastCheckReason,
|
|
683
|
+
lastUpdate: st.lastUpdate,
|
|
684
|
+
lastFailure: st.lastFailure,
|
|
685
|
+
needsRestart: st.needsRestart,
|
|
686
|
+
prefs,
|
|
687
|
+
lock: lock ? { ...lock, stale: lockIsStale(lock) } : null,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
export async function readHistory(limit = 10, home) {
|
|
691
|
+
const h = home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
692
|
+
return readUpdateHistory(limit, h);
|
|
693
|
+
}
|
|
694
|
+
export function renderHistory(records) {
|
|
695
|
+
if (records.length === 0)
|
|
696
|
+
return '没有更新历史记录。';
|
|
697
|
+
const L = ['时间 · 当前版本 → 目标版本 · 结果 · 耗时 · 原因'];
|
|
698
|
+
for (const r of records) {
|
|
699
|
+
const ms = r.durationMs != null ? `${(r.durationMs / 1000).toFixed(1)}s` : '-';
|
|
700
|
+
L.push(`${r.at} · ${r.from} → ${r.to} · ${r.status} · ${ms}${r.reason ? ` · ${r.reason}` : ''}`);
|
|
701
|
+
}
|
|
702
|
+
return L.join('\n');
|
|
703
|
+
}
|
|
704
|
+
/** 启动后台检查用的摘要 (供 --version 显示)。 */
|
|
705
|
+
export async function updateSummaryFor(home) {
|
|
706
|
+
const h = home ?? (await import('../setup/setup-store.js')).resolveBolloonHome();
|
|
707
|
+
const st = await readUpdateState(h);
|
|
708
|
+
return {
|
|
709
|
+
lastCheckAt: st.lastCheckAt,
|
|
710
|
+
lastCheckStatus: st.lastCheckStatus,
|
|
711
|
+
lastCheckReason: st.lastCheckReason,
|
|
712
|
+
latestVersion: st.latestVersion,
|
|
713
|
+
lastUpdate: st.lastUpdate ? { at: st.lastUpdate.at, from: st.lastUpdate.from, to: st.lastUpdate.to, status: st.lastUpdate.status, reason: st.lastUpdate.reason } : null,
|
|
714
|
+
needsRestart: st.needsRestart,
|
|
715
|
+
};
|
|
716
|
+
}
|