chainlesschain 0.165.6 → 0.165.9
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/package.json +2 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-DxwjrnEA.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-JdbYowzn.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-C06GCahv.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BUVEEyi1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-D_ORzkJd.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-8o_Y5ejG.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-DrkHd5VU.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-CKBZCX11.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-C8HkI399.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-zb2H2Wux.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BDcNpU_F.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-MwPtQmNz.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-3p5QCkFK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-BPLlhn52.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-C-3dBNOM.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-BUcy-F_b.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-CI8NxT8m.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-DaDEBS-Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BSv9HXu5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-yuiKlJmv.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-D6LfldB0.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-CBhLqG61.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-BG8pIl3O.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-CY92VIE6.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-1Q_ehauM.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-D3XirJIU.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DfI9UuGZ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-IeCZk2m7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-DzK4IJAZ.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-BHf5KfmQ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-BmaxSALS.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-BgePFLA9.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-nqgxpOc4.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-xkX6izBt.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Bu8to-86.js} +1 -1
- package/src/assets/web-panel/assets/McpTools-BFWTzEk0.js +4 -0
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-B026G2GA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-CM2Eeg6I.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-B79Ob3hy.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CxevSZuV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-X396cPEX.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-HWIB_aZ1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-gnYD1Xwy.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-Dn2Gf_J1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-BRKBQ5rq.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-Dy___Cgo.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-cqfccxnt.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CSX4FHN3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-Bwa4aghp.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-Dhszyjig.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-CXzTyk6e.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-CFCAAJbb.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-BPJbx6xt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-BbiXvEHz.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-B-s7-wSD.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-Dyug206y.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-BtAI5Fwo.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-JpIPmz6N.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-CZdoN64A.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-BwvnoRIT.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-ASMlMQrD.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-BiJzKFKi.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-C80TWLIO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-C1VNmjmX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-T2MyGUlG.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-DXM6O83L.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-nv69kU7W.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-AyHWe7Qj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D6PWLxFI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BqVeOmNQ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-BegIPl9M.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BHUI7cox.js → SpeechSettings-CKJn3xk-.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-Th4cnAMl.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BuKfBroh.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-CMuWN8Ac.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-C4je8E8Q.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-agJESOcO.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-D2qDuVZ_.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-Cvmn99rH.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-D_QYIHH8.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-BmzvQLnX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-D_LopHwH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-DoWl3PhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-5NYcAkxe.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-B-_Q3ewp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-CHET-gi2.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-C6nvb-n6.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-CTDE9LTX.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-KnemdF8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-DUJR3Uek.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-1DqLH1KX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-CFNTaby8.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-65HO56Bv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-B8RtbSkV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-BHqSqnW7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-BTetEUWd.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-BXWg1crv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-BdiC4LOs.js} +1 -1
- package/src/assets/web-panel/assets/index-BhJtcPWo.js +1 -0
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-BuizJo2E.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BwZM1rod.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-C96N9xeL.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-CHfUdnJH.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-CKvBF3Ov.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-CSD9_I2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-Cc7dbl_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-CcF0D7UZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-Ceu92a8D.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-CnLpuTAU.js} +1 -1
- package/src/assets/web-panel/assets/index-Cq8MWPDT.js +1 -0
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-CquKnw5r.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CyEtHvEE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-DF-fcTvQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-DF1Y0Vuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DJyPeBBk.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-DP-PbO11.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-DZgCRMxk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-DfJzS3Qa.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-DqPpJkte.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-DzReoLyh.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-JGTiDeOW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-NUR0If7O.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-NtAcnJYW.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-OmuhFYKE.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-Wlc7o_C8.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-bxTPfdri.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-k6uorYmf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-ox6mIwE2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-rVDD7AAR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-u0h6QqVV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-v-mtxmWH.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-qEt8dI-t.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-DetKPWy-.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-BeNYeDV2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-C0q0MK_T.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-DLKkrKpE.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-Bw1tWsY8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-DeesBLbd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-bqLhxHhn.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-DGCXRoQm.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-H-re3Egl.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-BXg4qGX_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-CKLb2KA6.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-CTAhHeyw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-BQ4pDh5g.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-jrSK6j4j.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-fOQE24pw.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/mcp-topic-handlers.js +167 -0
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-network.js +166 -29
- package/src/lib/agent-router.js +62 -6
- package/src/lib/agent-sandbox.js +7 -1
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-code-bridge.js +159 -54
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/cowork-workflow.js +69 -6
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/external-agent-adapters.js +269 -0
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/ipfs-storage.js +127 -25
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/orchestrator.js +47 -0
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/shell-approval.js +7 -5
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workflow-engine.js +42 -11
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/agent-runtime.js +12 -1
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/McpTools-DFaIhOfu.js +0 -7
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -0,0 +1,991 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
mutateSecurityStore,
|
|
6
|
+
readSecurityStore,
|
|
7
|
+
} from "./durable-security-store.js";
|
|
8
|
+
import { canonicalJson } from "./scheduler-kernel/contract.js";
|
|
9
|
+
|
|
10
|
+
export const EXECUTION_LOCATION_RUNNER_LIFECYCLE_SCHEMA =
|
|
11
|
+
"cc-execution-location-runner-lifecycle/v1";
|
|
12
|
+
export const EXECUTION_LOCATION_RUNNER_STATES = Object.freeze([
|
|
13
|
+
"accepting",
|
|
14
|
+
"draining",
|
|
15
|
+
"parked",
|
|
16
|
+
"reclaiming",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const STORE_LABEL = "Execution location runner lifecycle";
|
|
20
|
+
const TARGETS = new Set(["local", "wsl", "ssh", "container"]);
|
|
21
|
+
const STATES = new Set(EXECUTION_LOCATION_RUNNER_STATES);
|
|
22
|
+
const LEASE_STATUSES = new Set(["active", "settled", "parked"]);
|
|
23
|
+
const ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,255}$/u;
|
|
24
|
+
const DIGEST_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
25
|
+
const MAX_PROXY_AGE_MS = 5 * 60 * 1000;
|
|
26
|
+
const MAX_RETAINED_LEASES = 1024;
|
|
27
|
+
|
|
28
|
+
function exactObject(value, fields, label) {
|
|
29
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
30
|
+
throw new TypeError(`${label} must be an object`);
|
|
31
|
+
}
|
|
32
|
+
const actual = Object.keys(value).sort();
|
|
33
|
+
const expected = [...fields].sort();
|
|
34
|
+
if (
|
|
35
|
+
actual.length !== expected.length ||
|
|
36
|
+
actual.some((field, index) => field !== expected[index])
|
|
37
|
+
) {
|
|
38
|
+
throw new TypeError(`${label} has an invalid schema`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function safeId(value, label) {
|
|
44
|
+
if (typeof value !== "string" || !ID_RE.test(value)) {
|
|
45
|
+
throw new TypeError(`${label} is invalid`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function safeDigest(value, label) {
|
|
51
|
+
if (typeof value !== "string" || !DIGEST_RE.test(value)) {
|
|
52
|
+
throw new TypeError(`${label} is invalid`);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function safeInteger(
|
|
58
|
+
value,
|
|
59
|
+
label,
|
|
60
|
+
minimum = 0,
|
|
61
|
+
maximum = Number.MAX_SAFE_INTEGER,
|
|
62
|
+
) {
|
|
63
|
+
const normalized = Number(value);
|
|
64
|
+
if (
|
|
65
|
+
!Number.isSafeInteger(normalized) ||
|
|
66
|
+
normalized < minimum ||
|
|
67
|
+
normalized > maximum
|
|
68
|
+
) {
|
|
69
|
+
throw new TypeError(`${label} is invalid`);
|
|
70
|
+
}
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function safeTimestamp(value, label) {
|
|
75
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
76
|
+
throw new TypeError(`${label} is invalid`);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function receiptDigest(domain, value) {
|
|
82
|
+
return `sha256:${createHash("sha256")
|
|
83
|
+
.update(domain, "utf8")
|
|
84
|
+
.update(canonicalJson(value, "executionLocationRunnerLifecycle"), "utf8")
|
|
85
|
+
.digest("hex")}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateResources(value) {
|
|
89
|
+
const input = exactObject(
|
|
90
|
+
value,
|
|
91
|
+
["cpuSeconds", "memoryBytes"],
|
|
92
|
+
"runner resources",
|
|
93
|
+
);
|
|
94
|
+
return Object.freeze({
|
|
95
|
+
cpuSeconds: safeInteger(input.cpuSeconds, "runner cpuSeconds", 1, 86_400),
|
|
96
|
+
memoryBytes: safeInteger(
|
|
97
|
+
input.memoryBytes,
|
|
98
|
+
"runner memoryBytes",
|
|
99
|
+
64 * 1024 * 1024,
|
|
100
|
+
64 * 1024 * 1024 * 1024,
|
|
101
|
+
),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function validateProxyAuthority(value, now, minimumRevision = 0) {
|
|
106
|
+
const input = exactObject(
|
|
107
|
+
value,
|
|
108
|
+
["id", "revision", "issuedAt", "expiresAt"],
|
|
109
|
+
"proxy authority",
|
|
110
|
+
);
|
|
111
|
+
const issuedAt = safeTimestamp(input.issuedAt, "proxy authority issuedAt");
|
|
112
|
+
const expiresAt = safeTimestamp(input.expiresAt, "proxy authority expiresAt");
|
|
113
|
+
const revision = safeInteger(
|
|
114
|
+
input.revision,
|
|
115
|
+
"proxy authority revision",
|
|
116
|
+
Math.max(1, minimumRevision),
|
|
117
|
+
);
|
|
118
|
+
const issuedAtMs = Date.parse(issuedAt);
|
|
119
|
+
if (
|
|
120
|
+
issuedAtMs > now + 30_000 ||
|
|
121
|
+
now - issuedAtMs > MAX_PROXY_AGE_MS ||
|
|
122
|
+
Date.parse(expiresAt) <= now
|
|
123
|
+
) {
|
|
124
|
+
throw new Error("execution location proxy authority is not fresh");
|
|
125
|
+
}
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
id: safeId(input.id, "proxy authority id"),
|
|
128
|
+
revision,
|
|
129
|
+
issuedAt,
|
|
130
|
+
expiresAt,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function validateLease(value) {
|
|
135
|
+
const input = exactObject(
|
|
136
|
+
value,
|
|
137
|
+
[
|
|
138
|
+
"id",
|
|
139
|
+
"sessionId",
|
|
140
|
+
"generation",
|
|
141
|
+
"issuedAt",
|
|
142
|
+
"expiresAt",
|
|
143
|
+
"status",
|
|
144
|
+
"resultDigest",
|
|
145
|
+
"hookReceiptDigest",
|
|
146
|
+
],
|
|
147
|
+
"runner lease",
|
|
148
|
+
);
|
|
149
|
+
const status = safeId(input.status, "runner lease status");
|
|
150
|
+
if (!LEASE_STATUSES.has(status)) {
|
|
151
|
+
throw new TypeError("runner lease status is invalid");
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
id: safeId(input.id, "runner lease id"),
|
|
155
|
+
sessionId: safeId(input.sessionId, "runner lease sessionId"),
|
|
156
|
+
generation: safeInteger(input.generation, "runner lease generation", 1),
|
|
157
|
+
issuedAt: safeTimestamp(input.issuedAt, "runner lease issuedAt"),
|
|
158
|
+
expiresAt: safeTimestamp(input.expiresAt, "runner lease expiresAt"),
|
|
159
|
+
status,
|
|
160
|
+
resultDigest:
|
|
161
|
+
input.resultDigest === null
|
|
162
|
+
? null
|
|
163
|
+
: safeDigest(input.resultDigest, "runner lease resultDigest"),
|
|
164
|
+
hookReceiptDigest:
|
|
165
|
+
input.hookReceiptDigest === null
|
|
166
|
+
? null
|
|
167
|
+
: safeDigest(input.hookReceiptDigest, "runner lease hookReceiptDigest"),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function validateState(value, expected = {}) {
|
|
172
|
+
const input = exactObject(
|
|
173
|
+
value,
|
|
174
|
+
[
|
|
175
|
+
"schema",
|
|
176
|
+
"runnerId",
|
|
177
|
+
"target",
|
|
178
|
+
"state",
|
|
179
|
+
"generation",
|
|
180
|
+
"policyRevision",
|
|
181
|
+
"baseDir",
|
|
182
|
+
"resources",
|
|
183
|
+
"proxyAuthority",
|
|
184
|
+
"postSessionHookDigest",
|
|
185
|
+
"drain",
|
|
186
|
+
"leases",
|
|
187
|
+
"updatedAt",
|
|
188
|
+
],
|
|
189
|
+
"runner lifecycle state",
|
|
190
|
+
);
|
|
191
|
+
if (input.schema !== EXECUTION_LOCATION_RUNNER_LIFECYCLE_SCHEMA) {
|
|
192
|
+
throw new TypeError("runner lifecycle state schema is invalid");
|
|
193
|
+
}
|
|
194
|
+
const runnerId = safeId(input.runnerId, "runnerId");
|
|
195
|
+
const target = safeId(input.target, "runner target");
|
|
196
|
+
const state = safeId(input.state, "runner state");
|
|
197
|
+
if (!TARGETS.has(target) || !STATES.has(state)) {
|
|
198
|
+
throw new TypeError("runner lifecycle target or state is invalid");
|
|
199
|
+
}
|
|
200
|
+
if (
|
|
201
|
+
(expected.runnerId && expected.runnerId !== runnerId) ||
|
|
202
|
+
(expected.target && expected.target !== target) ||
|
|
203
|
+
(expected.baseDir && expected.baseDir !== input.baseDir)
|
|
204
|
+
) {
|
|
205
|
+
throw new Error("runner lifecycle identity drifted");
|
|
206
|
+
}
|
|
207
|
+
const drain =
|
|
208
|
+
input.drain === null
|
|
209
|
+
? null
|
|
210
|
+
: exactObject(
|
|
211
|
+
input.drain,
|
|
212
|
+
["signal", "requestedAt", "deadlineAt"],
|
|
213
|
+
"runner drain",
|
|
214
|
+
);
|
|
215
|
+
if ((state === "draining") !== Boolean(drain)) {
|
|
216
|
+
throw new TypeError("runner drain state is inconsistent");
|
|
217
|
+
}
|
|
218
|
+
if (drain) {
|
|
219
|
+
if (drain.signal !== "SIGTERM") {
|
|
220
|
+
throw new TypeError("runner drain signal is invalid");
|
|
221
|
+
}
|
|
222
|
+
safeTimestamp(drain.requestedAt, "runner drain requestedAt");
|
|
223
|
+
safeTimestamp(drain.deadlineAt, "runner drain deadlineAt");
|
|
224
|
+
}
|
|
225
|
+
if (
|
|
226
|
+
!Array.isArray(input.leases) ||
|
|
227
|
+
input.leases.length > MAX_RETAINED_LEASES
|
|
228
|
+
) {
|
|
229
|
+
throw new TypeError("runner leases are invalid");
|
|
230
|
+
}
|
|
231
|
+
const leases = input.leases.map(validateLease);
|
|
232
|
+
if (new Set(leases.map((lease) => lease.id)).size !== leases.length) {
|
|
233
|
+
throw new TypeError("runner leases contain duplicate identities");
|
|
234
|
+
}
|
|
235
|
+
const proxyAuthority = exactObject(
|
|
236
|
+
input.proxyAuthority,
|
|
237
|
+
["id", "revision"],
|
|
238
|
+
"runner proxy authority",
|
|
239
|
+
);
|
|
240
|
+
return {
|
|
241
|
+
...input,
|
|
242
|
+
runnerId,
|
|
243
|
+
target,
|
|
244
|
+
state,
|
|
245
|
+
generation: safeInteger(input.generation, "runner generation", 1),
|
|
246
|
+
policyRevision: safeInteger(
|
|
247
|
+
input.policyRevision,
|
|
248
|
+
"runner policyRevision",
|
|
249
|
+
1,
|
|
250
|
+
),
|
|
251
|
+
baseDir: input.baseDir,
|
|
252
|
+
resources: validateResources(input.resources),
|
|
253
|
+
proxyAuthority: {
|
|
254
|
+
id: safeId(proxyAuthority.id, "runner proxy authority id"),
|
|
255
|
+
revision: safeInteger(
|
|
256
|
+
proxyAuthority.revision,
|
|
257
|
+
"runner proxy authority revision",
|
|
258
|
+
0,
|
|
259
|
+
),
|
|
260
|
+
},
|
|
261
|
+
postSessionHookDigest: safeDigest(
|
|
262
|
+
input.postSessionHookDigest,
|
|
263
|
+
"runner post-session hook digest",
|
|
264
|
+
),
|
|
265
|
+
drain,
|
|
266
|
+
leases,
|
|
267
|
+
updatedAt: safeTimestamp(input.updatedAt, "runner updatedAt"),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function project(state) {
|
|
272
|
+
const activeLeaseCount = state.leases.filter(
|
|
273
|
+
(lease) => lease.status === "active",
|
|
274
|
+
).length;
|
|
275
|
+
return Object.freeze({
|
|
276
|
+
schema: state.schema,
|
|
277
|
+
runnerId: state.runnerId,
|
|
278
|
+
target: state.target,
|
|
279
|
+
state: state.state,
|
|
280
|
+
accepting: state.state === "accepting",
|
|
281
|
+
generation: state.generation,
|
|
282
|
+
policyRevision: state.policyRevision,
|
|
283
|
+
baseDirDigest: receiptDigest(
|
|
284
|
+
"chainlesschain.execution-location.base-dir.v1\0",
|
|
285
|
+
state.baseDir,
|
|
286
|
+
),
|
|
287
|
+
resources: Object.freeze({ ...state.resources }),
|
|
288
|
+
proxyAuthority: Object.freeze({ ...state.proxyAuthority }),
|
|
289
|
+
drain: state.drain ? Object.freeze({ ...state.drain }) : null,
|
|
290
|
+
activeLeaseCount,
|
|
291
|
+
parkedLeaseCount: state.leases.filter((lease) => lease.status === "parked")
|
|
292
|
+
.length,
|
|
293
|
+
settledLeaseCount: state.leases.filter(
|
|
294
|
+
(lease) => lease.status === "settled",
|
|
295
|
+
).length,
|
|
296
|
+
updatedAt: state.updatedAt,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function preflightWritableDirectory(
|
|
301
|
+
baseDir,
|
|
302
|
+
runtimeFs = fs,
|
|
303
|
+
randomId = randomUUID,
|
|
304
|
+
) {
|
|
305
|
+
const stat = runtimeFs.lstatSync(baseDir);
|
|
306
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
"execution location base directory must be a real directory",
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
const probe = path.join(baseDir, `.cc-runner-preflight-${randomId()}`);
|
|
312
|
+
let descriptor = null;
|
|
313
|
+
let created = false;
|
|
314
|
+
try {
|
|
315
|
+
descriptor = runtimeFs.openSync(probe, "wx", 0o600);
|
|
316
|
+
created = true;
|
|
317
|
+
runtimeFs.writeFileSync(descriptor, "preflight\n", "utf8");
|
|
318
|
+
runtimeFs.fsyncSync(descriptor);
|
|
319
|
+
} finally {
|
|
320
|
+
if (descriptor !== null) runtimeFs.closeSync(descriptor);
|
|
321
|
+
if (created) {
|
|
322
|
+
try {
|
|
323
|
+
runtimeFs.unlinkSync(probe);
|
|
324
|
+
} catch {
|
|
325
|
+
// Preserve the original preflight error.
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export class ExecutionLocationRunnerLifecycle {
|
|
332
|
+
constructor({
|
|
333
|
+
filePath,
|
|
334
|
+
runnerId,
|
|
335
|
+
target,
|
|
336
|
+
baseDir,
|
|
337
|
+
resources,
|
|
338
|
+
postSessionHookDigest,
|
|
339
|
+
now = Date.now,
|
|
340
|
+
randomId = randomUUID,
|
|
341
|
+
runtimeFs = fs,
|
|
342
|
+
preflightBaseDir = preflightWritableDirectory,
|
|
343
|
+
normalizeBaseDir = path.resolve,
|
|
344
|
+
lockOptions,
|
|
345
|
+
} = {}) {
|
|
346
|
+
if (!path.isAbsolute(String(filePath || ""))) {
|
|
347
|
+
throw new TypeError("runner lifecycle filePath must be absolute");
|
|
348
|
+
}
|
|
349
|
+
this.filePath = path.resolve(filePath);
|
|
350
|
+
this.runnerId = safeId(runnerId, "runnerId");
|
|
351
|
+
this.target = safeId(target, "runner target");
|
|
352
|
+
if (!TARGETS.has(this.target))
|
|
353
|
+
throw new TypeError("runner target is invalid");
|
|
354
|
+
if (typeof normalizeBaseDir !== "function") {
|
|
355
|
+
throw new TypeError("runner normalizeBaseDir must be a function");
|
|
356
|
+
}
|
|
357
|
+
this.baseDir = String(normalizeBaseDir(baseDir));
|
|
358
|
+
if (
|
|
359
|
+
this.baseDir.length < 1 ||
|
|
360
|
+
this.baseDir.length > 4096 ||
|
|
361
|
+
this.baseDir.includes("\0")
|
|
362
|
+
) {
|
|
363
|
+
throw new TypeError("runner baseDir is invalid");
|
|
364
|
+
}
|
|
365
|
+
this.resources = validateResources(resources);
|
|
366
|
+
this.postSessionHookDigest = safeDigest(
|
|
367
|
+
postSessionHookDigest,
|
|
368
|
+
"postSessionHookDigest",
|
|
369
|
+
);
|
|
370
|
+
this._now = now;
|
|
371
|
+
this._randomId = randomId;
|
|
372
|
+
this._fs = runtimeFs;
|
|
373
|
+
if (typeof preflightBaseDir !== "function") {
|
|
374
|
+
throw new TypeError("runner preflightBaseDir must be a function");
|
|
375
|
+
}
|
|
376
|
+
this._preflightBaseDir = preflightBaseDir;
|
|
377
|
+
this._lockOptions = lockOptions;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
initialize() {
|
|
381
|
+
const now = new Date(this._now()).toISOString();
|
|
382
|
+
mutateSecurityStore(
|
|
383
|
+
this.filePath,
|
|
384
|
+
STORE_LABEL,
|
|
385
|
+
(draft) => {
|
|
386
|
+
if (Object.keys(draft).length > 0) {
|
|
387
|
+
validateState(draft, this);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
Object.assign(draft, {
|
|
391
|
+
schema: EXECUTION_LOCATION_RUNNER_LIFECYCLE_SCHEMA,
|
|
392
|
+
runnerId: this.runnerId,
|
|
393
|
+
target: this.target,
|
|
394
|
+
state: "accepting",
|
|
395
|
+
generation: 1,
|
|
396
|
+
policyRevision: 1,
|
|
397
|
+
baseDir: this.baseDir,
|
|
398
|
+
resources: { ...this.resources },
|
|
399
|
+
proxyAuthority: { id: "unbound", revision: 0 },
|
|
400
|
+
postSessionHookDigest: this.postSessionHookDigest,
|
|
401
|
+
drain: null,
|
|
402
|
+
leases: [],
|
|
403
|
+
updatedAt: now,
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
this._lockOptions,
|
|
407
|
+
);
|
|
408
|
+
return this.snapshot();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
_read() {
|
|
412
|
+
return validateState(readSecurityStore(this.filePath, STORE_LABEL), this);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
snapshot() {
|
|
416
|
+
return project(this._read());
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
acquireLease({
|
|
420
|
+
sessionId,
|
|
421
|
+
expectedGeneration,
|
|
422
|
+
ttlMs = 10 * 60 * 1000,
|
|
423
|
+
proxyAuthority,
|
|
424
|
+
} = {}) {
|
|
425
|
+
const nowMs = Number(this._now());
|
|
426
|
+
const now = new Date(nowMs).toISOString();
|
|
427
|
+
const normalizedSessionId = safeId(sessionId, "sessionId");
|
|
428
|
+
const normalizedProxy = validateProxyAuthority(proxyAuthority, nowMs);
|
|
429
|
+
const leaseId = `lease-${this._randomId()}`;
|
|
430
|
+
const expiresAt = new Date(
|
|
431
|
+
nowMs + safeInteger(ttlMs, "lease ttlMs", 1_000, 24 * 60 * 60 * 1000),
|
|
432
|
+
).toISOString();
|
|
433
|
+
this._preflightBaseDir(this.baseDir, this._fs, this._randomId);
|
|
434
|
+
let lease;
|
|
435
|
+
mutateSecurityStore(
|
|
436
|
+
this.filePath,
|
|
437
|
+
STORE_LABEL,
|
|
438
|
+
(draft) => {
|
|
439
|
+
const state = validateState(draft, this);
|
|
440
|
+
if (
|
|
441
|
+
state.state !== "accepting" ||
|
|
442
|
+
state.generation !== Number(expectedGeneration)
|
|
443
|
+
) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
"execution location runner is not accepting this generation",
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
if (
|
|
449
|
+
state.leases.some(
|
|
450
|
+
(candidate) =>
|
|
451
|
+
candidate.sessionId === normalizedSessionId &&
|
|
452
|
+
candidate.status === "active",
|
|
453
|
+
)
|
|
454
|
+
) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
"execution location session already has an active lease",
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
if (
|
|
460
|
+
state.proxyAuthority.id !== "unbound" &&
|
|
461
|
+
state.proxyAuthority.id !== normalizedProxy.id
|
|
462
|
+
) {
|
|
463
|
+
throw new Error(
|
|
464
|
+
"execution location proxy authority identity drifted",
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
if (normalizedProxy.revision < state.proxyAuthority.revision) {
|
|
468
|
+
throw new Error(
|
|
469
|
+
"execution location proxy authority revision rolled back",
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
lease = {
|
|
473
|
+
id: leaseId,
|
|
474
|
+
sessionId: normalizedSessionId,
|
|
475
|
+
generation: state.generation,
|
|
476
|
+
issuedAt: now,
|
|
477
|
+
expiresAt,
|
|
478
|
+
status: "active",
|
|
479
|
+
resultDigest: null,
|
|
480
|
+
hookReceiptDigest: null,
|
|
481
|
+
};
|
|
482
|
+
Object.assign(draft, state, {
|
|
483
|
+
proxyAuthority: {
|
|
484
|
+
id: normalizedProxy.id,
|
|
485
|
+
revision: normalizedProxy.revision,
|
|
486
|
+
},
|
|
487
|
+
leases: [...state.leases.slice(-(MAX_RETAINED_LEASES - 1)), lease],
|
|
488
|
+
updatedAt: now,
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
this._lockOptions,
|
|
492
|
+
);
|
|
493
|
+
const material = {
|
|
494
|
+
runnerId: this.runnerId,
|
|
495
|
+
target: this.target,
|
|
496
|
+
authorityFile: this.filePath,
|
|
497
|
+
state: "accepting",
|
|
498
|
+
runnerGeneration: Number(expectedGeneration),
|
|
499
|
+
lease,
|
|
500
|
+
proxyAuthority: normalizedProxy,
|
|
501
|
+
baseDir: { path: this.baseDir, writableRequired: true },
|
|
502
|
+
resources: { ...this.resources },
|
|
503
|
+
postSessionHook: {
|
|
504
|
+
digest: this.postSessionHookDigest,
|
|
505
|
+
generation: lease.generation,
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
return Object.freeze({
|
|
509
|
+
...material,
|
|
510
|
+
leaseReceiptDigest: receiptDigest(
|
|
511
|
+
"chainlesschain.execution-location.runner-lease.v1\0",
|
|
512
|
+
material,
|
|
513
|
+
),
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
assertPoll({
|
|
518
|
+
leaseId,
|
|
519
|
+
leaseGeneration,
|
|
520
|
+
proxyAuthorityId,
|
|
521
|
+
proxyAuthorityRevision,
|
|
522
|
+
} = {}) {
|
|
523
|
+
const state = this._read();
|
|
524
|
+
const lease = state.leases.find((candidate) => candidate.id === leaseId);
|
|
525
|
+
if (
|
|
526
|
+
!lease ||
|
|
527
|
+
lease.status !== "active" ||
|
|
528
|
+
lease.generation !== Number(leaseGeneration) ||
|
|
529
|
+
Date.parse(lease.expiresAt) <= this._now() ||
|
|
530
|
+
state.proxyAuthority.id !== proxyAuthorityId ||
|
|
531
|
+
state.proxyAuthority.revision !== Number(proxyAuthorityRevision) ||
|
|
532
|
+
state.state === "parked" ||
|
|
533
|
+
state.state === "reclaiming"
|
|
534
|
+
) {
|
|
535
|
+
throw new Error("execution location poll lease is stale or parked");
|
|
536
|
+
}
|
|
537
|
+
return Object.freeze({
|
|
538
|
+
runnerId: state.runnerId,
|
|
539
|
+
runnerGeneration: state.generation,
|
|
540
|
+
leaseId: lease.id,
|
|
541
|
+
leaseGeneration: lease.generation,
|
|
542
|
+
state: state.state,
|
|
543
|
+
proxyAuthority: Object.freeze({ ...state.proxyAuthority }),
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
rotateProxyAuthority({ expectedGeneration, proxyAuthority } = {}) {
|
|
548
|
+
const nowMs = Number(this._now());
|
|
549
|
+
const now = new Date(nowMs).toISOString();
|
|
550
|
+
let normalizedProxy;
|
|
551
|
+
mutateSecurityStore(
|
|
552
|
+
this.filePath,
|
|
553
|
+
STORE_LABEL,
|
|
554
|
+
(draft) => {
|
|
555
|
+
const state = validateState(draft, this);
|
|
556
|
+
normalizedProxy = validateProxyAuthority(
|
|
557
|
+
proxyAuthority,
|
|
558
|
+
nowMs,
|
|
559
|
+
state.proxyAuthority.revision + 1,
|
|
560
|
+
);
|
|
561
|
+
if (
|
|
562
|
+
(state.state !== "accepting" && state.state !== "draining") ||
|
|
563
|
+
state.generation !== Number(expectedGeneration) ||
|
|
564
|
+
normalizedProxy.id !== state.proxyAuthority.id
|
|
565
|
+
) {
|
|
566
|
+
throw new Error("execution location proxy rotation fence is stale");
|
|
567
|
+
}
|
|
568
|
+
Object.assign(draft, state, {
|
|
569
|
+
generation: state.generation + 1,
|
|
570
|
+
policyRevision: state.policyRevision + 1,
|
|
571
|
+
proxyAuthority: {
|
|
572
|
+
id: normalizedProxy.id,
|
|
573
|
+
revision: normalizedProxy.revision,
|
|
574
|
+
},
|
|
575
|
+
updatedAt: now,
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
this._lockOptions,
|
|
579
|
+
);
|
|
580
|
+
return this.snapshot();
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
refreshLeaseAuthority({
|
|
584
|
+
leaseId,
|
|
585
|
+
leaseGeneration,
|
|
586
|
+
expectedGeneration,
|
|
587
|
+
proxyAuthority,
|
|
588
|
+
} = {}) {
|
|
589
|
+
const nowMs = Number(this._now());
|
|
590
|
+
const state = this._read();
|
|
591
|
+
const normalizedProxy = validateProxyAuthority(
|
|
592
|
+
proxyAuthority,
|
|
593
|
+
nowMs,
|
|
594
|
+
state.proxyAuthority.revision,
|
|
595
|
+
);
|
|
596
|
+
const lease = state.leases.find((candidate) => candidate.id === leaseId);
|
|
597
|
+
if (
|
|
598
|
+
state.state !== "accepting" ||
|
|
599
|
+
state.generation !== Number(expectedGeneration) ||
|
|
600
|
+
state.proxyAuthority.id !== normalizedProxy.id ||
|
|
601
|
+
state.proxyAuthority.revision !== normalizedProxy.revision ||
|
|
602
|
+
!lease ||
|
|
603
|
+
lease.status !== "active" ||
|
|
604
|
+
lease.generation !== Number(leaseGeneration) ||
|
|
605
|
+
Date.parse(lease.expiresAt) <= nowMs
|
|
606
|
+
) {
|
|
607
|
+
throw new Error("execution location lease refresh fence is stale");
|
|
608
|
+
}
|
|
609
|
+
this._preflightBaseDir(this.baseDir, this._fs, this._randomId);
|
|
610
|
+
const material = {
|
|
611
|
+
runnerId: this.runnerId,
|
|
612
|
+
target: this.target,
|
|
613
|
+
authorityFile: this.filePath,
|
|
614
|
+
state: state.state,
|
|
615
|
+
runnerGeneration: state.generation,
|
|
616
|
+
lease: { ...lease },
|
|
617
|
+
proxyAuthority: normalizedProxy,
|
|
618
|
+
baseDir: { path: this.baseDir, writableRequired: true },
|
|
619
|
+
resources: { ...this.resources },
|
|
620
|
+
postSessionHook: {
|
|
621
|
+
digest: this.postSessionHookDigest,
|
|
622
|
+
generation: lease.generation,
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
return Object.freeze({
|
|
626
|
+
...material,
|
|
627
|
+
leaseReceiptDigest: receiptDigest(
|
|
628
|
+
"chainlesschain.execution-location.runner-lease.v1\0",
|
|
629
|
+
material,
|
|
630
|
+
),
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
requestDrain({
|
|
635
|
+
expectedGeneration,
|
|
636
|
+
timeoutMs = 30_000,
|
|
637
|
+
signal = "SIGTERM",
|
|
638
|
+
} = {}) {
|
|
639
|
+
if (signal !== "SIGTERM")
|
|
640
|
+
throw new TypeError("runner drain requires SIGTERM");
|
|
641
|
+
const nowMs = Number(this._now());
|
|
642
|
+
const requestedAt = new Date(nowMs).toISOString();
|
|
643
|
+
const deadlineAt = new Date(
|
|
644
|
+
nowMs + safeInteger(timeoutMs, "drain timeoutMs", 1, 10 * 60 * 1000),
|
|
645
|
+
).toISOString();
|
|
646
|
+
mutateSecurityStore(
|
|
647
|
+
this.filePath,
|
|
648
|
+
STORE_LABEL,
|
|
649
|
+
(draft) => {
|
|
650
|
+
const state = validateState(draft, this);
|
|
651
|
+
if (
|
|
652
|
+
state.state !== "accepting" ||
|
|
653
|
+
state.generation !== Number(expectedGeneration)
|
|
654
|
+
) {
|
|
655
|
+
throw new Error("execution location drain generation is stale");
|
|
656
|
+
}
|
|
657
|
+
Object.assign(draft, state, {
|
|
658
|
+
state: "draining",
|
|
659
|
+
generation: state.generation + 1,
|
|
660
|
+
drain: { signal, requestedAt, deadlineAt },
|
|
661
|
+
updatedAt: requestedAt,
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
this._lockOptions,
|
|
665
|
+
);
|
|
666
|
+
return this.snapshot();
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
settleLease({ leaseId, leaseGeneration, resultDigest } = {}) {
|
|
670
|
+
const normalizedResultDigest = safeDigest(resultDigest, "resultDigest");
|
|
671
|
+
const now = new Date(this._now()).toISOString();
|
|
672
|
+
mutateSecurityStore(
|
|
673
|
+
this.filePath,
|
|
674
|
+
STORE_LABEL,
|
|
675
|
+
(draft) => {
|
|
676
|
+
const state = validateState(draft, this);
|
|
677
|
+
const index = state.leases.findIndex((lease) => lease.id === leaseId);
|
|
678
|
+
const lease = state.leases[index];
|
|
679
|
+
if (
|
|
680
|
+
!lease ||
|
|
681
|
+
lease.status !== "active" ||
|
|
682
|
+
lease.generation !== Number(leaseGeneration)
|
|
683
|
+
) {
|
|
684
|
+
throw new Error("execution location result lease fence is stale");
|
|
685
|
+
}
|
|
686
|
+
const leases = state.leases.map((candidate, candidateIndex) =>
|
|
687
|
+
candidateIndex === index
|
|
688
|
+
? {
|
|
689
|
+
...candidate,
|
|
690
|
+
status: "settled",
|
|
691
|
+
resultDigest: normalizedResultDigest,
|
|
692
|
+
}
|
|
693
|
+
: candidate,
|
|
694
|
+
);
|
|
695
|
+
const parkRunner =
|
|
696
|
+
state.state === "draining" &&
|
|
697
|
+
leases.every((candidate) => candidate.status !== "active");
|
|
698
|
+
Object.assign(draft, state, {
|
|
699
|
+
state: parkRunner ? "parked" : state.state,
|
|
700
|
+
generation: parkRunner ? state.generation + 1 : state.generation,
|
|
701
|
+
drain: parkRunner ? null : state.drain,
|
|
702
|
+
leases,
|
|
703
|
+
updatedAt: now,
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
this._lockOptions,
|
|
707
|
+
);
|
|
708
|
+
return this.snapshot();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
parkLease({ leaseId, leaseGeneration, resultDigest, reason } = {}) {
|
|
712
|
+
const normalizedResultDigest = safeDigest(resultDigest, "resultDigest");
|
|
713
|
+
if (
|
|
714
|
+
reason !== "checkout-failure" &&
|
|
715
|
+
reason !== "lost-poll" &&
|
|
716
|
+
reason !== "resource-limit"
|
|
717
|
+
) {
|
|
718
|
+
throw new TypeError("execution location park reason is invalid");
|
|
719
|
+
}
|
|
720
|
+
const now = new Date(this._now()).toISOString();
|
|
721
|
+
mutateSecurityStore(
|
|
722
|
+
this.filePath,
|
|
723
|
+
STORE_LABEL,
|
|
724
|
+
(draft) => {
|
|
725
|
+
const state = validateState(draft, this);
|
|
726
|
+
const index = state.leases.findIndex((lease) => lease.id === leaseId);
|
|
727
|
+
const lease = state.leases[index];
|
|
728
|
+
if (
|
|
729
|
+
(state.state !== "accepting" && state.state !== "draining") ||
|
|
730
|
+
!lease ||
|
|
731
|
+
lease.status !== "active" ||
|
|
732
|
+
lease.generation !== Number(leaseGeneration)
|
|
733
|
+
) {
|
|
734
|
+
throw new Error("execution location park lease fence is stale");
|
|
735
|
+
}
|
|
736
|
+
const leases = state.leases.map((candidate, candidateIndex) =>
|
|
737
|
+
candidateIndex === index
|
|
738
|
+
? {
|
|
739
|
+
...candidate,
|
|
740
|
+
status: "parked",
|
|
741
|
+
resultDigest: normalizedResultDigest,
|
|
742
|
+
}
|
|
743
|
+
: candidate,
|
|
744
|
+
);
|
|
745
|
+
const parkRunner = leases.every(
|
|
746
|
+
(candidate) => candidate.status !== "active",
|
|
747
|
+
);
|
|
748
|
+
Object.assign(draft, state, {
|
|
749
|
+
state: parkRunner ? "parked" : state.state,
|
|
750
|
+
generation: parkRunner ? state.generation + 1 : state.generation,
|
|
751
|
+
drain: parkRunner ? null : state.drain,
|
|
752
|
+
leases,
|
|
753
|
+
updatedAt: now,
|
|
754
|
+
});
|
|
755
|
+
},
|
|
756
|
+
this._lockOptions,
|
|
757
|
+
);
|
|
758
|
+
return Object.freeze({ ...this.snapshot(), parkReason: reason });
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
parkExpiredDrain({ expectedGeneration } = {}) {
|
|
762
|
+
const nowMs = Number(this._now());
|
|
763
|
+
const now = new Date(nowMs).toISOString();
|
|
764
|
+
let parked = 0;
|
|
765
|
+
mutateSecurityStore(
|
|
766
|
+
this.filePath,
|
|
767
|
+
STORE_LABEL,
|
|
768
|
+
(draft) => {
|
|
769
|
+
const state = validateState(draft, this);
|
|
770
|
+
if (
|
|
771
|
+
state.state !== "draining" ||
|
|
772
|
+
state.generation !== Number(expectedGeneration) ||
|
|
773
|
+
Date.parse(state.drain.deadlineAt) > nowMs
|
|
774
|
+
) {
|
|
775
|
+
throw new Error("execution location drain is not ready to park");
|
|
776
|
+
}
|
|
777
|
+
const leases = state.leases.map((lease) => {
|
|
778
|
+
if (lease.status !== "active") return lease;
|
|
779
|
+
parked += 1;
|
|
780
|
+
return { ...lease, status: "parked" };
|
|
781
|
+
});
|
|
782
|
+
Object.assign(draft, state, {
|
|
783
|
+
state: "parked",
|
|
784
|
+
generation: state.generation + 1,
|
|
785
|
+
drain: null,
|
|
786
|
+
leases,
|
|
787
|
+
updatedAt: now,
|
|
788
|
+
});
|
|
789
|
+
},
|
|
790
|
+
this._lockOptions,
|
|
791
|
+
);
|
|
792
|
+
return Object.freeze({ ...this.snapshot(), newlyParkedLeaseCount: parked });
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
beginReclaim({ expectedGeneration, proxyAuthority } = {}) {
|
|
796
|
+
const nowMs = Number(this._now());
|
|
797
|
+
const now = new Date(nowMs).toISOString();
|
|
798
|
+
let normalizedProxy;
|
|
799
|
+
mutateSecurityStore(
|
|
800
|
+
this.filePath,
|
|
801
|
+
STORE_LABEL,
|
|
802
|
+
(draft) => {
|
|
803
|
+
const state = validateState(draft, this);
|
|
804
|
+
normalizedProxy = validateProxyAuthority(
|
|
805
|
+
proxyAuthority,
|
|
806
|
+
nowMs,
|
|
807
|
+
state.proxyAuthority.revision + 1,
|
|
808
|
+
);
|
|
809
|
+
if (
|
|
810
|
+
state.state !== "parked" ||
|
|
811
|
+
state.generation !== Number(expectedGeneration) ||
|
|
812
|
+
normalizedProxy.id !== state.proxyAuthority.id
|
|
813
|
+
) {
|
|
814
|
+
throw new Error("execution location reclaim fence is stale");
|
|
815
|
+
}
|
|
816
|
+
Object.assign(draft, state, {
|
|
817
|
+
state: "reclaiming",
|
|
818
|
+
generation: state.generation + 1,
|
|
819
|
+
proxyAuthority: {
|
|
820
|
+
id: normalizedProxy.id,
|
|
821
|
+
revision: normalizedProxy.revision,
|
|
822
|
+
},
|
|
823
|
+
updatedAt: now,
|
|
824
|
+
});
|
|
825
|
+
},
|
|
826
|
+
this._lockOptions,
|
|
827
|
+
);
|
|
828
|
+
return this.snapshot();
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
completeReclaim({ expectedGeneration } = {}) {
|
|
832
|
+
const now = new Date(this._now()).toISOString();
|
|
833
|
+
mutateSecurityStore(
|
|
834
|
+
this.filePath,
|
|
835
|
+
STORE_LABEL,
|
|
836
|
+
(draft) => {
|
|
837
|
+
const state = validateState(draft, this);
|
|
838
|
+
if (
|
|
839
|
+
state.state !== "reclaiming" ||
|
|
840
|
+
state.generation !== Number(expectedGeneration)
|
|
841
|
+
) {
|
|
842
|
+
throw new Error("execution location reclaim completion is stale");
|
|
843
|
+
}
|
|
844
|
+
Object.assign(draft, state, {
|
|
845
|
+
state: "accepting",
|
|
846
|
+
generation: state.generation + 1,
|
|
847
|
+
leases: state.leases.slice(-256),
|
|
848
|
+
updatedAt: now,
|
|
849
|
+
});
|
|
850
|
+
},
|
|
851
|
+
this._lockOptions,
|
|
852
|
+
);
|
|
853
|
+
return this.snapshot();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
authorizePostSessionHook({
|
|
857
|
+
expectedRunnerGeneration,
|
|
858
|
+
leaseId,
|
|
859
|
+
leaseGeneration,
|
|
860
|
+
resultDigest,
|
|
861
|
+
hookDigest,
|
|
862
|
+
} = {}) {
|
|
863
|
+
const normalizedResultDigest = safeDigest(resultDigest, "resultDigest");
|
|
864
|
+
const normalizedHookDigest = safeDigest(hookDigest, "hookDigest");
|
|
865
|
+
const now = new Date(this._now()).toISOString();
|
|
866
|
+
let receipt;
|
|
867
|
+
mutateSecurityStore(
|
|
868
|
+
this.filePath,
|
|
869
|
+
STORE_LABEL,
|
|
870
|
+
(draft) => {
|
|
871
|
+
const state = validateState(draft, this);
|
|
872
|
+
const index = state.leases.findIndex((lease) => lease.id === leaseId);
|
|
873
|
+
const lease = state.leases[index];
|
|
874
|
+
if (
|
|
875
|
+
state.generation !== Number(expectedRunnerGeneration) ||
|
|
876
|
+
(state.state !== "parked" && state.state !== "draining") ||
|
|
877
|
+
!lease ||
|
|
878
|
+
lease.generation !== Number(leaseGeneration) ||
|
|
879
|
+
lease.status === "active" ||
|
|
880
|
+
lease.resultDigest !== normalizedResultDigest ||
|
|
881
|
+
lease.hookReceiptDigest !== null ||
|
|
882
|
+
normalizedHookDigest !== state.postSessionHookDigest
|
|
883
|
+
) {
|
|
884
|
+
throw new Error(
|
|
885
|
+
"execution location post-session hook fence is stale",
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
const material = {
|
|
889
|
+
runnerId: state.runnerId,
|
|
890
|
+
runnerGeneration: state.generation,
|
|
891
|
+
leaseId: lease.id,
|
|
892
|
+
leaseGeneration: lease.generation,
|
|
893
|
+
resultDigest: normalizedResultDigest,
|
|
894
|
+
hookDigest: normalizedHookDigest,
|
|
895
|
+
};
|
|
896
|
+
receipt = {
|
|
897
|
+
...material,
|
|
898
|
+
receiptDigest: receiptDigest(
|
|
899
|
+
"chainlesschain.execution-location.post-session-hook.v1\0",
|
|
900
|
+
material,
|
|
901
|
+
),
|
|
902
|
+
};
|
|
903
|
+
const leases = state.leases.map((candidate, candidateIndex) =>
|
|
904
|
+
candidateIndex === index
|
|
905
|
+
? { ...candidate, hookReceiptDigest: receipt.receiptDigest }
|
|
906
|
+
: candidate,
|
|
907
|
+
);
|
|
908
|
+
Object.assign(draft, state, { leases, updatedAt: now });
|
|
909
|
+
},
|
|
910
|
+
this._lockOptions,
|
|
911
|
+
);
|
|
912
|
+
return Object.freeze(receipt);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
export function lifecycleProfileFromLease(leaseReceipt) {
|
|
917
|
+
if (!leaseReceipt || typeof leaseReceipt !== "object") {
|
|
918
|
+
throw new TypeError("runner lease receipt is required");
|
|
919
|
+
}
|
|
920
|
+
return Object.freeze({
|
|
921
|
+
runnerId: leaseReceipt.runnerId,
|
|
922
|
+
authorityFile: leaseReceipt.authorityFile,
|
|
923
|
+
state: leaseReceipt.state,
|
|
924
|
+
generation: leaseReceipt.runnerGeneration,
|
|
925
|
+
lease: Object.freeze({
|
|
926
|
+
id: leaseReceipt.lease.id,
|
|
927
|
+
generation: leaseReceipt.lease.generation,
|
|
928
|
+
expiresAt: leaseReceipt.lease.expiresAt,
|
|
929
|
+
}),
|
|
930
|
+
proxyAuthority: Object.freeze({ ...leaseReceipt.proxyAuthority }),
|
|
931
|
+
baseDir: Object.freeze({ ...leaseReceipt.baseDir }),
|
|
932
|
+
resources: Object.freeze({ ...leaseReceipt.resources }),
|
|
933
|
+
postSessionHook: Object.freeze({ ...leaseReceipt.postSessionHook }),
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
export function assertExecutionLocationRunnerLeaseAuthority(
|
|
938
|
+
lifecycle,
|
|
939
|
+
target,
|
|
940
|
+
deps = {},
|
|
941
|
+
) {
|
|
942
|
+
if (!lifecycle || typeof lifecycle !== "object") {
|
|
943
|
+
throw new TypeError("runner lifecycle authority is required");
|
|
944
|
+
}
|
|
945
|
+
const authorityFileInput = String(lifecycle.authorityFile || "");
|
|
946
|
+
if (
|
|
947
|
+
!path.isAbsolute(authorityFileInput) ||
|
|
948
|
+
authorityFileInput.length > 4096 ||
|
|
949
|
+
authorityFileInput.includes("\0")
|
|
950
|
+
) {
|
|
951
|
+
throw new TypeError("runner authority file is invalid");
|
|
952
|
+
}
|
|
953
|
+
const authorityFile = path.resolve(authorityFileInput);
|
|
954
|
+
const state = validateState(
|
|
955
|
+
(deps.readSecurityStore || readSecurityStore)(authorityFile, STORE_LABEL),
|
|
956
|
+
{
|
|
957
|
+
runnerId: lifecycle.runnerId,
|
|
958
|
+
target,
|
|
959
|
+
baseDir: lifecycle.baseDir?.path,
|
|
960
|
+
},
|
|
961
|
+
);
|
|
962
|
+
const lease = state.leases.find(
|
|
963
|
+
(candidate) => candidate.id === lifecycle.lease?.id,
|
|
964
|
+
);
|
|
965
|
+
if (
|
|
966
|
+
state.state !== "accepting" ||
|
|
967
|
+
state.generation !== Number(lifecycle.generation) ||
|
|
968
|
+
state.resources.cpuSeconds !== Number(lifecycle.resources?.cpuSeconds) ||
|
|
969
|
+
state.resources.memoryBytes !== Number(lifecycle.resources?.memoryBytes) ||
|
|
970
|
+
state.proxyAuthority.id !== lifecycle.proxyAuthority?.id ||
|
|
971
|
+
state.proxyAuthority.revision !==
|
|
972
|
+
Number(lifecycle.proxyAuthority?.revision) ||
|
|
973
|
+
state.postSessionHookDigest !== lifecycle.postSessionHook?.digest ||
|
|
974
|
+
!lease ||
|
|
975
|
+
lease.status !== "active" ||
|
|
976
|
+
lease.generation !== Number(lifecycle.lease?.generation) ||
|
|
977
|
+
lease.expiresAt !== lifecycle.lease?.expiresAt ||
|
|
978
|
+
Date.parse(lease.expiresAt) <= Number((deps.now || Date.now)())
|
|
979
|
+
) {
|
|
980
|
+
throw new Error("execution location runner lease authority is stale");
|
|
981
|
+
}
|
|
982
|
+
return Object.freeze({
|
|
983
|
+
runnerId: state.runnerId,
|
|
984
|
+
target: state.target,
|
|
985
|
+
state: state.state,
|
|
986
|
+
generation: state.generation,
|
|
987
|
+
leaseId: lease.id,
|
|
988
|
+
leaseGeneration: lease.generation,
|
|
989
|
+
proxyAuthority: Object.freeze({ ...state.proxyAuthority }),
|
|
990
|
+
});
|
|
991
|
+
}
|