chainlesschain 0.162.188 → 0.162.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -11
- package/bin/chainlesschain.js +7 -52
- package/completions/_cc +209 -0
- package/completions/cc.bash +18 -0
- package/completions/cc.fish +13 -0
- package/completions/cc.ps1 +226 -0
- package/package.json +10 -2
- package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
- package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
- package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
- package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
- package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
- package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
- package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
- package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
- package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
- package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
- package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
- package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
- package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
- package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
- package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
- package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
- package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
- package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/cli-help.js +181 -0
- package/src/command-help-index.json +181 -0
- package/src/command-manifest.json +1107 -175
- package/src/command-surface-policy.js +197 -0
- package/src/commands/agent.js +98 -33
- package/src/commands/artifacts.js +400 -2
- package/src/commands/background-session.js +41 -3
- package/src/commands/checkpoint-managed.js +1059 -0
- package/src/commands/checkpoint-restore-recovery.js +1476 -0
- package/src/commands/checkpoint.js +950 -22
- package/src/commands/compact.js +101 -25
- package/src/commands/config.js +506 -20
- package/src/commands/doctor.js +4 -1
- package/src/commands/hub.js +1 -1
- package/src/commands/mcp.js +688 -24
- package/src/commands/pack.js +16 -2
- package/src/commands/plugin.js +20 -12
- package/src/commands/session-mcp-recovery.js +218 -0
- package/src/commands/session.js +470 -110
- package/src/commands/setup.js +36 -10
- package/src/commands/skill.js +181 -110
- package/src/commands/status.js +21 -5
- package/src/commands/team-distributed.js +3324 -0
- package/src/commands/team.js +1130 -74
- package/src/commands/update.js +68 -6
- package/src/constants.js +11 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/action-protocol.js +10 -0
- package/src/gateways/ws/session-protocol.js +555 -27
- package/src/gateways/ws/ws-agent-handler.js +541 -14
- package/src/gateways/ws/ws-session-gateway.js +129 -22
- package/src/gateways/ws/ws-session-state.js +431 -0
- package/src/harness/background-task-manager.js +233 -36
- package/src/harness/jsonl-session-store.js +2551 -167
- package/src/harness/mcp-client.js +802 -61
- package/src/harness/prompt-compressor.js +227 -27
- package/src/harness/session-index.js +89 -38
- package/src/harness/session-list-index.js +254 -0
- package/src/harness/structured-handoff.js +410 -0
- package/src/lazy-dispatch.js +747 -84
- package/src/lib/__tests__/logger.test.js +50 -2
- package/src/lib/advisor-runtime.js +1047 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +120 -26
- package/src/lib/agent-session-export.js +20 -0
- package/src/lib/agent-team/task-lease.js +271 -3
- package/src/lib/agent-team/team-adjudication.js +1501 -0
- package/src/lib/agent-team/team-budget.js +4 -1
- package/src/lib/agent-team/team-control-store.js +582 -0
- package/src/lib/agent-team/team-distributed-queue.js +5246 -0
- package/src/lib/agent-team/team-process-checkpoint.js +211 -0
- package/src/lib/agent-team/team-runner.js +543 -24
- package/src/lib/agent-team/team-worktree.js +1403 -95
- package/src/lib/artifact-store.js +131 -8
- package/src/lib/background-agent-supervisor.js +185 -13
- package/src/lib/background-interaction-journal.js +37 -9
- package/src/lib/background-launch-profile.js +1058 -0
- package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
- package/src/lib/checkpoint-restore-orchestrator.js +987 -0
- package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
- package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
- package/src/lib/checkpoint-restore-recovery.js +884 -0
- package/src/lib/checkpoint-restore-retention.js +126 -0
- package/src/lib/checkpoint-restore-saga.js +5079 -0
- package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
- package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
- package/src/lib/checkpoint-store.js +1688 -97
- package/src/lib/checkpoint-timeline-authority.js +522 -0
- package/src/lib/checkpoint-timeline.js +642 -0
- package/src/lib/config-keys.js +18 -119
- package/src/lib/config-manager.js +446 -27
- package/src/lib/config-redaction.js +57 -0
- package/src/lib/config-schema.js +853 -0
- package/src/lib/cowork-mcp-tools.js +5 -0
- package/src/lib/cowork-task-runner.js +458 -63
- package/src/lib/delivery-coordinator.js +1234 -0
- package/src/lib/delivery-evidence.js +450 -0
- package/src/lib/doctor-checkup.js +199 -14
- package/src/lib/executable-path.js +34 -0
- package/src/lib/fatal-handler.js +16 -0
- package/src/lib/file-checkpoint.js +4909 -186
- package/src/lib/file-lines.js +125 -0
- package/src/lib/headless-config-command.js +16 -5
- package/src/lib/hook-runner.cjs +139 -38
- package/src/lib/hooks-v2-workspace-context.js +10 -0
- package/src/lib/impacted-gate-selector.js +424 -0
- package/src/lib/interaction-adapter.js +62 -0
- package/src/lib/jsonl-session-store.js +1 -35
- package/src/lib/logger.js +66 -14
- package/src/lib/managed-checkpoint-render.js +60 -0
- package/src/lib/managed-tool-checkpoint.js +194 -0
- package/src/lib/mcp-call-ledger-store.js +1069 -0
- package/src/lib/mcp-call-ledger.js +747 -0
- package/src/lib/mcp-client.js +5 -0
- package/src/lib/mcp-conflict-scheduler.js +468 -0
- package/src/lib/mcp-effect-contract.js +102 -0
- package/src/lib/mcp-headers-helper-trust.js +145 -0
- package/src/lib/mcp-headers-helper.js +616 -0
- package/src/lib/mcp-host-recovery-runtime.js +131 -0
- package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
- package/src/lib/mcp-recovery-adjudication.js +443 -0
- package/src/lib/output-context.js +86 -0
- package/src/lib/packer/file-identity.js +170 -0
- package/src/lib/packer/index.js +3 -0
- package/src/lib/packer/native-update-state.js +277 -0
- package/src/lib/packer/pack-update-applier.js +1983 -136
- package/src/lib/packer/pack-update-checker.js +34 -0
- package/src/lib/packer/pack-update-downloader.js +875 -86
- package/src/lib/packer/pack-update-signature.js +86 -0
- package/src/lib/packer/pkg-config-generator.js +6 -0
- package/src/lib/paths.js +65 -10
- package/src/lib/plan-mode.js +831 -14
- package/src/lib/plan-persistence.js +540 -0
- package/src/lib/plugin-runtime/hooks.js +227 -69
- package/src/lib/plugin-runtime/plugin-options.js +189 -44
- package/src/lib/pr-automation-policy.js +146 -8
- package/src/lib/process-execution-broker/credential-agent.js +7 -0
- package/src/lib/process-execution-broker/index.js +491 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
- package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
- package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
- package/src/lib/project-instructions.js +165 -29
- package/src/lib/project-mcp-trust.js +67 -0
- package/src/lib/public-ide-capability-manifest.js +272 -0
- package/src/lib/public-ide-capability-validation.js +351 -0
- package/src/lib/secret-store.js +267 -43
- package/src/lib/secure-file-identity.js +433 -0
- package/src/lib/secure-fs.js +1089 -0
- package/src/lib/security-audit.js +58 -0
- package/src/lib/service-manager.js +34 -13
- package/src/lib/session-budget-runtime.js +1452 -0
- package/src/lib/session-host-snapshot.js +388 -0
- package/src/lib/session-message-provenance.js +429 -0
- package/src/lib/session-projection.js +804 -0
- package/src/lib/session-resource-budget.js +1495 -0
- package/src/lib/settings-hooks.cjs +138 -22
- package/src/lib/settings-loader.cjs +25 -2
- package/src/lib/side-effect-ledger-store.js +45 -23
- package/src/lib/skill-execution-identity.js +217 -0
- package/src/lib/skill-loader.js +299 -18
- package/src/lib/sub-agent-context.js +236 -28
- package/src/lib/sub-agent-registry.js +5 -0
- package/src/lib/subagent-contract.js +94 -0
- package/src/lib/todo-manager.js +185 -18
- package/src/lib/todo-persistence.js +417 -0
- package/src/lib/turn-binding-store.js +41 -23
- package/src/lib/turn-binding.js +160 -2
- package/src/lib/with-file-lock.js +165 -24
- package/src/lib/ws-chat-handler.js +21 -0
- package/src/program-base.js +18 -0
- package/src/repl/advisor-command.js +103 -0
- package/src/repl/agent-repl.js +2175 -146
- package/src/repl/btw-command.js +93 -10
- package/src/repl/clipboard-image.js +119 -0
- package/src/repl/config-summary.js +64 -108
- package/src/repl/prompt-editor.js +233 -0
- package/src/repl/prompt-interactions.js +344 -0
- package/src/repl/prompt-stash.js +354 -0
- package/src/repl/prompt-suggestions.js +312 -0
- package/src/repl/repl-keybindings.js +227 -0
- package/src/repl/session-recap.js +217 -0
- package/src/repl/slash-command-registry.js +22 -0
- package/src/repl/terminal-layout.js +88 -0
- package/src/runtime/agent-core.js +1630 -447
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
- package/src/runtime/diagnostics.js +32 -13
- package/src/runtime/headless-runner.js +310 -118
- package/src/runtime/headless-stream.js +356 -95
- package/src/runtime/mcp-config.js +148 -14
- package/src/runtime/policies/agent-policy.js +14 -0
- package/src/runtime/status-diagnostics-lite.js +137 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
- package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
- package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
- package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
|
@@ -0,0 +1,1476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cc checkpoint recovery` command surface.
|
|
3
|
+
*
|
|
4
|
+
* The command layer keeps read projections and mutation authority separate:
|
|
5
|
+
* saga evidence supplies the exact sequence/head, while the canonical
|
|
6
|
+
* workspace lock is inspected immediately before show/abort/resume/release to
|
|
7
|
+
* derive a live owner digest (or verified absence). The complete owner is
|
|
8
|
+
* never returned or printed.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { realpathSync } from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
CHECKPOINT_RESTORE_SAGA_PHASES,
|
|
15
|
+
CheckpointRestoreSagaStore,
|
|
16
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest,
|
|
17
|
+
} from "../lib/checkpoint-restore-saga.js";
|
|
18
|
+
import {
|
|
19
|
+
readVerifiedProjection,
|
|
20
|
+
withSessionAuthorityTransaction,
|
|
21
|
+
} from "../harness/jsonl-session-store.js";
|
|
22
|
+
import {
|
|
23
|
+
createCheckpointRestoreRecoveryReader,
|
|
24
|
+
MAX_CHECKPOINT_RESTORE_RECOVERY_LIST_LIMIT,
|
|
25
|
+
} from "../lib/checkpoint-restore-recovery.js";
|
|
26
|
+
import { CheckpointRestoreRecoveryController } from "../lib/checkpoint-restore-recovery-controller.js";
|
|
27
|
+
import {
|
|
28
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_ACTION,
|
|
29
|
+
CheckpointRestoreAlreadyCompletedController,
|
|
30
|
+
} from "../lib/checkpoint-restore-already-completed-controller.js";
|
|
31
|
+
import { createCheckpointRestoreSessionRecoveryReader } from "../lib/checkpoint-restore-session-recovery.js";
|
|
32
|
+
import {
|
|
33
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION,
|
|
34
|
+
CheckpointRestorePartialRollbackController,
|
|
35
|
+
} from "../lib/checkpoint-restore-partial-rollback-controller.js";
|
|
36
|
+
import { CheckpointRestoreWorkspaceTargetVerifier } from "../lib/checkpoint-restore-workspace-target-verifier.js";
|
|
37
|
+
import {
|
|
38
|
+
executeCheckpointRollback as executeGitCheckpointRollback,
|
|
39
|
+
prepareCheckpointRollback as prepareGitCheckpointRollback,
|
|
40
|
+
} from "../lib/checkpoint-store.js";
|
|
41
|
+
import {
|
|
42
|
+
executeCheckpointRollback as executeCopyCheckpointRollback,
|
|
43
|
+
prepareCheckpointRollback as prepareCopyCheckpointRollback,
|
|
44
|
+
} from "../lib/file-checkpoint.js";
|
|
45
|
+
import {
|
|
46
|
+
inspectWorkspaceLockOwnerSync,
|
|
47
|
+
withWorkspaceRecoveryLockSync,
|
|
48
|
+
} from "../lib/process-execution-broker/workspace-transaction.js";
|
|
49
|
+
|
|
50
|
+
export const CHECKPOINT_RESTORE_RECOVERY_COMMAND_PREVIEW_SCHEMA =
|
|
51
|
+
"chainlesschain.checkpoint-restore-recovery-command-preview";
|
|
52
|
+
export const CHECKPOINT_RESTORE_RECOVERY_COMMAND_RESULT_SCHEMA =
|
|
53
|
+
"chainlesschain.checkpoint-restore-recovery-command-result";
|
|
54
|
+
export const CHECKPOINT_RESTORE_RECOVERY_COMMAND_ERROR_SCHEMA =
|
|
55
|
+
"chainlesschain.checkpoint-restore-recovery-command-error";
|
|
56
|
+
export const CHECKPOINT_RESTORE_RECOVERY_COMMAND_VERSION = 1;
|
|
57
|
+
|
|
58
|
+
export const CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES = Object.freeze({
|
|
59
|
+
OK: 0,
|
|
60
|
+
FAILURE: 1,
|
|
61
|
+
INVALID_USAGE: 2,
|
|
62
|
+
NOT_EXECUTABLE: 3,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES = Object.freeze({
|
|
66
|
+
INVALID_ARGUMENT: "CHECKPOINT_RESTORE_RECOVERY_CLI_INVALID_ARGUMENT",
|
|
67
|
+
CONFIRMATION_REQUIRED:
|
|
68
|
+
"CHECKPOINT_RESTORE_RECOVERY_CLI_CONFIRMATION_REQUIRED",
|
|
69
|
+
FENCE_MISMATCH: "CHECKPOINT_RESTORE_RECOVERY_CLI_FENCE_MISMATCH",
|
|
70
|
+
ACTION_NOT_ELIGIBLE: "CHECKPOINT_RESTORE_RECOVERY_CLI_ACTION_NOT_ELIGIBLE",
|
|
71
|
+
INVALID_DEPENDENCY: "CHECKPOINT_RESTORE_RECOVERY_CLI_INVALID_DEPENDENCY",
|
|
72
|
+
WORKSPACE_UNAVAILABLE:
|
|
73
|
+
"CHECKPOINT_RESTORE_RECOVERY_CLI_WORKSPACE_UNAVAILABLE",
|
|
74
|
+
FAILED: "CHECKPOINT_RESTORE_RECOVERY_CLI_FAILED",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
78
|
+
const OPERATION_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/;
|
|
79
|
+
const ERROR_CODE_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
|
|
80
|
+
const PHASES = new Set(CHECKPOINT_RESTORE_SAGA_PHASES);
|
|
81
|
+
const CONTROLLER_ABORT_PHASES = new Set(["created", "locked"]);
|
|
82
|
+
const CONTROLLER_RESUME_PHASES = new Set([
|
|
83
|
+
"workspace_applied",
|
|
84
|
+
"session_committed",
|
|
85
|
+
]);
|
|
86
|
+
const CONTROLLER_ROLLBACK_PHASES = new Set([
|
|
87
|
+
"mutation_started",
|
|
88
|
+
"rollback_prepared",
|
|
89
|
+
"rollback_started",
|
|
90
|
+
"workspace_rolled_back",
|
|
91
|
+
"session_rollback_committed",
|
|
92
|
+
]);
|
|
93
|
+
const ROLLBACK_BINDING_PHASES = new Set([
|
|
94
|
+
"rollback_prepared",
|
|
95
|
+
"rollback_started",
|
|
96
|
+
"workspace_rolled_back",
|
|
97
|
+
"session_rollback_committed",
|
|
98
|
+
]);
|
|
99
|
+
const CHECKPOINT_RESTORE_PURPOSE = "checkpoint-restore";
|
|
100
|
+
const RECOVERY_REQUEST_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/;
|
|
101
|
+
const GIT_CHECKPOINT_IDENTITY_PATTERN = /^git:(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
102
|
+
const GIT_CHECKPOINT_NAMESPACE_PATTERN =
|
|
103
|
+
/^(?!\.)(?!.*\.\.)(?!.*\.lock$)(?!.*\.$)[A-Za-z0-9._-]{1,256}$/i;
|
|
104
|
+
|
|
105
|
+
const PUBLIC_ERROR_MESSAGES = Object.freeze({
|
|
106
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_INVALID_ARGUMENT:
|
|
107
|
+
"Recovery arguments are invalid; refresh the read-only preview and retry.",
|
|
108
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_CONFIRMATION_REQUIRED:
|
|
109
|
+
"Mutation requires an explicit --yes confirmation.",
|
|
110
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_FENCE_MISMATCH:
|
|
111
|
+
"Recovery authority changed or the supplied fence is stale; run recovery show again.",
|
|
112
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ACTION_NOT_ELIGIBLE:
|
|
113
|
+
"This recovery action is not executable from the verified current state.",
|
|
114
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_INVALID_DEPENDENCY:
|
|
115
|
+
"Recovery authority could not be projected safely.",
|
|
116
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_WORKSPACE_UNAVAILABLE:
|
|
117
|
+
"The recovery workspace does not exist or cannot be resolved safely.",
|
|
118
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_FAILED:
|
|
119
|
+
"Checkpoint restore recovery failed without changing the requested authority.",
|
|
120
|
+
CHECKPOINT_RESTORE_RECOVERY_INVALID_ARGUMENT:
|
|
121
|
+
"Recovery arguments are invalid; refresh the read-only preview and retry.",
|
|
122
|
+
CHECKPOINT_RESTORE_RECOVERY_SAGA_CONFLICT:
|
|
123
|
+
"The checkpoint restore saga changed; run recovery show again.",
|
|
124
|
+
CHECKPOINT_RESTORE_RECOVERY_ACTION_NOT_ALLOWED:
|
|
125
|
+
"The requested action is not allowed from the verified recovery phase.",
|
|
126
|
+
CHECKPOINT_RESTORE_RECOVERY_OWNER_CONFLICT:
|
|
127
|
+
"The live workspace recovery owner could not be matched exactly.",
|
|
128
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_INVALID_ARGUMENT:
|
|
129
|
+
"Recovery arguments are invalid; refresh the read-only preview and retry.",
|
|
130
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_SAGA_CONFLICT:
|
|
131
|
+
"The checkpoint restore saga changed; run recovery show again.",
|
|
132
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_ACTION_NOT_ALLOWED:
|
|
133
|
+
"Resume is limited to a verified timeline restore already completed in session authority.",
|
|
134
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_OWNER_CONFLICT:
|
|
135
|
+
"The live workspace recovery owner could not be matched exactly.",
|
|
136
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_SESSION_CONFLICT:
|
|
137
|
+
"The session transcript does not prove one exact completed restore settlement.",
|
|
138
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_WORKSPACE_CONFLICT:
|
|
139
|
+
"The workspace does not match the exact restored checkpoint target.",
|
|
140
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_ASYNC_UNSUPPORTED:
|
|
141
|
+
"Recovery verification must complete synchronously under workspace authority.",
|
|
142
|
+
CHECKPOINT_RESTORE_WORKSPACE_TARGET_INVALID_ARGUMENT:
|
|
143
|
+
"Workspace target verification authority is invalid.",
|
|
144
|
+
CHECKPOINT_RESTORE_WORKSPACE_TARGET_CONFLICT:
|
|
145
|
+
"The workspace does not match the exact restored checkpoint target.",
|
|
146
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_INVALID_ARGUMENT:
|
|
147
|
+
"Recovery arguments are invalid; refresh the read-only preview and retry.",
|
|
148
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_SAGA_CONFLICT:
|
|
149
|
+
"The checkpoint restore saga changed; run recovery show again.",
|
|
150
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION_NOT_ALLOWED:
|
|
151
|
+
"Partial rollback is not executable from the verified current state.",
|
|
152
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_OWNER_CONFLICT:
|
|
153
|
+
"The live workspace recovery owner could not be matched exactly.",
|
|
154
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_PLAN_INVALID:
|
|
155
|
+
"The exact workspace rollback plan could not be proven safely.",
|
|
156
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_RESULT_INVALID:
|
|
157
|
+
"Workspace rollback completion could not be proven; recovery authority was retained.",
|
|
158
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_SESSION_CONFLICT:
|
|
159
|
+
"The session transcript does not prove one exact rollback settlement.",
|
|
160
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ASYNC_UNSUPPORTED:
|
|
161
|
+
"Recovery verification must remain synchronous under workspace authority.",
|
|
162
|
+
CHECKPOINT_RESTORE_SAGA_NOT_FOUND:
|
|
163
|
+
"The requested checkpoint restore recovery operation was not found.",
|
|
164
|
+
CHECKPOINT_RESTORE_SAGA_CONFLICT:
|
|
165
|
+
"The checkpoint restore saga changed; run recovery show again.",
|
|
166
|
+
CHECKPOINT_RESTORE_SAGA_CORRUPT:
|
|
167
|
+
"Checkpoint restore recovery evidence is corrupt and was rejected.",
|
|
168
|
+
WORKSPACE_TRANSACTION_RECOVERY_REQUIRED:
|
|
169
|
+
"The workspace is owned by a different durable operation.",
|
|
170
|
+
WORKSPACE_TRANSACTION_LOCK_CORRUPT:
|
|
171
|
+
"The live workspace lock could not be verified safely.",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const INVALID_USAGE_PUBLIC_ERRORS = new Set([
|
|
175
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
176
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.CONFIRMATION_REQUIRED,
|
|
177
|
+
"CHECKPOINT_RESTORE_RECOVERY_INVALID_ARGUMENT",
|
|
178
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_INVALID_ARGUMENT",
|
|
179
|
+
"CHECKPOINT_RESTORE_WORKSPACE_TARGET_INVALID_ARGUMENT",
|
|
180
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_INVALID_ARGUMENT",
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
const NOT_EXECUTABLE_PUBLIC_ERRORS = new Set([
|
|
184
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.FENCE_MISMATCH,
|
|
185
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.ACTION_NOT_ELIGIBLE,
|
|
186
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.WORKSPACE_UNAVAILABLE,
|
|
187
|
+
"CHECKPOINT_RESTORE_RECOVERY_SAGA_CONFLICT",
|
|
188
|
+
"CHECKPOINT_RESTORE_RECOVERY_ACTION_NOT_ALLOWED",
|
|
189
|
+
"CHECKPOINT_RESTORE_RECOVERY_OWNER_CONFLICT",
|
|
190
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_SAGA_CONFLICT",
|
|
191
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_ACTION_NOT_ALLOWED",
|
|
192
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_OWNER_CONFLICT",
|
|
193
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_SESSION_CONFLICT",
|
|
194
|
+
"CHECKPOINT_RESTORE_ALREADY_COMPLETED_WORKSPACE_CONFLICT",
|
|
195
|
+
"CHECKPOINT_RESTORE_WORKSPACE_TARGET_CONFLICT",
|
|
196
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_SAGA_CONFLICT",
|
|
197
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION_NOT_ALLOWED",
|
|
198
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_OWNER_CONFLICT",
|
|
199
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_PLAN_INVALID",
|
|
200
|
+
"CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_SESSION_CONFLICT",
|
|
201
|
+
"CHECKPOINT_RESTORE_SAGA_NOT_FOUND",
|
|
202
|
+
"CHECKPOINT_RESTORE_SAGA_CONFLICT",
|
|
203
|
+
"WORKSPACE_TRANSACTION_RECOVERY_REQUIRED",
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
export class CheckpointRestoreRecoveryCliError extends Error {
|
|
207
|
+
constructor(code, message, exitCode) {
|
|
208
|
+
super(message);
|
|
209
|
+
this.name = "CheckpointRestoreRecoveryCliError";
|
|
210
|
+
this.code = code;
|
|
211
|
+
this.exitCode = exitCode;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function cliError(code, exitCode) {
|
|
216
|
+
return new CheckpointRestoreRecoveryCliError(
|
|
217
|
+
code,
|
|
218
|
+
PUBLIC_ERROR_MESSAGES[code] ||
|
|
219
|
+
PUBLIC_ERROR_MESSAGES.CHECKPOINT_RESTORE_RECOVERY_CLI_FAILED,
|
|
220
|
+
exitCode,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function isPlainObject(value) {
|
|
225
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
226
|
+
const prototype = Object.getPrototypeOf(value);
|
|
227
|
+
return prototype === Object.prototype || prototype === null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function publicErrorCode(error) {
|
|
231
|
+
let value = null;
|
|
232
|
+
try {
|
|
233
|
+
value = error?.code;
|
|
234
|
+
} catch {
|
|
235
|
+
return CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.FAILED;
|
|
236
|
+
}
|
|
237
|
+
return typeof value === "string" &&
|
|
238
|
+
ERROR_CODE_PATTERN.test(value) &&
|
|
239
|
+
Object.hasOwn(PUBLIC_ERROR_MESSAGES, value)
|
|
240
|
+
? value
|
|
241
|
+
: CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.FAILED;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function publicError(error) {
|
|
245
|
+
const code = publicErrorCode(error);
|
|
246
|
+
const message =
|
|
247
|
+
PUBLIC_ERROR_MESSAGES[code] ||
|
|
248
|
+
PUBLIC_ERROR_MESSAGES.CHECKPOINT_RESTORE_RECOVERY_CLI_FAILED;
|
|
249
|
+
return new CheckpointRestoreRecoveryCliError(
|
|
250
|
+
code,
|
|
251
|
+
message,
|
|
252
|
+
INVALID_USAGE_PUBLIC_ERRORS.has(code)
|
|
253
|
+
? CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE
|
|
254
|
+
: NOT_EXECUTABLE_PUBLIC_ERRORS.has(code)
|
|
255
|
+
? CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE
|
|
256
|
+
: CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function assertOperationId(operationId) {
|
|
261
|
+
if (
|
|
262
|
+
typeof operationId !== "string" ||
|
|
263
|
+
!OPERATION_ID_PATTERN.test(operationId)
|
|
264
|
+
) {
|
|
265
|
+
throw cliError(
|
|
266
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
267
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return operationId;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function parsePositiveInteger(value) {
|
|
274
|
+
if (
|
|
275
|
+
(typeof value !== "string" && !Number.isSafeInteger(value)) ||
|
|
276
|
+
(typeof value === "string" && !/^[1-9]\d*$/.test(value))
|
|
277
|
+
) {
|
|
278
|
+
throw cliError(
|
|
279
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
280
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const parsed = Number(value);
|
|
284
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
285
|
+
throw cliError(
|
|
286
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
287
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return parsed;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function parseHash(value, { optional = false } = {}) {
|
|
294
|
+
if (optional && (value === undefined || value === null || value === "")) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
if (typeof value !== "string" || !HASH_PATTERN.test(value)) {
|
|
298
|
+
throw cliError(
|
|
299
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
300
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return value;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function normalizeDependencies(dependencies = {}) {
|
|
307
|
+
if (!isPlainObject(dependencies)) {
|
|
308
|
+
throw cliError(
|
|
309
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
310
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return Object.freeze({
|
|
314
|
+
resolveWorkspaceRoot:
|
|
315
|
+
dependencies.resolveWorkspaceRoot || ((value) => path.resolve(value)),
|
|
316
|
+
canonicalizeWorkspaceRoot:
|
|
317
|
+
dependencies.canonicalizeWorkspaceRoot ||
|
|
318
|
+
((value) => realpathSync.native(value)),
|
|
319
|
+
createStore:
|
|
320
|
+
dependencies.createStore ||
|
|
321
|
+
((options) => new CheckpointRestoreSagaStore(options)),
|
|
322
|
+
createRecoveryReader:
|
|
323
|
+
dependencies.createRecoveryReader ||
|
|
324
|
+
((options) => createCheckpointRestoreRecoveryReader(options)),
|
|
325
|
+
createRecoveryController:
|
|
326
|
+
dependencies.createRecoveryController ||
|
|
327
|
+
((options) => new CheckpointRestoreRecoveryController(options)),
|
|
328
|
+
createSessionRecoveryReader:
|
|
329
|
+
dependencies.createSessionRecoveryReader ||
|
|
330
|
+
(() =>
|
|
331
|
+
createCheckpointRestoreSessionRecoveryReader({
|
|
332
|
+
readVerifiedProjection,
|
|
333
|
+
})),
|
|
334
|
+
createWorkspaceTargetVerifier:
|
|
335
|
+
dependencies.createWorkspaceTargetVerifier ||
|
|
336
|
+
(() => new CheckpointRestoreWorkspaceTargetVerifier()),
|
|
337
|
+
createAlreadyCompletedController:
|
|
338
|
+
dependencies.createAlreadyCompletedController ||
|
|
339
|
+
((options) => new CheckpointRestoreAlreadyCompletedController(options)),
|
|
340
|
+
createPartialRollbackController:
|
|
341
|
+
dependencies.createPartialRollbackController ||
|
|
342
|
+
((options) => new CheckpointRestorePartialRollbackController(options)),
|
|
343
|
+
prepareGitCheckpointRollback:
|
|
344
|
+
dependencies.prepareGitCheckpointRollback || prepareGitCheckpointRollback,
|
|
345
|
+
executeGitCheckpointRollback:
|
|
346
|
+
dependencies.executeGitCheckpointRollback || executeGitCheckpointRollback,
|
|
347
|
+
prepareCopyCheckpointRollback:
|
|
348
|
+
dependencies.prepareCopyCheckpointRollback ||
|
|
349
|
+
prepareCopyCheckpointRollback,
|
|
350
|
+
executeCopyCheckpointRollback:
|
|
351
|
+
dependencies.executeCopyCheckpointRollback ||
|
|
352
|
+
executeCopyCheckpointRollback,
|
|
353
|
+
withSessionAuthorityTransaction:
|
|
354
|
+
dependencies.withSessionAuthorityTransaction ||
|
|
355
|
+
withSessionAuthorityTransaction,
|
|
356
|
+
inspectWorkspaceLockOwnerSync:
|
|
357
|
+
dependencies.inspectWorkspaceLockOwnerSync ||
|
|
358
|
+
inspectWorkspaceLockOwnerSync,
|
|
359
|
+
withWorkspaceRecoveryLockSync:
|
|
360
|
+
dependencies.withWorkspaceRecoveryLockSync ||
|
|
361
|
+
withWorkspaceRecoveryLockSync,
|
|
362
|
+
computeWorkspaceLockOwnerDigest:
|
|
363
|
+
dependencies.computeWorkspaceLockOwnerDigest ||
|
|
364
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest,
|
|
365
|
+
workspaceLockOptions: Object.freeze({
|
|
366
|
+
...(dependencies.workspaceLockOptions || {}),
|
|
367
|
+
}),
|
|
368
|
+
writeStdout:
|
|
369
|
+
dependencies.writeStdout || ((value) => console.log(String(value))),
|
|
370
|
+
writeStderr:
|
|
371
|
+
dependencies.writeStderr || ((value) => console.error(String(value))),
|
|
372
|
+
setExitCode:
|
|
373
|
+
dependencies.setExitCode || ((value) => (process.exitCode = value)),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function invalidRollbackAdapterAuthority() {
|
|
378
|
+
return cliError(
|
|
379
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
380
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function isCanonicalGitCheckpointNamespace(value) {
|
|
385
|
+
return (
|
|
386
|
+
typeof value === "string" && GIT_CHECKPOINT_NAMESPACE_PATTERN.test(value)
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function createWorkspaceRollbackAdapters(runtime) {
|
|
391
|
+
const prepareWorkspaceRollback = (request) => {
|
|
392
|
+
const expected = request?.expected;
|
|
393
|
+
const original = expected?.originalCheckpoint;
|
|
394
|
+
const safety = expected?.safetyCheckpoint;
|
|
395
|
+
if (
|
|
396
|
+
!isPlainObject(request) ||
|
|
397
|
+
!isPlainObject(expected) ||
|
|
398
|
+
!isPlainObject(original) ||
|
|
399
|
+
!isPlainObject(safety) ||
|
|
400
|
+
typeof request.workspaceRoot !== "string"
|
|
401
|
+
) {
|
|
402
|
+
throw invalidRollbackAdapterAuthority();
|
|
403
|
+
}
|
|
404
|
+
const options = {
|
|
405
|
+
expectedOriginalIdentity: original.identity,
|
|
406
|
+
expectedSafetyIdentity: safety.identity,
|
|
407
|
+
expectedSafetyPlanIdentity: safety.planIdentity,
|
|
408
|
+
originalMutationTargetCount: expected.originalMutationTargetCount,
|
|
409
|
+
};
|
|
410
|
+
if (expected.engine === "git") {
|
|
411
|
+
if (!isCanonicalGitCheckpointNamespace(expected.checkpointNamespace)) {
|
|
412
|
+
throw invalidRollbackAdapterAuthority();
|
|
413
|
+
}
|
|
414
|
+
return runtime.prepareGitCheckpointRollback(
|
|
415
|
+
request.workspaceRoot,
|
|
416
|
+
original.id,
|
|
417
|
+
safety.id,
|
|
418
|
+
{ ...options, session: expected.checkpointNamespace },
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
if (expected.engine === "copy") {
|
|
422
|
+
if (expected.checkpointNamespace !== null) {
|
|
423
|
+
throw invalidRollbackAdapterAuthority();
|
|
424
|
+
}
|
|
425
|
+
return runtime.prepareCopyCheckpointRollback(
|
|
426
|
+
request.workspaceRoot,
|
|
427
|
+
original.id,
|
|
428
|
+
safety.id,
|
|
429
|
+
options,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
throw invalidRollbackAdapterAuthority();
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const executeWorkspaceRollback = (request) => {
|
|
436
|
+
if (
|
|
437
|
+
!isPlainObject(request) ||
|
|
438
|
+
typeof request.workspaceRoot !== "string" ||
|
|
439
|
+
!isPlainObject(request.plan)
|
|
440
|
+
) {
|
|
441
|
+
throw invalidRollbackAdapterAuthority();
|
|
442
|
+
}
|
|
443
|
+
const options = { workspaceLease: request.workspaceLease };
|
|
444
|
+
if (request.plan.engine === "git") {
|
|
445
|
+
return runtime.executeGitCheckpointRollback(
|
|
446
|
+
request.workspaceRoot,
|
|
447
|
+
request.plan,
|
|
448
|
+
options,
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (request.plan.engine === "copy") {
|
|
452
|
+
if (request.plan.checkpointNamespace !== null) {
|
|
453
|
+
throw invalidRollbackAdapterAuthority();
|
|
454
|
+
}
|
|
455
|
+
return runtime.executeCopyCheckpointRollback(
|
|
456
|
+
request.workspaceRoot,
|
|
457
|
+
request.plan,
|
|
458
|
+
options,
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
throw invalidRollbackAdapterAuthority();
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
return Object.freeze({
|
|
465
|
+
prepareWorkspaceRollback,
|
|
466
|
+
executeWorkspaceRollback,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function openContext(
|
|
471
|
+
runtime,
|
|
472
|
+
directory,
|
|
473
|
+
{
|
|
474
|
+
mutation = false,
|
|
475
|
+
alreadyCompletedResume = false,
|
|
476
|
+
partialRollback = false,
|
|
477
|
+
} = {},
|
|
478
|
+
) {
|
|
479
|
+
const resolvedWorkspaceRoot = runtime.resolveWorkspaceRoot(directory || ".");
|
|
480
|
+
if (
|
|
481
|
+
typeof resolvedWorkspaceRoot !== "string" ||
|
|
482
|
+
resolvedWorkspaceRoot.length === 0
|
|
483
|
+
) {
|
|
484
|
+
throw cliError(
|
|
485
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
486
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
let workspaceRoot;
|
|
490
|
+
try {
|
|
491
|
+
workspaceRoot = runtime.canonicalizeWorkspaceRoot(resolvedWorkspaceRoot);
|
|
492
|
+
} catch {
|
|
493
|
+
throw cliError(
|
|
494
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.WORKSPACE_UNAVAILABLE,
|
|
495
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) {
|
|
499
|
+
throw cliError(
|
|
500
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
501
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
const store = runtime.createStore({ workspaceRoot });
|
|
505
|
+
const reader = runtime.createRecoveryReader({ workspaceRoot, store });
|
|
506
|
+
if (
|
|
507
|
+
!reader ||
|
|
508
|
+
typeof reader.list !== "function" ||
|
|
509
|
+
typeof reader.show !== "function"
|
|
510
|
+
) {
|
|
511
|
+
throw cliError(
|
|
512
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
513
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
let controller = null;
|
|
517
|
+
if (mutation) {
|
|
518
|
+
controller = runtime.createRecoveryController({
|
|
519
|
+
workspaceRoot,
|
|
520
|
+
store,
|
|
521
|
+
inspectWorkspaceLockOwnerSync: runtime.inspectWorkspaceLockOwnerSync,
|
|
522
|
+
computeWorkspaceLockOwnerDigest: runtime.computeWorkspaceLockOwnerDigest,
|
|
523
|
+
workspaceLockOptions: runtime.workspaceLockOptions,
|
|
524
|
+
});
|
|
525
|
+
if (
|
|
526
|
+
!controller ||
|
|
527
|
+
typeof controller.abort !== "function" ||
|
|
528
|
+
typeof controller.release !== "function"
|
|
529
|
+
) {
|
|
530
|
+
throw cliError(
|
|
531
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
532
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
let alreadyCompletedController = null;
|
|
537
|
+
if (alreadyCompletedResume) {
|
|
538
|
+
const sessionRecoveryReader = runtime.createSessionRecoveryReader({
|
|
539
|
+
workspaceRoot,
|
|
540
|
+
});
|
|
541
|
+
const workspaceTargetVerifier = runtime.createWorkspaceTargetVerifier({
|
|
542
|
+
workspaceRoot,
|
|
543
|
+
});
|
|
544
|
+
if (
|
|
545
|
+
!sessionRecoveryReader ||
|
|
546
|
+
typeof sessionRecoveryReader.read !== "function" ||
|
|
547
|
+
!workspaceTargetVerifier ||
|
|
548
|
+
typeof workspaceTargetVerifier.verify !== "function"
|
|
549
|
+
) {
|
|
550
|
+
throw cliError(
|
|
551
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
552
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
alreadyCompletedController = runtime.createAlreadyCompletedController({
|
|
556
|
+
workspaceRoot,
|
|
557
|
+
store,
|
|
558
|
+
sessionRecoveryReader,
|
|
559
|
+
verifyWorkspaceTarget: (request) =>
|
|
560
|
+
workspaceTargetVerifier.verify(request),
|
|
561
|
+
inspectWorkspaceLockOwnerSync: runtime.inspectWorkspaceLockOwnerSync,
|
|
562
|
+
computeWorkspaceLockOwnerDigest: runtime.computeWorkspaceLockOwnerDigest,
|
|
563
|
+
workspaceLockOptions: runtime.workspaceLockOptions,
|
|
564
|
+
});
|
|
565
|
+
if (
|
|
566
|
+
!alreadyCompletedController ||
|
|
567
|
+
typeof alreadyCompletedController.resume !== "function"
|
|
568
|
+
) {
|
|
569
|
+
throw cliError(
|
|
570
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
571
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
let partialRollbackController = null;
|
|
576
|
+
if (partialRollback) {
|
|
577
|
+
const sessionRecoveryReader = runtime.createSessionRecoveryReader({
|
|
578
|
+
workspaceRoot,
|
|
579
|
+
});
|
|
580
|
+
if (
|
|
581
|
+
!sessionRecoveryReader ||
|
|
582
|
+
typeof sessionRecoveryReader.read !== "function"
|
|
583
|
+
) {
|
|
584
|
+
throw cliError(
|
|
585
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
586
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
const adapters = createWorkspaceRollbackAdapters(runtime);
|
|
590
|
+
partialRollbackController = runtime.createPartialRollbackController({
|
|
591
|
+
workspaceRoot,
|
|
592
|
+
store,
|
|
593
|
+
prepareWorkspaceRollback: adapters.prepareWorkspaceRollback,
|
|
594
|
+
executeWorkspaceRollback: adapters.executeWorkspaceRollback,
|
|
595
|
+
sessionRecoveryReader,
|
|
596
|
+
withSessionAuthorityTransaction: runtime.withSessionAuthorityTransaction,
|
|
597
|
+
inspectWorkspaceLockOwnerSync: runtime.inspectWorkspaceLockOwnerSync,
|
|
598
|
+
withWorkspaceRecoveryLockSync: runtime.withWorkspaceRecoveryLockSync,
|
|
599
|
+
computeWorkspaceLockOwnerDigest: runtime.computeWorkspaceLockOwnerDigest,
|
|
600
|
+
workspaceLockOptions: runtime.workspaceLockOptions,
|
|
601
|
+
});
|
|
602
|
+
if (
|
|
603
|
+
!partialRollbackController ||
|
|
604
|
+
typeof partialRollbackController.rollback !== "function" ||
|
|
605
|
+
partialRollbackController.rollback.constructor?.name === "AsyncFunction"
|
|
606
|
+
) {
|
|
607
|
+
throw cliError(
|
|
608
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
609
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return Object.freeze({
|
|
614
|
+
workspaceRoot,
|
|
615
|
+
store,
|
|
616
|
+
reader,
|
|
617
|
+
controller,
|
|
618
|
+
alreadyCompletedController,
|
|
619
|
+
partialRollbackController,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function previewLiveAuthority(runtime, { workspaceRoot, operationId }) {
|
|
624
|
+
const owner = runtime.inspectWorkspaceLockOwnerSync({
|
|
625
|
+
...runtime.workspaceLockOptions,
|
|
626
|
+
workspaceRoot,
|
|
627
|
+
operationId,
|
|
628
|
+
purpose: CHECKPOINT_RESTORE_PURPOSE,
|
|
629
|
+
});
|
|
630
|
+
if (owner === null) {
|
|
631
|
+
return Object.freeze({
|
|
632
|
+
state: "absent",
|
|
633
|
+
verified: true,
|
|
634
|
+
expectedOwnerDigest: null,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
const digest = runtime.computeWorkspaceLockOwnerDigest(owner);
|
|
638
|
+
if (typeof digest !== "string" || !HASH_PATTERN.test(digest)) {
|
|
639
|
+
throw cliError(
|
|
640
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
641
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
return Object.freeze({
|
|
645
|
+
state: "retained",
|
|
646
|
+
verified: true,
|
|
647
|
+
expectedOwnerDigest: digest,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Read the current canonical workspace owner and immediately discard its full
|
|
653
|
+
* value after calculating the digest. The returned object is safe to print.
|
|
654
|
+
*/
|
|
655
|
+
export function previewCheckpointRestoreRecoveryAuthority(
|
|
656
|
+
input,
|
|
657
|
+
dependencies = {},
|
|
658
|
+
) {
|
|
659
|
+
if (!isPlainObject(input) || typeof input.workspaceRoot !== "string") {
|
|
660
|
+
throw cliError(
|
|
661
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
662
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const runtime = normalizeDependencies(dependencies);
|
|
666
|
+
return previewLiveAuthority(runtime, {
|
|
667
|
+
workspaceRoot: input.workspaceRoot,
|
|
668
|
+
operationId: assertOperationId(input.operationId),
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function commandAction({ candidate, eligible, blockers, prerequisites }) {
|
|
673
|
+
return Object.freeze({
|
|
674
|
+
candidate: candidate === true,
|
|
675
|
+
eligible: eligible === true,
|
|
676
|
+
blockers: Object.freeze([...blockers]),
|
|
677
|
+
prerequisites: Object.freeze([...prerequisites]),
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function projectedText(value, maximum = 1_024) {
|
|
682
|
+
return (
|
|
683
|
+
typeof value === "string" &&
|
|
684
|
+
value.length > 0 &&
|
|
685
|
+
value.length <= maximum &&
|
|
686
|
+
value.trim() === value &&
|
|
687
|
+
!value.includes("\0")
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function projectedOriginalMutationTargetCount(projection) {
|
|
692
|
+
const rollback = projection?.rollback;
|
|
693
|
+
const rollbackPhase = projection?.rollback?.phase ?? null;
|
|
694
|
+
const rollbackCycleBound =
|
|
695
|
+
ROLLBACK_BINDING_PHASES.has(projection?.basePhase) ||
|
|
696
|
+
ROLLBACK_BINDING_PHASES.has(rollbackPhase);
|
|
697
|
+
if (rollbackCycleBound) {
|
|
698
|
+
return rollback?.originalMutationTargetCount;
|
|
699
|
+
}
|
|
700
|
+
const rollbackBindingPresent = [
|
|
701
|
+
rollback?.rollbackPrestateDigest,
|
|
702
|
+
rollback?.rollbackPlanIdentity,
|
|
703
|
+
rollback?.originalMutationTargetCount,
|
|
704
|
+
rollback?.targetCount,
|
|
705
|
+
].some((value) => value !== null && value !== undefined);
|
|
706
|
+
if (
|
|
707
|
+
projection?.basePhase === "mutation_started" &&
|
|
708
|
+
(rollbackPhase === null || rollbackPhase === "recovery_started") &&
|
|
709
|
+
!rollbackBindingPresent
|
|
710
|
+
) {
|
|
711
|
+
return projection?.progress?.targetCount;
|
|
712
|
+
}
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function projectedRollbackAuthority(projection) {
|
|
717
|
+
const restore = projection?.restore;
|
|
718
|
+
const safety = projection?.safety;
|
|
719
|
+
const kind = restore?.kind;
|
|
720
|
+
const identityPattern =
|
|
721
|
+
kind === "git" ? GIT_CHECKPOINT_IDENTITY_PATTERN : HASH_PATTERN;
|
|
722
|
+
const originalMutationTargetCount =
|
|
723
|
+
projectedOriginalMutationTargetCount(projection);
|
|
724
|
+
const engineAuthority =
|
|
725
|
+
["git", "copy"].includes(kind) &&
|
|
726
|
+
projectedText(restore?.checkpointId, 256) &&
|
|
727
|
+
identityPattern.test(String(restore?.checkpointIdentity || "")) &&
|
|
728
|
+
safety?.coverage === "full" &&
|
|
729
|
+
safety?.complete === true &&
|
|
730
|
+
projectedText(safety?.checkpointId, 256) &&
|
|
731
|
+
identityPattern.test(String(safety?.checkpointIdentity || "")) &&
|
|
732
|
+
HASH_PATTERN.test(String(safety?.planIdentity || "")) &&
|
|
733
|
+
Number.isSafeInteger(originalMutationTargetCount) &&
|
|
734
|
+
originalMutationTargetCount > 0 &&
|
|
735
|
+
(kind === "git"
|
|
736
|
+
? isCanonicalGitCheckpointNamespace(restore?.checkpointNamespace)
|
|
737
|
+
: restore?.checkpointNamespace === null);
|
|
738
|
+
if (!engineAuthority) return false;
|
|
739
|
+
if (restore.surface === "direct") {
|
|
740
|
+
return (
|
|
741
|
+
restore.intentAuthority === "operation" &&
|
|
742
|
+
restore.sessionId === null &&
|
|
743
|
+
restore.timelineEntryId === null
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
return (
|
|
747
|
+
restore.surface === "timeline" &&
|
|
748
|
+
restore.intentAuthority === "session" &&
|
|
749
|
+
projectedText(restore.sessionId, 256) &&
|
|
750
|
+
projectedText(restore.timelineEntryId, 256)
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function deriveCommandActions(projection, authority) {
|
|
755
|
+
const clean = projection?.integrity?.clean === true;
|
|
756
|
+
const pending = projection?.pending === true;
|
|
757
|
+
const terminal = projection?.terminal === true;
|
|
758
|
+
const basePhase = projection?.basePhase;
|
|
759
|
+
const abortCandidate =
|
|
760
|
+
projection?.actionEligibility?.abort?.candidate === true;
|
|
761
|
+
const abortSupported = CONTROLLER_ABORT_PHASES.has(basePhase);
|
|
762
|
+
const lockFreeCreated =
|
|
763
|
+
basePhase === "created" &&
|
|
764
|
+
projection?.phase === "created" &&
|
|
765
|
+
authority.state === "absent";
|
|
766
|
+
const retainedTakeover = authority.state === "retained";
|
|
767
|
+
const abortEligible =
|
|
768
|
+
pending &&
|
|
769
|
+
clean &&
|
|
770
|
+
abortCandidate &&
|
|
771
|
+
abortSupported &&
|
|
772
|
+
(lockFreeCreated || retainedTakeover);
|
|
773
|
+
const abortBlockers = [];
|
|
774
|
+
if (!pending) abortBlockers.push("saga_is_terminal");
|
|
775
|
+
if (!clean) abortBlockers.push("saga_integrity_not_clean");
|
|
776
|
+
if (!abortCandidate) abortBlockers.push("abort_not_a_safe_candidate");
|
|
777
|
+
if (abortCandidate && !abortSupported) {
|
|
778
|
+
abortBlockers.push("controller_phase_not_supported");
|
|
779
|
+
}
|
|
780
|
+
if (
|
|
781
|
+
abortCandidate &&
|
|
782
|
+
abortSupported &&
|
|
783
|
+
basePhase === "locked" &&
|
|
784
|
+
authority.state !== "retained"
|
|
785
|
+
) {
|
|
786
|
+
abortBlockers.push("retained_workspace_owner_required");
|
|
787
|
+
}
|
|
788
|
+
if (
|
|
789
|
+
abortCandidate &&
|
|
790
|
+
abortSupported &&
|
|
791
|
+
basePhase === "created" &&
|
|
792
|
+
projection?.phase !== "created" &&
|
|
793
|
+
authority.state === "absent"
|
|
794
|
+
) {
|
|
795
|
+
abortBlockers.push("retained_workspace_owner_required_after_recovery");
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const releaseCandidate = terminal && clean;
|
|
799
|
+
const releaseEligible = releaseCandidate && authority.verified === true;
|
|
800
|
+
const releaseBlockers = [];
|
|
801
|
+
if (!terminal) releaseBlockers.push("saga_is_not_terminal");
|
|
802
|
+
if (!clean) releaseBlockers.push("saga_integrity_not_clean");
|
|
803
|
+
|
|
804
|
+
const resumeCandidate =
|
|
805
|
+
projection?.actionEligibility?.resume?.candidate === true;
|
|
806
|
+
const resumePhaseSupported = CONTROLLER_RESUME_PHASES.has(basePhase);
|
|
807
|
+
const restoreKind = projection?.restore?.kind;
|
|
808
|
+
const timelineAuthority =
|
|
809
|
+
projection?.restore?.surface === "timeline" &&
|
|
810
|
+
projection?.restore?.intentAuthority === "session" &&
|
|
811
|
+
typeof projection?.restore?.sessionId === "string" &&
|
|
812
|
+
typeof projection?.restore?.timelineEntryId === "string" &&
|
|
813
|
+
["git", "copy"].includes(restoreKind) &&
|
|
814
|
+
typeof projection?.restore?.checkpointId === "string" &&
|
|
815
|
+
typeof projection?.restore?.checkpointIdentity === "string" &&
|
|
816
|
+
(restoreKind !== "git" ||
|
|
817
|
+
typeof projection?.restore?.checkpointNamespace === "string");
|
|
818
|
+
const resumeEligible =
|
|
819
|
+
pending &&
|
|
820
|
+
clean &&
|
|
821
|
+
resumeCandidate &&
|
|
822
|
+
resumePhaseSupported &&
|
|
823
|
+
timelineAuthority &&
|
|
824
|
+
retainedTakeover;
|
|
825
|
+
const resumeBlockers = [];
|
|
826
|
+
if (!pending) resumeBlockers.push("saga_is_terminal");
|
|
827
|
+
if (!clean) resumeBlockers.push("saga_integrity_not_clean");
|
|
828
|
+
if (!resumeCandidate) resumeBlockers.push("resume_not_a_safe_candidate");
|
|
829
|
+
if (resumeCandidate && !resumePhaseSupported) {
|
|
830
|
+
resumeBlockers.push("controller_phase_not_supported");
|
|
831
|
+
}
|
|
832
|
+
if (resumeCandidate && !timelineAuthority) {
|
|
833
|
+
resumeBlockers.push("verified_timeline_session_authority_required");
|
|
834
|
+
}
|
|
835
|
+
if (resumeCandidate && authority.state !== "retained") {
|
|
836
|
+
resumeBlockers.push("retained_workspace_owner_required");
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const rollbackCandidate =
|
|
840
|
+
projection?.actionEligibility?.rollback?.candidate === true;
|
|
841
|
+
const rollbackPhaseSupported = CONTROLLER_ROLLBACK_PHASES.has(basePhase);
|
|
842
|
+
const rollbackAuthority = projectedRollbackAuthority(projection);
|
|
843
|
+
const rollbackEligible =
|
|
844
|
+
pending &&
|
|
845
|
+
clean &&
|
|
846
|
+
rollbackCandidate &&
|
|
847
|
+
rollbackPhaseSupported &&
|
|
848
|
+
rollbackAuthority &&
|
|
849
|
+
retainedTakeover;
|
|
850
|
+
const rollbackBlockers = [];
|
|
851
|
+
if (!rollbackCandidate) {
|
|
852
|
+
rollbackBlockers.push(
|
|
853
|
+
...(Array.isArray(projection?.actionEligibility?.rollback?.blockers)
|
|
854
|
+
? projection.actionEligibility.rollback.blockers
|
|
855
|
+
: ["rollback_not_a_safe_candidate"]),
|
|
856
|
+
);
|
|
857
|
+
} else {
|
|
858
|
+
if (!pending) rollbackBlockers.push("saga_is_terminal");
|
|
859
|
+
if (!clean) rollbackBlockers.push("saga_integrity_not_clean");
|
|
860
|
+
if (!rollbackPhaseSupported) {
|
|
861
|
+
rollbackBlockers.push("controller_phase_not_supported");
|
|
862
|
+
}
|
|
863
|
+
if (!rollbackAuthority) {
|
|
864
|
+
rollbackBlockers.push("verified_rollback_authority_required");
|
|
865
|
+
}
|
|
866
|
+
if (!retainedTakeover) {
|
|
867
|
+
rollbackBlockers.push("retained_workspace_owner_required");
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return Object.freeze({
|
|
872
|
+
abort: commandAction({
|
|
873
|
+
candidate: abortCandidate,
|
|
874
|
+
eligible: abortEligible,
|
|
875
|
+
blockers: abortBlockers,
|
|
876
|
+
prerequisites: abortCandidate
|
|
877
|
+
? ["exact_mutation_fence", "controller_compare_and_swap"]
|
|
878
|
+
: [],
|
|
879
|
+
}),
|
|
880
|
+
resume: commandAction({
|
|
881
|
+
candidate: resumeCandidate,
|
|
882
|
+
eligible: resumeEligible,
|
|
883
|
+
blockers: resumeBlockers,
|
|
884
|
+
prerequisites: resumeCandidate
|
|
885
|
+
? [
|
|
886
|
+
"exact_mutation_fence",
|
|
887
|
+
"verified_session_already_completed",
|
|
888
|
+
"verified_workspace_target_state",
|
|
889
|
+
"controller_compare_and_swap",
|
|
890
|
+
]
|
|
891
|
+
: [],
|
|
892
|
+
}),
|
|
893
|
+
rollback: commandAction({
|
|
894
|
+
candidate: rollbackCandidate,
|
|
895
|
+
eligible: rollbackEligible,
|
|
896
|
+
blockers: rollbackBlockers,
|
|
897
|
+
prerequisites: rollbackCandidate
|
|
898
|
+
? [
|
|
899
|
+
"exact_mutation_fence",
|
|
900
|
+
"verified_full_safety_checkpoint",
|
|
901
|
+
"verified_workspace_rollback_state",
|
|
902
|
+
...(projection?.restore?.surface === "timeline"
|
|
903
|
+
? ["verified_session_rollback_state"]
|
|
904
|
+
: []),
|
|
905
|
+
"controller_compare_and_swap",
|
|
906
|
+
]
|
|
907
|
+
: [],
|
|
908
|
+
}),
|
|
909
|
+
release: commandAction({
|
|
910
|
+
candidate: releaseCandidate,
|
|
911
|
+
eligible: releaseEligible,
|
|
912
|
+
blockers: releaseBlockers,
|
|
913
|
+
prerequisites: releaseCandidate
|
|
914
|
+
? ["exact_mutation_fence", "controller_compare_and_swap"]
|
|
915
|
+
: [],
|
|
916
|
+
}),
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function commandPreview(runtime, context, operationId) {
|
|
921
|
+
const projection = context.reader.show(operationId);
|
|
922
|
+
const expectedSeq = projection?.fence?.expectedSeq;
|
|
923
|
+
const expectedHash = projection?.fence?.expectedHash;
|
|
924
|
+
if (
|
|
925
|
+
!Number.isSafeInteger(expectedSeq) ||
|
|
926
|
+
expectedSeq < 1 ||
|
|
927
|
+
typeof expectedHash !== "string" ||
|
|
928
|
+
!HASH_PATTERN.test(expectedHash)
|
|
929
|
+
) {
|
|
930
|
+
throw cliError(
|
|
931
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
932
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
const authority = previewLiveAuthority(runtime, {
|
|
936
|
+
workspaceRoot: context.workspaceRoot,
|
|
937
|
+
operationId,
|
|
938
|
+
});
|
|
939
|
+
const mutationFence = Object.freeze({
|
|
940
|
+
expectedSeq,
|
|
941
|
+
expectedHash,
|
|
942
|
+
expectedOwnerDigest: authority.expectedOwnerDigest,
|
|
943
|
+
});
|
|
944
|
+
return Object.freeze({
|
|
945
|
+
schema: CHECKPOINT_RESTORE_RECOVERY_COMMAND_PREVIEW_SCHEMA,
|
|
946
|
+
version: CHECKPOINT_RESTORE_RECOVERY_COMMAND_VERSION,
|
|
947
|
+
recovery: projection,
|
|
948
|
+
liveAuthority: Object.freeze({
|
|
949
|
+
state: authority.state,
|
|
950
|
+
verified: authority.verified,
|
|
951
|
+
}),
|
|
952
|
+
mutationFence,
|
|
953
|
+
actions: deriveCommandActions(projection, authority),
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function mutationFence(options, preview) {
|
|
958
|
+
const supplied = Object.freeze({
|
|
959
|
+
expectedSeq: parsePositiveInteger(options.expectedSeq),
|
|
960
|
+
expectedHash: parseHash(options.expectedHeadHash),
|
|
961
|
+
expectedOwnerDigest: parseHash(options.expectedOwnerDigest, {
|
|
962
|
+
optional: true,
|
|
963
|
+
}),
|
|
964
|
+
});
|
|
965
|
+
if (
|
|
966
|
+
supplied.expectedSeq !== preview.mutationFence.expectedSeq ||
|
|
967
|
+
supplied.expectedHash !== preview.mutationFence.expectedHash ||
|
|
968
|
+
supplied.expectedOwnerDigest !== preview.mutationFence.expectedOwnerDigest
|
|
969
|
+
) {
|
|
970
|
+
throw cliError(
|
|
971
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.FENCE_MISMATCH,
|
|
972
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
return supplied;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function assertSynchronousMutationResult(value) {
|
|
979
|
+
if (
|
|
980
|
+
value === null ||
|
|
981
|
+
(typeof value !== "object" && typeof value !== "function")
|
|
982
|
+
) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
let then;
|
|
986
|
+
try {
|
|
987
|
+
then = value.then;
|
|
988
|
+
} catch {
|
|
989
|
+
throw cliError(
|
|
990
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
991
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
if (typeof then === "function") {
|
|
995
|
+
throw cliError(
|
|
996
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
997
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function safeMutationResult(
|
|
1003
|
+
result,
|
|
1004
|
+
actionName,
|
|
1005
|
+
operationId,
|
|
1006
|
+
{ restoreSurface = null } = {},
|
|
1007
|
+
) {
|
|
1008
|
+
const alreadyCompletedResume =
|
|
1009
|
+
actionName === CHECKPOINT_RESTORE_ALREADY_COMPLETED_ACTION;
|
|
1010
|
+
const partialRollback =
|
|
1011
|
+
actionName === CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION;
|
|
1012
|
+
assertSynchronousMutationResult(result);
|
|
1013
|
+
const rollbackSessionDigestValid =
|
|
1014
|
+
!partialRollback ||
|
|
1015
|
+
(restoreSurface === "timeline"
|
|
1016
|
+
? HASH_PATTERN.test(String(result?.sessionRollbackCommitDigest || ""))
|
|
1017
|
+
: restoreSurface === "direct" &&
|
|
1018
|
+
result?.sessionRollbackCommitDigest === null);
|
|
1019
|
+
if (
|
|
1020
|
+
!isPlainObject(result) ||
|
|
1021
|
+
result.ok !== true ||
|
|
1022
|
+
result.action !== actionName ||
|
|
1023
|
+
result.operationId !== operationId ||
|
|
1024
|
+
!PHASES.has(result.phase) ||
|
|
1025
|
+
!Number.isSafeInteger(result.seq) ||
|
|
1026
|
+
result.seq < 1 ||
|
|
1027
|
+
typeof result.headHash !== "string" ||
|
|
1028
|
+
!HASH_PATTERN.test(result.headHash) ||
|
|
1029
|
+
(alreadyCompletedResume &&
|
|
1030
|
+
(!HASH_PATTERN.test(String(result.sessionCommitDigest || "")) ||
|
|
1031
|
+
!HASH_PATTERN.test(String(result.resultDigest || "")))) ||
|
|
1032
|
+
(partialRollback &&
|
|
1033
|
+
(result.phase !== "rolled_back" ||
|
|
1034
|
+
!RECOVERY_REQUEST_ID_PATTERN.test(
|
|
1035
|
+
String(result.recoveryRequestId || ""),
|
|
1036
|
+
) ||
|
|
1037
|
+
!Number.isSafeInteger(result.rolledBackCount) ||
|
|
1038
|
+
result.rolledBackCount < 0 ||
|
|
1039
|
+
!HASH_PATTERN.test(String(result.rollbackStateDigest || "")) ||
|
|
1040
|
+
!HASH_PATTERN.test(String(result.resultDigest || "")) ||
|
|
1041
|
+
!rollbackSessionDigestValid))
|
|
1042
|
+
) {
|
|
1043
|
+
throw cliError(
|
|
1044
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_DEPENDENCY,
|
|
1045
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.FAILURE,
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
return Object.freeze({
|
|
1049
|
+
schema: CHECKPOINT_RESTORE_RECOVERY_COMMAND_RESULT_SCHEMA,
|
|
1050
|
+
version: CHECKPOINT_RESTORE_RECOVERY_COMMAND_VERSION,
|
|
1051
|
+
ok: true,
|
|
1052
|
+
action: alreadyCompletedResume
|
|
1053
|
+
? "resume"
|
|
1054
|
+
: partialRollback
|
|
1055
|
+
? "rollback"
|
|
1056
|
+
: actionName,
|
|
1057
|
+
...(alreadyCompletedResume || partialRollback
|
|
1058
|
+
? { recoveryAction: actionName }
|
|
1059
|
+
: {}),
|
|
1060
|
+
operationId,
|
|
1061
|
+
phase: result.phase,
|
|
1062
|
+
seq: result.seq,
|
|
1063
|
+
headHash: result.headHash,
|
|
1064
|
+
archived: result.archived === true,
|
|
1065
|
+
alreadyArchived: result.alreadyArchived === true,
|
|
1066
|
+
reconciledFromError: result.reconciledFromError === true,
|
|
1067
|
+
...(alreadyCompletedResume
|
|
1068
|
+
? {
|
|
1069
|
+
sessionCommitDigest: result.sessionCommitDigest,
|
|
1070
|
+
resultDigest: result.resultDigest,
|
|
1071
|
+
}
|
|
1072
|
+
: {}),
|
|
1073
|
+
...(partialRollback
|
|
1074
|
+
? {
|
|
1075
|
+
recoveryRequestId: result.recoveryRequestId,
|
|
1076
|
+
rolledBackCount: result.rolledBackCount,
|
|
1077
|
+
rollbackStateDigest: result.rollbackStateDigest,
|
|
1078
|
+
resultDigest: result.resultDigest,
|
|
1079
|
+
sessionRollbackCommitDigest: result.sessionRollbackCommitDigest,
|
|
1080
|
+
}
|
|
1081
|
+
: {}),
|
|
1082
|
+
warning: result.warning
|
|
1083
|
+
? Object.freeze({
|
|
1084
|
+
code: "CHECKPOINT_RESTORE_SAGA_ARCHIVE_PENDING",
|
|
1085
|
+
})
|
|
1086
|
+
: null,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
function actionLabel(value) {
|
|
1091
|
+
if (value?.eligible === true) return "eligible";
|
|
1092
|
+
if (value?.candidate === true) {
|
|
1093
|
+
return `candidate only (not executable: ${(value.blockers || []).join(", ") || "verification required"})`;
|
|
1094
|
+
}
|
|
1095
|
+
return `unavailable (${(value?.blockers || []).join(", ") || "not applicable"})`;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function humanList(result) {
|
|
1099
|
+
const lines = ["Checkpoint restore recovery operations"];
|
|
1100
|
+
if (!Array.isArray(result.items) || result.items.length === 0) {
|
|
1101
|
+
lines.push(" (no verified pending operations in this page)");
|
|
1102
|
+
}
|
|
1103
|
+
for (const item of result.items || []) {
|
|
1104
|
+
const requestId = item.rollback?.recoveryRequestId || "(none)";
|
|
1105
|
+
lines.push(
|
|
1106
|
+
` ${item.operationId} status=${item.status} phase=${item.phase} base=${item.basePhase || "unknown"} request=${requestId}`,
|
|
1107
|
+
);
|
|
1108
|
+
lines.push(
|
|
1109
|
+
` fence: seq=${item.fence?.expectedSeq ?? "unknown"} head=${item.fence?.expectedHash || "unknown"}`,
|
|
1110
|
+
);
|
|
1111
|
+
lines.push(
|
|
1112
|
+
` abort=${actionLabel(item.actionEligibility?.abort)}; ` +
|
|
1113
|
+
`resume=${actionLabel(item.actionEligibility?.resume)}; ` +
|
|
1114
|
+
`rollback=${actionLabel(item.actionEligibility?.rollback)}`,
|
|
1115
|
+
);
|
|
1116
|
+
lines.push(" run recovery show for the live mutation fence");
|
|
1117
|
+
}
|
|
1118
|
+
for (const diagnostic of result.diagnostics || []) {
|
|
1119
|
+
lines.push(
|
|
1120
|
+
` diagnostic ${diagnostic.operationId}: ${diagnostic.status} (${diagnostic.code || "no-code"})`,
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
if (result.page?.truncated) {
|
|
1124
|
+
lines.push(
|
|
1125
|
+
` next cursor: ${result.page.nextCursor === "" ? "(retry current cursor)" : result.page.nextCursor}`,
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
return lines.join("\n");
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function humanPreview(preview) {
|
|
1132
|
+
const recovery = preview.recovery;
|
|
1133
|
+
const ownerFence = preview.mutationFence.expectedOwnerDigest || "(absent)";
|
|
1134
|
+
return [
|
|
1135
|
+
`Checkpoint restore recovery: ${recovery.operationId}`,
|
|
1136
|
+
` status: ${recovery.status}`,
|
|
1137
|
+
` phase: ${recovery.phase} (base ${recovery.basePhase || "unknown"})`,
|
|
1138
|
+
` rollback request: ${recovery.rollback?.recoveryRequestId || "(none)"}`,
|
|
1139
|
+
` saga fence: seq=${preview.mutationFence.expectedSeq} head=${preview.mutationFence.expectedHash}`,
|
|
1140
|
+
` live owner: ${preview.liveAuthority.state}`,
|
|
1141
|
+
` expected owner digest: ${ownerFence}`,
|
|
1142
|
+
` abort: ${actionLabel(preview.actions.abort)}`,
|
|
1143
|
+
` resume: ${actionLabel(preview.actions.resume)}`,
|
|
1144
|
+
` rollback: ${actionLabel(preview.actions.rollback)}`,
|
|
1145
|
+
` release: ${actionLabel(preview.actions.release)}`,
|
|
1146
|
+
` recovery reason recorded: ${recovery.recovery?.reasonPresent ? "yes" : "no"}`,
|
|
1147
|
+
].join("\n");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function humanMutation(result) {
|
|
1151
|
+
const lines = [
|
|
1152
|
+
`Checkpoint restore ${result.action} completed: ${result.operationId}`,
|
|
1153
|
+
` phase: ${result.phase}`,
|
|
1154
|
+
` seq: ${result.seq}`,
|
|
1155
|
+
` head: ${result.headHash}`,
|
|
1156
|
+
` archived: ${result.archived ? "yes" : "no"}`,
|
|
1157
|
+
];
|
|
1158
|
+
if (result.recoveryAction === CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION) {
|
|
1159
|
+
lines.push(
|
|
1160
|
+
` rollback request: ${result.recoveryRequestId}`,
|
|
1161
|
+
` paths rolled back: ${result.rolledBackCount}`,
|
|
1162
|
+
` rollback state: ${result.rollbackStateDigest}`,
|
|
1163
|
+
` result: ${result.resultDigest}`,
|
|
1164
|
+
);
|
|
1165
|
+
if (result.sessionRollbackCommitDigest) {
|
|
1166
|
+
lines.push(
|
|
1167
|
+
` session rollback commit: ${result.sessionRollbackCommitDigest}`,
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (result.warning) lines.push(` warning: ${result.warning.code}`);
|
|
1172
|
+
return lines.join("\n");
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function emitSuccess(runtime, options, value, humanFormatter) {
|
|
1176
|
+
runtime.writeStdout(
|
|
1177
|
+
options?.json ? JSON.stringify(value, null, 2) : humanFormatter(value),
|
|
1178
|
+
);
|
|
1179
|
+
runtime.setExitCode(CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.OK);
|
|
1180
|
+
return value;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
function emitFailure(runtime, options, error) {
|
|
1184
|
+
const safe = publicError(error);
|
|
1185
|
+
const payload = Object.freeze({
|
|
1186
|
+
schema: CHECKPOINT_RESTORE_RECOVERY_COMMAND_ERROR_SCHEMA,
|
|
1187
|
+
version: CHECKPOINT_RESTORE_RECOVERY_COMMAND_VERSION,
|
|
1188
|
+
ok: false,
|
|
1189
|
+
error: Object.freeze({ code: safe.code, message: safe.message }),
|
|
1190
|
+
exitCode: safe.exitCode,
|
|
1191
|
+
});
|
|
1192
|
+
runtime.writeStderr(
|
|
1193
|
+
options?.json
|
|
1194
|
+
? JSON.stringify(payload, null, 2)
|
|
1195
|
+
: `${safe.code}: ${safe.message}`,
|
|
1196
|
+
);
|
|
1197
|
+
runtime.setExitCode(safe.exitCode);
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/** Build dependency-injected command handlers without registering Commander. */
|
|
1202
|
+
export function createCheckpointRestoreRecoveryCommandHandlers(
|
|
1203
|
+
dependencies = {},
|
|
1204
|
+
) {
|
|
1205
|
+
const runtime = normalizeDependencies(dependencies);
|
|
1206
|
+
|
|
1207
|
+
const run = (options, callback) => {
|
|
1208
|
+
try {
|
|
1209
|
+
return callback();
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
return emitFailure(runtime, options, error);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
return Object.freeze({
|
|
1216
|
+
list(options = {}) {
|
|
1217
|
+
return run(options, () => {
|
|
1218
|
+
const context = openContext(runtime, options.dir);
|
|
1219
|
+
const limit =
|
|
1220
|
+
options.limit === undefined
|
|
1221
|
+
? MAX_CHECKPOINT_RESTORE_RECOVERY_LIST_LIMIT
|
|
1222
|
+
: parsePositiveInteger(options.limit);
|
|
1223
|
+
if (limit > MAX_CHECKPOINT_RESTORE_RECOVERY_LIST_LIMIT) {
|
|
1224
|
+
throw cliError(
|
|
1225
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
1226
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
const result = context.reader.list({
|
|
1230
|
+
afterOperationId: options.afterOperationId || "",
|
|
1231
|
+
limit,
|
|
1232
|
+
});
|
|
1233
|
+
return emitSuccess(runtime, options, result, humanList);
|
|
1234
|
+
});
|
|
1235
|
+
},
|
|
1236
|
+
|
|
1237
|
+
show(operationId, options = {}) {
|
|
1238
|
+
return run(options, () => {
|
|
1239
|
+
const safeOperationId = assertOperationId(operationId);
|
|
1240
|
+
const context = openContext(runtime, options.dir);
|
|
1241
|
+
const preview = commandPreview(runtime, context, safeOperationId);
|
|
1242
|
+
return emitSuccess(runtime, options, preview, humanPreview);
|
|
1243
|
+
});
|
|
1244
|
+
},
|
|
1245
|
+
|
|
1246
|
+
abort(operationId, options = {}) {
|
|
1247
|
+
return run(options, () => {
|
|
1248
|
+
if (options.yes !== true) {
|
|
1249
|
+
throw cliError(
|
|
1250
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.CONFIRMATION_REQUIRED,
|
|
1251
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
const safeOperationId = assertOperationId(operationId);
|
|
1255
|
+
const context = openContext(runtime, options.dir, { mutation: true });
|
|
1256
|
+
const preview = commandPreview(runtime, context, safeOperationId);
|
|
1257
|
+
if (preview.actions.abort.eligible !== true) {
|
|
1258
|
+
throw cliError(
|
|
1259
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.ACTION_NOT_ELIGIBLE,
|
|
1260
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
const fence = mutationFence(options, preview);
|
|
1264
|
+
const result = context.controller.abort(safeOperationId, {
|
|
1265
|
+
...fence,
|
|
1266
|
+
reason: "Operator confirmed checkpoint restore recovery abort",
|
|
1267
|
+
});
|
|
1268
|
+
return emitSuccess(
|
|
1269
|
+
runtime,
|
|
1270
|
+
options,
|
|
1271
|
+
safeMutationResult(result, "abort", safeOperationId),
|
|
1272
|
+
humanMutation,
|
|
1273
|
+
);
|
|
1274
|
+
});
|
|
1275
|
+
},
|
|
1276
|
+
|
|
1277
|
+
resume(operationId, options = {}) {
|
|
1278
|
+
return run(options, () => {
|
|
1279
|
+
if (options.yes !== true) {
|
|
1280
|
+
throw cliError(
|
|
1281
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.CONFIRMATION_REQUIRED,
|
|
1282
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
const safeOperationId = assertOperationId(operationId);
|
|
1286
|
+
const context = openContext(runtime, options.dir, {
|
|
1287
|
+
alreadyCompletedResume: true,
|
|
1288
|
+
});
|
|
1289
|
+
const preview = commandPreview(runtime, context, safeOperationId);
|
|
1290
|
+
if (preview.actions.resume.eligible !== true) {
|
|
1291
|
+
throw cliError(
|
|
1292
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.ACTION_NOT_ELIGIBLE,
|
|
1293
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
const fence = mutationFence(options, preview);
|
|
1297
|
+
const result = context.alreadyCompletedController.resume(
|
|
1298
|
+
safeOperationId,
|
|
1299
|
+
fence,
|
|
1300
|
+
);
|
|
1301
|
+
return emitSuccess(
|
|
1302
|
+
runtime,
|
|
1303
|
+
options,
|
|
1304
|
+
safeMutationResult(
|
|
1305
|
+
result,
|
|
1306
|
+
CHECKPOINT_RESTORE_ALREADY_COMPLETED_ACTION,
|
|
1307
|
+
safeOperationId,
|
|
1308
|
+
),
|
|
1309
|
+
humanMutation,
|
|
1310
|
+
);
|
|
1311
|
+
});
|
|
1312
|
+
},
|
|
1313
|
+
|
|
1314
|
+
rollback(operationId, options = {}) {
|
|
1315
|
+
return run(options, () => {
|
|
1316
|
+
if (options.yes !== true) {
|
|
1317
|
+
throw cliError(
|
|
1318
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.CONFIRMATION_REQUIRED,
|
|
1319
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
const safeOperationId = assertOperationId(operationId);
|
|
1323
|
+
const context = openContext(runtime, options.dir, {
|
|
1324
|
+
partialRollback: true,
|
|
1325
|
+
});
|
|
1326
|
+
const preview = commandPreview(runtime, context, safeOperationId);
|
|
1327
|
+
if (preview.actions.rollback.eligible !== true) {
|
|
1328
|
+
throw cliError(
|
|
1329
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.ACTION_NOT_ELIGIBLE,
|
|
1330
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
const fence = mutationFence(options, preview);
|
|
1334
|
+
const result = context.partialRollbackController.rollback(
|
|
1335
|
+
safeOperationId,
|
|
1336
|
+
fence,
|
|
1337
|
+
);
|
|
1338
|
+
return emitSuccess(
|
|
1339
|
+
runtime,
|
|
1340
|
+
options,
|
|
1341
|
+
safeMutationResult(
|
|
1342
|
+
result,
|
|
1343
|
+
CHECKPOINT_RESTORE_PARTIAL_ROLLBACK_ACTION,
|
|
1344
|
+
safeOperationId,
|
|
1345
|
+
{ restoreSurface: preview.recovery.restore.surface },
|
|
1346
|
+
),
|
|
1347
|
+
humanMutation,
|
|
1348
|
+
);
|
|
1349
|
+
});
|
|
1350
|
+
},
|
|
1351
|
+
|
|
1352
|
+
release(operationId, options = {}) {
|
|
1353
|
+
return run(options, () => {
|
|
1354
|
+
if (options.yes !== true) {
|
|
1355
|
+
throw cliError(
|
|
1356
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.CONFIRMATION_REQUIRED,
|
|
1357
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
const safeOperationId = assertOperationId(operationId);
|
|
1361
|
+
const context = openContext(runtime, options.dir, { mutation: true });
|
|
1362
|
+
const preview = commandPreview(runtime, context, safeOperationId);
|
|
1363
|
+
if (preview.actions.release.eligible !== true) {
|
|
1364
|
+
throw cliError(
|
|
1365
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.ACTION_NOT_ELIGIBLE,
|
|
1366
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.NOT_EXECUTABLE,
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
const fence = mutationFence(options, preview);
|
|
1370
|
+
const result = context.controller.release(safeOperationId, fence);
|
|
1371
|
+
return emitSuccess(
|
|
1372
|
+
runtime,
|
|
1373
|
+
options,
|
|
1374
|
+
safeMutationResult(result, "release", safeOperationId),
|
|
1375
|
+
humanMutation,
|
|
1376
|
+
);
|
|
1377
|
+
});
|
|
1378
|
+
},
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/** Register `checkpoint recovery list/show/abort/resume/rollback/release`. */
|
|
1383
|
+
export function registerCheckpointRestoreRecoveryCommands(
|
|
1384
|
+
checkpointCommand,
|
|
1385
|
+
dependencies = {},
|
|
1386
|
+
) {
|
|
1387
|
+
if (!checkpointCommand || typeof checkpointCommand.command !== "function") {
|
|
1388
|
+
throw cliError(
|
|
1389
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_ERROR_CODES.INVALID_ARGUMENT,
|
|
1390
|
+
CHECKPOINT_RESTORE_RECOVERY_CLI_EXIT_CODES.INVALID_USAGE,
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
const handlers = createCheckpointRestoreRecoveryCommandHandlers(dependencies);
|
|
1394
|
+
const recovery = checkpointCommand
|
|
1395
|
+
.command("recovery")
|
|
1396
|
+
.description(
|
|
1397
|
+
"Inspect and conservatively settle durable checkpoint restore recovery",
|
|
1398
|
+
)
|
|
1399
|
+
.addHelpText(
|
|
1400
|
+
"after",
|
|
1401
|
+
"\nResume only reconciles a verified already-completed timeline restore. Rollback only reverses a verified partial workspace mutation to its full safety checkpoint.\n",
|
|
1402
|
+
);
|
|
1403
|
+
|
|
1404
|
+
recovery
|
|
1405
|
+
.command("list")
|
|
1406
|
+
.description("List verified pending recovery projections (read-only)")
|
|
1407
|
+
.option("-d, --dir <dir>", "Workspace directory", ".")
|
|
1408
|
+
.option("--after-operation-id <id>", "Stable operationId page cursor")
|
|
1409
|
+
.option("--limit <count>", "Bounded page size (1-64)")
|
|
1410
|
+
.option("--json", "Output safe JSON")
|
|
1411
|
+
.action((options) => handlers.list(options));
|
|
1412
|
+
|
|
1413
|
+
recovery
|
|
1414
|
+
.command("show")
|
|
1415
|
+
.description("Show one recovery projection and its live mutation fence")
|
|
1416
|
+
.argument("<operation-id>", "Checkpoint restore operationId")
|
|
1417
|
+
.option("-d, --dir <dir>", "Workspace directory", ".")
|
|
1418
|
+
.option("--json", "Output safe JSON")
|
|
1419
|
+
.action((operationId, options) => handlers.show(operationId, options));
|
|
1420
|
+
|
|
1421
|
+
const addMutationOptions = (command) =>
|
|
1422
|
+
command
|
|
1423
|
+
.option(
|
|
1424
|
+
"--expected-seq <number>",
|
|
1425
|
+
"Required exact sequence from recovery show",
|
|
1426
|
+
)
|
|
1427
|
+
.option(
|
|
1428
|
+
"--expected-head-hash <digest>",
|
|
1429
|
+
"Required exact head hash from recovery show",
|
|
1430
|
+
)
|
|
1431
|
+
.option(
|
|
1432
|
+
"--expected-owner-digest <digest>",
|
|
1433
|
+
"Required only when recovery show reports a retained live owner",
|
|
1434
|
+
)
|
|
1435
|
+
.option("--yes", "Confirm the requested recovery mutation")
|
|
1436
|
+
.option("-d, --dir <dir>", "Workspace directory", ".")
|
|
1437
|
+
.option("--json", "Output safe JSON");
|
|
1438
|
+
|
|
1439
|
+
addMutationOptions(
|
|
1440
|
+
recovery
|
|
1441
|
+
.command("abort")
|
|
1442
|
+
.description("Abort only a verified created/locked restore"),
|
|
1443
|
+
)
|
|
1444
|
+
.argument("<operation-id>", "Checkpoint restore operationId")
|
|
1445
|
+
.action((operationId, options) => handlers.abort(operationId, options));
|
|
1446
|
+
|
|
1447
|
+
addMutationOptions(
|
|
1448
|
+
recovery
|
|
1449
|
+
.command("resume")
|
|
1450
|
+
.description(
|
|
1451
|
+
"Complete only a verified timeline restore already settled in the session",
|
|
1452
|
+
),
|
|
1453
|
+
)
|
|
1454
|
+
.argument("<operation-id>", "Checkpoint restore operationId")
|
|
1455
|
+
.action((operationId, options) => handlers.resume(operationId, options));
|
|
1456
|
+
|
|
1457
|
+
addMutationOptions(
|
|
1458
|
+
recovery
|
|
1459
|
+
.command("rollback")
|
|
1460
|
+
.description(
|
|
1461
|
+
"Reverse only a verified partial workspace mutation to full safety",
|
|
1462
|
+
),
|
|
1463
|
+
)
|
|
1464
|
+
.argument("<operation-id>", "Checkpoint restore operationId")
|
|
1465
|
+
.action((operationId, options) => handlers.rollback(operationId, options));
|
|
1466
|
+
|
|
1467
|
+
addMutationOptions(
|
|
1468
|
+
recovery
|
|
1469
|
+
.command("release")
|
|
1470
|
+
.description("Release/archive only a verified terminal restore"),
|
|
1471
|
+
)
|
|
1472
|
+
.argument("<operation-id>", "Checkpoint restore operationId")
|
|
1473
|
+
.action((operationId, options) => handlers.release(operationId, options));
|
|
1474
|
+
|
|
1475
|
+
return recovery;
|
|
1476
|
+
}
|