chainlesschain 0.166.90 → 0.166.92
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 +15 -13
- package/package.json +4 -4
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-CZzXRe1-.js → AIOps-C74YzqwJ.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-_AZfbW1Q.js → ActionButton-TFef1hOE.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CzaUx_wv.js → Analytics-CYsWpkkU.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-xHEeeD-m.js → AppLayout-CFf9kjwu.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-DmTbtudE.js → Artifacts-TQqa94YO.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Cn4ng4fo.js → Audit-B79ayqfS.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DLUY1VD2.js → BackgroundAgents-pjkHfLP9.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B2v--wB6.js → Backup-CE6A-t_r.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Bgih7yAv.js → BaseInput-CwcgWMBY.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-1MiiA3yA.js → Chat-BIkDNesY.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CPLynmbb.js → ChatBubbleRenderer-DgaokYts.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DsyBa_NO.js → Checkbox-MbypIy5V.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B7WCFx9H.js → Codegen-DiFHWDnt.js} +1 -1
- package/src/assets/web-panel/assets/{Col-D2x4GBGm.js → Col-BNMlYMso.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Rnwg9nea.js → Community-B3HVyHOX.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BPczlrRh.js → Compact-C7hlAubb.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Br1L_wGU.js → Compliance-PaXZu6sE.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DlsWB_oS.js → Cowork-BGiojbm-.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BWo_KGpG.js → Cron-Bmr-1QAx.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Cug7N8ge.js → Crosschain-BwUvWCVd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CMbyOZJN.js → DID-k_4QZocW.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C4dl_UaE.js → Dashboard-_ieRo2C5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CBrC7M-9.js → Dropdown-DYHh6T27.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DTq7tU4b.js → EmailListRenderer-BNlQcWQX.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-DWP3csxS.js → EvolutionSettings-DIkZ6U2D.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DZl7zuv_.js → FamilyGuardDashboard-Dq7qSr0P.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-W-GSlVK5.js → Federation-C64e3Cbx.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DMB7xYFV.js → FormItemContext-D-GE2vUa.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DNv8GEoq.js → GenericCardRenderer-DO3B2gis.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BZ0Bbzw6.js → Git-0ETirIQH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BNEdcOM5.js → Governance-C9fnRygX.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-C8249gIz.js → Inference-CLJ9RTsK.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-ngzebU3e.js → KnowledgeGraph-D3C_GxrN.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CP15th-f.js → Logs-C5ctGbkN.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Ce-eWK3j.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CaH59OsY.js → Marketplace-GF_t6hJ_.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C07vnJev.js → McpTools-BGsGzgQs.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CqAOkVAS.js → Memory-CcrY2A0j.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-qp7mZimq.js → MobileBridge-Bi4T0RlS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DKhbqvTw.js → MobileProjects-DvjEGyvf.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CRYCnWXv.js → Mtc-DL77Vf-k.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-Z08IrKws.js → MtcAudit-CDy_Uswb.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BWdatUnH.js → Multisig-DL0BAO_c.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BKWiCZ_m.js → NLProgramming-B-woh5IR.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Bgpzk3sm.js → Notes-CEFVCxk7.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DvtZEktR.js → NotificationSettings-BqPPMuAP.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CBcy0szc.js → OrderTableRenderer-GUKQ1xwp.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DRRL6vW1.js → Organization-B1gX8jlr.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-COmfB8Ns.js → Overflow-CeMxuUZy.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BqMq_DSa.js → P2P-B5aIRWwJ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BX7iNvle.js → PdhVaultBrowser-6rYgejd4.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DzYkjmV_.js → Permissions-CqCp2SIl.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BBTcxBbN.js → PersonalDataHub-BsA7DJWW.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-eRkYFSbO.js → Pipeline-CiW6HeZh.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Ch7IFKBh.js → Privacy-DckUOLYq.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Ci8EcBYg.js → ProjectInit-CZppOOYR.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-B4lYHFbI.js → ProjectSettings-B9QPzhDU.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-zkE5cCo_.js → Projects-BErjjGha.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BKDVD3_e.js → Providers-DehU6Qiz.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BromrHlf.js → QrScannerModal-E1SUvv1m.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CCgoY1gR.js → QuickAsk-COWFxyH3.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DCPZvpq7.js → Recommend-D26pWKxX.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DPnFP8HU.js → RemoteSession-DCOIaId3.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-l6r8ytAs.js → Reputation-DT_AHGAR.js} +1 -1
- package/src/assets/web-panel/assets/{Row-ke9Mk1cx.js → Row-B50S-1nl.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DQOEzUEn.js → RssFeed-BaBnHg3Q.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BqgA0aUz.js → Search-BVdnLuuS.js} +1 -1
- package/src/assets/web-panel/assets/{Security-FeGD_jqV.js → Security-BnacH74J.js} +3 -3
- package/src/assets/web-panel/assets/{Services-4kiYYAjZ.js → Services-CxM08Zch.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BbU8Xegs.js → Skeleton-DE7CIc1N.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Dl2bUMUL.js → Skills-CGI0SJmf.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-xRGALeaZ.js → Sla-DpEzPcqc.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DZfd4RDm.js → SpeechSettings-CTmnmMHn.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BRtCV9Ga.js → SyncSettings-Dxgrd9hd.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DVRQklfG.js → Tasks-CW4slS0A.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BbhE5jt5.js → Templates-DxvGWDgB.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-qXPDNKbK.js → Tenant-_Q0o2tNK.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CV48Ks7E.js → Terminal-BTZn8pow.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BLGfJIDr.js → TimelineRenderer-CauL0KFe.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-7G9H6Hrt.js → Tokens-CDoM2qeT.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--F64N11F.js → Trigger-B-QYGEU7.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Cq1r-D_y.js → Trust-BD6XJHT-.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Dqyu4RVh.js → UkeySign-x4TFwCy0.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DuFmWH_B.js → VideoEditing-t3imBK_0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C3mg7EWP.js → Wallet-BCfkWwNC.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CDK-Aj_X.js → WebAuthn-WH314Mdm.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor--K7K-T1F.js → WorkflowEditor-jUlcjrUg.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BQDcS6VP.js → chat-Dg3VASTL.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BKxJL2-T.js → colors-BNvmBRKE.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CFjQAMbY.js → compact-item-BGOfoqf4.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DbgCsdQd.js → createContext-BNQeqKAs.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B7_ypN1G.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BhkaOBNv.js → hasIn-Bg3AjCrq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvWWFJHJ.js → index--I3UF5t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJ2OiRvh.js → index-2D04Zltx.js} +3 -3
- package/src/assets/web-panel/assets/{index-BuWPqSGT.js → index-60AnjUTX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2Zb-Kd1.js → index-7QJUWemf.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0DzhUOX.js → index-B--0QM0S.js} +1 -1
- package/src/assets/web-panel/assets/index-B-jSKJK6.js +1 -0
- package/src/assets/web-panel/assets/{index-COGy6wXn.js → index-B0mgngoB.js} +1 -1
- package/src/assets/web-panel/assets/{index-bMX8BBLE.js → index-BBCMulQ-.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE506mgj.js → index-BSNhW95G.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoOu7sdP.js → index-B_JmInC0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4LlAJqc.js → index-BaPvEvB4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRtUDiXx.js → index-Bkz1tgFR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du_SxxBY.js → index-BmTacaQV.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSUDlsIr.js → index-CCFZN66A.js} +1 -1
- package/src/assets/web-panel/assets/{index-3uR0ThJO.js → index-COol3xfA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmYK9Tqt.js → index-CY8cRKHR.js} +1 -1
- package/src/assets/web-panel/assets/{index-T9ka3Xj3.js → index-Cbp-wp-U.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB4oHynY.js → index-Co_LnCWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEwHywz9.js → index-Cx2EXRfr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUs7XpUO.js → index-D-mSvCQK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRqlCTrz.js → index-D072oLjp.js} +1 -1
- package/src/assets/web-panel/assets/{index-sKfUUldY.js → index-D6UqK83L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dhfa_BIl.js → index-DF_FKSVH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9sVcw6v.js → index-DLwEXRrb.js} +1 -1
- package/src/assets/web-panel/assets/{index-3djsY75k.js → index-DO0VlNtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfGGsAU7.js → index-DS04wRll.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dn2QSsuI.js → index-DYn5DnDk.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-Yh3-1z.js → index-D_O2ZjPk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNjYPJNU.js → index-DcCnO0RU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP1NrM9J.js → index-Dkp-okWa.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdcrixaQ.js → index-OUFoxUgH.js} +1 -1
- package/src/assets/web-panel/assets/index-P4FXZJqI.js +1 -0
- package/src/assets/web-panel/assets/{index-7BY44UEv.js → index-Wo0f2iqb.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjmVlpap.js → index-ZD5Kd8tx.js} +1 -1
- package/src/assets/web-panel/assets/{index-3YutaXnp.js → index-fOwii3LP.js} +1 -1
- package/src/assets/web-panel/assets/{index-aPdVzagA.js → index-hG7jXGEW.js} +1 -1
- package/src/assets/web-panel/assets/{index-De7daEnc.js → index-pVdQieac.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJoXgJ2N.js → index-uH2LnAs_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFt48Z-x.js → index-xzaUx19r.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-wrqabmui.js → initDefaultProps-B8PN9AxJ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BoyB3ilP.js → motion-BHivfw5Q.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cq31cSFE.js → move-B0QU5nDd.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkxNt-8H.js → mtc-parser-DRwRM3PE.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DofGni53.js → omit-SHt2rV2p.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BnluLL16.js → pickAttrs-CcmDlEb2.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-C-yegJSP.js → placementArrow-DYatmXDS.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CD3ziNhu.js → responsiveObserve-B7qrsXq5.js} +1 -1
- package/src/assets/web-panel/assets/{slide-ToZO4gQ1.js → slide-BRGhlOWd.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DFVmDmVl.js → statusUtils-CT-7NXsQ.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-De0Y8g1O.js → styleChecker-rZdw_Yyp.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-wBKIYFKX.js → useFlexGapSupport-D8YNIP4P.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CP9nmUMJ.js → useFs-B8pt0Gu-.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-vFGrVDyp.js → usePersonalDataHub-VMmYM1tt.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BcBIjvjH.js → vnode-UNDuoPoq.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnXDAQxA.js → zoom-dN-u667S.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agent-process-ownership.js +121 -0
- package/src/commands/agent.js +2 -0
- package/src/commands/hub.js +86 -9
- package/src/data/changelog.json +54 -2
- package/src/lib/audit-logger.js +46 -20
- package/src/lib/checkpoint-restore-saga.js +11 -1
- package/src/lib/eval/verify01-host-evidence.js +36 -3
- package/src/lib/evolution/evolution-artifact-ports.js +26 -0
- package/src/lib/evolution/evolution-eval-cohort-enrollment.js +713 -47
- package/src/lib/evolution/evolution-eval-gate.js +146 -0
- package/src/lib/evolution/evolution-eval-launch-admission.js +168 -90
- package/src/lib/evolution/evolution-eval-ledger-capture.js +50 -0
- package/src/lib/evolution/evolution-eval-readonly-audit.js +106 -0
- package/src/lib/evolution/evolution-ledger-file-backend.js +20 -0
- package/src/lib/evolution/evolution-ledger-ports.js +59 -27
- package/src/lib/evolution/evolution-ledger-v2-journal.js +55 -18
- package/src/lib/evolution/evolution-workbench-file-resources.js +19 -1
- package/src/lib/evolution/rrsi-cohort-registration.js +275 -0
- package/src/lib/evolution/rrsi-contracts.js +742 -0
- package/src/lib/evolution/rrsi-data.js +196 -0
- package/src/lib/evolution/rrsi-evaluation-adapter.js +275 -0
- package/src/lib/evolution/rrsi-evaluation-variants.js +267 -0
- package/src/lib/evolution/rrsi-group-statistics.js +553 -0
- package/src/lib/evolution/rrsi-history-ledger-adapter.js +3261 -0
- package/src/lib/evolution/rrsi-native-batch-evidence.js +520 -0
- package/src/lib/evolution/rrsi-native-eval-row-collector.js +538 -0
- package/src/lib/evolution/rrsi-native-evaluation-batch.js +547 -0
- package/src/lib/evolution/rrsi-native-evaluation-plan.js +431 -0
- package/src/lib/evolution/rrsi-native-group-statistics-plan.js +121 -0
- package/src/lib/evolution/rrsi-native-group-statistics.js +482 -0
- package/src/lib/evolution/rrsi-native-quality-receipt-contracts.js +321 -0
- package/src/lib/evolution/rrsi-native-quality-receipt.js +256 -0
- package/src/lib/evolution/rrsi-native-statistics-protocol.js +330 -0
- package/src/lib/evolution/rrsi-parent-binding.js +290 -0
- package/src/lib/evolution/rrsi-pm-execution-bridge.js +500 -0
- package/src/lib/evolution/rrsi-pm-training-mapping.js +134 -0
- package/src/lib/evolution/rrsi-preparation-contracts.js +213 -0
- package/src/lib/evolution/rrsi-registry-history-binding.js +193 -0
- package/src/lib/evolution/rrsi-registry-store-policy.js +1191 -0
- package/src/lib/evolution/rrsi-runtime-composition.js +79 -0
- package/src/lib/evolution/rrsi-selector.js +434 -0
- package/src/lib/evolution/rrsi-statistics-simulation.js +354 -0
- package/src/lib/evolution/skill-candidate-registry.js +216 -29
- package/src/lib/evolution/skill-registry-maintenance.js +80 -0
- package/src/lib/evolution/skill-registry-writer-control.js +138 -0
- package/src/lib/evolution/skill-release-registry.js +399 -169
- package/src/lib/formal-quality-eval-runtime.js +2 -14
- package/src/lib/goal-store.js +225 -16
- package/src/lib/process-execution-broker/index.js +91 -1
- package/src/lib/process-execution-broker/platform-sandbox.js +42 -0
- package/src/lib/process-execution-broker/windows-native-evaluator-capabilities.js +757 -0
- package/src/lib/process-execution-broker/windows-native-evaluator.js +316 -0
- 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 +383 -11
- package/src/lib/record-replay/skill-recorder.js +38 -3
- package/src/lib/scheduler-kernel/authority-resolver.js +12 -163
- package/src/lib/scheduler-kernel/contract.js +27 -414
- package/src/lib/scheduler-kernel/runtime.js +21 -823
- package/src/lib/scheduler-kernel/service.js +11 -328
- package/src/lib/scheduler-kernel/source-locator-path.js +7 -142
- package/src/lib/scheduler-kernel/store.js +67 -5931
- package/src/lib/secure-fs.js +16 -1314
- package/src/lib/with-file-lock.js +122 -20
- package/src/assets/web-panel/assets/MarkdownRenderer-CW3B3JDq.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DlIv9Mbc.js +0 -1
- package/src/assets/web-panel/assets/index-BC972Grv.js +0 -1
- package/src/assets/web-panel/assets/index-CVifyQqL.js +0 -1
|
@@ -6,6 +6,12 @@ import { types as utilTypes } from "node:util";
|
|
|
6
6
|
import { getHomeDir } from "../paths.js";
|
|
7
7
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
8
8
|
import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
|
|
9
|
+
import { createSkillRegistryWriterControl } from "./skill-registry-writer-control.js";
|
|
10
|
+
import {
|
|
11
|
+
captureRrsiRegistryComponentBinding,
|
|
12
|
+
assertOutsideRrsiRegistryNamespace,
|
|
13
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
14
|
+
} from "./rrsi-registry-store-policy.js";
|
|
9
15
|
import {
|
|
10
16
|
SKILL_CANDIDATE_MIGRATION_RECEIPT_SCHEMA,
|
|
11
17
|
SKILL_CANDIDATE_MIGRATION_RECORD_SCHEMA,
|
|
@@ -62,6 +68,7 @@ export const SKILL_RELEASE_JOURNAL_ARCHIVE_SCHEMA =
|
|
|
62
68
|
|
|
63
69
|
const JOURNAL_SCHEMA = "chainlesschain.skill-release-journal/v4";
|
|
64
70
|
const REGISTRY_READERS = new WeakMap();
|
|
71
|
+
const REGISTRY_WRITERS = new WeakMap();
|
|
65
72
|
|
|
66
73
|
export function captureSkillReleaseRegistryReader(value) {
|
|
67
74
|
const reader = REGISTRY_READERS.get(value);
|
|
@@ -72,6 +79,16 @@ export function captureSkillReleaseRegistryReader(value) {
|
|
|
72
79
|
throw new TypeError("a genuine SkillReleaseRegistry reader is required");
|
|
73
80
|
return reader;
|
|
74
81
|
}
|
|
82
|
+
|
|
83
|
+
export function captureSkillReleaseRegistryWriterControl(value) {
|
|
84
|
+
captureSkillReleaseRegistryReader(value);
|
|
85
|
+
const control = REGISTRY_WRITERS.get(value);
|
|
86
|
+
if (!control)
|
|
87
|
+
throw new TypeError(
|
|
88
|
+
"a genuine release Registry writer control is required",
|
|
89
|
+
);
|
|
90
|
+
return control;
|
|
91
|
+
}
|
|
75
92
|
const INTENT_SCHEMA = "chainlesschain.skill-release-transition-intent/v2";
|
|
76
93
|
const RELEASE_DOMAIN = `${SKILL_RELEASE_SCHEMA}\0`;
|
|
77
94
|
const STATE_DOMAIN = `${SKILL_RELEASE_STATE_SCHEMA}\0`;
|
|
@@ -1850,128 +1867,151 @@ export function archiveLegacySkillReleaseJournal(input) {
|
|
|
1850
1867
|
) {
|
|
1851
1868
|
throw migrationRequired("legacy journal archive paths are invalid");
|
|
1852
1869
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
);
|
|
1867
|
-
const expectedBytes = serialize(archive);
|
|
1868
|
-
let created = false;
|
|
1869
|
-
if (lstatOrNull(fsImpl, archivePath) === null) {
|
|
1870
|
-
const source = readBoundedSingleLinkFile(
|
|
1871
|
-
fsImpl,
|
|
1872
|
-
sourcePath,
|
|
1873
|
-
MAX_FILE_BYTES,
|
|
1874
|
-
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1875
|
-
"legacy release journal source",
|
|
1870
|
+
// An independent archive helper can also retire bytes under a tenant's
|
|
1871
|
+
// journals directory. It must participate in that physical root's writer
|
|
1872
|
+
// protocol rather than bypassing the class-method maintenance boundary.
|
|
1873
|
+
const writer = createSkillRegistryWriterControl({
|
|
1874
|
+
rootDir: path.dirname(sourceDirectory),
|
|
1875
|
+
tenantId: disposition.tenantId,
|
|
1876
|
+
component: TENANT_MARKER_COMPONENT,
|
|
1877
|
+
fsImpl,
|
|
1878
|
+
});
|
|
1879
|
+
return writer.runSync(() => {
|
|
1880
|
+
const sourcePath = path.join(
|
|
1881
|
+
sourceDirectory,
|
|
1882
|
+
path.basename(requestedSourcePath),
|
|
1876
1883
|
);
|
|
1877
|
-
if (
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1884
|
+
if (input.secure) {
|
|
1885
|
+
ensurePrivateDirectory(archiveDirectory, {
|
|
1886
|
+
applyWindowsAcl: true,
|
|
1887
|
+
failIfUnavailable: true,
|
|
1888
|
+
});
|
|
1881
1889
|
}
|
|
1882
|
-
const
|
|
1890
|
+
const archivePath = path.join(
|
|
1883
1891
|
archiveDirectory,
|
|
1884
|
-
|
|
1892
|
+
`${disposition.planDigest.slice("sha256:".length)}.json`,
|
|
1885
1893
|
);
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
fsImpl.fsyncSync(descriptor);
|
|
1891
|
-
fsImpl.closeSync(descriptor);
|
|
1892
|
-
descriptor = null;
|
|
1893
|
-
if (input.secure) {
|
|
1894
|
-
ensurePrivateFile(temporaryPath, {
|
|
1895
|
-
applyWindowsAcl: true,
|
|
1896
|
-
failIfUnavailable: true,
|
|
1897
|
-
});
|
|
1898
|
-
}
|
|
1899
|
-
const staged = readBoundedSingleLinkFile(
|
|
1894
|
+
const expectedBytes = serialize(archive);
|
|
1895
|
+
let created = false;
|
|
1896
|
+
if (lstatOrNull(fsImpl, archivePath) === null) {
|
|
1897
|
+
const source = readBoundedSingleLinkFile(
|
|
1900
1898
|
fsImpl,
|
|
1901
|
-
|
|
1899
|
+
sourcePath,
|
|
1902
1900
|
MAX_FILE_BYTES,
|
|
1903
1901
|
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1904
|
-
"legacy journal
|
|
1902
|
+
"legacy release journal source",
|
|
1905
1903
|
);
|
|
1906
|
-
if (!
|
|
1907
|
-
throw migrationRequired(
|
|
1904
|
+
if (!source.bytes.equals(serialize(journal))) {
|
|
1905
|
+
throw migrationRequired(
|
|
1906
|
+
"legacy release journal source bytes differ from the verified input",
|
|
1907
|
+
);
|
|
1908
1908
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1909
|
+
const temporaryPath = path.join(
|
|
1910
|
+
archiveDirectory,
|
|
1911
|
+
`.journal-${process.pid}-${crypto.randomBytes(16).toString("hex")}.tmp`,
|
|
1912
|
+
);
|
|
1913
|
+
let descriptor = null;
|
|
1913
1914
|
try {
|
|
1914
|
-
fsImpl.
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1915
|
+
descriptor = fsImpl.openSync(temporaryPath, "wx", 0o600);
|
|
1916
|
+
fsImpl.writeFileSync(descriptor, expectedBytes);
|
|
1917
|
+
fsImpl.fsyncSync(descriptor);
|
|
1918
|
+
fsImpl.closeSync(descriptor);
|
|
1919
|
+
descriptor = null;
|
|
1920
|
+
if (input.secure) {
|
|
1921
|
+
ensurePrivateFile(temporaryPath, {
|
|
1922
|
+
applyWindowsAcl: true,
|
|
1923
|
+
failIfUnavailable: true,
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
const staged = readBoundedSingleLinkFile(
|
|
1927
|
+
fsImpl,
|
|
1928
|
+
temporaryPath,
|
|
1929
|
+
MAX_FILE_BYTES,
|
|
1930
|
+
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1931
|
+
"legacy journal archive temporary",
|
|
1932
|
+
);
|
|
1933
|
+
if (!staged.bytes.equals(expectedBytes)) {
|
|
1934
|
+
throw migrationRequired(
|
|
1935
|
+
"legacy journal archive write was incomplete",
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
checkpoint("after-archive-fsync", {
|
|
1939
|
+
archiveDigest: archive.archiveDigest,
|
|
1940
|
+
dispositionPlanDigest: disposition.planDigest,
|
|
1941
|
+
});
|
|
1942
|
+
try {
|
|
1943
|
+
writer.assertIfWriting();
|
|
1944
|
+
fsImpl.linkSync(temporaryPath, archivePath);
|
|
1945
|
+
created = true;
|
|
1946
|
+
} catch (error) {
|
|
1947
|
+
if (error?.code !== "EEXIST") throw error;
|
|
1948
|
+
}
|
|
1928
1949
|
fsImpl.unlinkSync(temporaryPath);
|
|
1950
|
+
fsyncDirectory(fsImpl, archiveDirectory);
|
|
1951
|
+
checkpoint("after-archive-publish", {
|
|
1952
|
+
archiveDigest: archive.archiveDigest,
|
|
1953
|
+
dispositionPlanDigest: disposition.planDigest,
|
|
1954
|
+
});
|
|
1955
|
+
} finally {
|
|
1956
|
+
if (descriptor !== null) fsImpl.closeSync(descriptor);
|
|
1957
|
+
let cleanupOwned = false;
|
|
1958
|
+
try {
|
|
1959
|
+
writer.assertIfWriting();
|
|
1960
|
+
cleanupOwned = true;
|
|
1961
|
+
} catch {
|
|
1962
|
+
/* Preserve private debris after lost ownership. */
|
|
1963
|
+
}
|
|
1964
|
+
if (cleanupOwned && lstatOrNull(fsImpl, temporaryPath) !== null)
|
|
1965
|
+
fsImpl.unlinkSync(temporaryPath);
|
|
1966
|
+
}
|
|
1929
1967
|
}
|
|
1930
|
-
|
|
1931
|
-
const persisted = readBoundedSingleLinkFile(
|
|
1932
|
-
fsImpl,
|
|
1933
|
-
archivePath,
|
|
1934
|
-
MAX_FILE_BYTES,
|
|
1935
|
-
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1936
|
-
"legacy journal archive",
|
|
1937
|
-
);
|
|
1938
|
-
if (
|
|
1939
|
-
!persisted.bytes.equals(expectedBytes) ||
|
|
1940
|
-
canonicalJson(
|
|
1941
|
-
verifySkillReleaseJournalArchive(
|
|
1942
|
-
JSON.parse(
|
|
1943
|
-
new TextDecoder("utf-8", { fatal: true }).decode(persisted.bytes),
|
|
1944
|
-
),
|
|
1945
|
-
),
|
|
1946
|
-
) !== canonicalJson(archive)
|
|
1947
|
-
) {
|
|
1948
|
-
throw migrationRequired("legacy journal archive readback differs");
|
|
1949
|
-
}
|
|
1950
|
-
checkpoint("after-archive-readback", {
|
|
1951
|
-
archiveDigest: archive.archiveDigest,
|
|
1952
|
-
dispositionPlanDigest: disposition.planDigest,
|
|
1953
|
-
});
|
|
1954
|
-
if (lstatOrNull(fsImpl, sourcePath) !== null) {
|
|
1955
|
-
const source = readBoundedSingleLinkFile(
|
|
1968
|
+
const persisted = readBoundedSingleLinkFile(
|
|
1956
1969
|
fsImpl,
|
|
1957
|
-
|
|
1970
|
+
archivePath,
|
|
1958
1971
|
MAX_FILE_BYTES,
|
|
1959
1972
|
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1960
|
-
"legacy
|
|
1973
|
+
"legacy journal archive",
|
|
1961
1974
|
);
|
|
1962
|
-
if (
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1975
|
+
if (
|
|
1976
|
+
!persisted.bytes.equals(expectedBytes) ||
|
|
1977
|
+
canonicalJson(
|
|
1978
|
+
verifySkillReleaseJournalArchive(
|
|
1979
|
+
JSON.parse(
|
|
1980
|
+
new TextDecoder("utf-8", { fatal: true }).decode(persisted.bytes),
|
|
1981
|
+
),
|
|
1982
|
+
),
|
|
1983
|
+
) !== canonicalJson(archive)
|
|
1984
|
+
) {
|
|
1985
|
+
throw migrationRequired("legacy journal archive readback differs");
|
|
1966
1986
|
}
|
|
1967
|
-
|
|
1968
|
-
fsyncDirectory(fsImpl, sourceDirectory);
|
|
1969
|
-
checkpoint("after-source-retire", {
|
|
1987
|
+
checkpoint("after-archive-readback", {
|
|
1970
1988
|
archiveDigest: archive.archiveDigest,
|
|
1971
1989
|
dispositionPlanDigest: disposition.planDigest,
|
|
1972
1990
|
});
|
|
1973
|
-
|
|
1974
|
-
|
|
1991
|
+
if (lstatOrNull(fsImpl, sourcePath) !== null) {
|
|
1992
|
+
const source = readBoundedSingleLinkFile(
|
|
1993
|
+
fsImpl,
|
|
1994
|
+
sourcePath,
|
|
1995
|
+
MAX_FILE_BYTES,
|
|
1996
|
+
SKILL_RELEASE_MIGRATION_REQUIRED_CODE,
|
|
1997
|
+
"legacy release journal source",
|
|
1998
|
+
);
|
|
1999
|
+
if (!source.bytes.equals(serialize(journal))) {
|
|
2000
|
+
throw migrationRequired(
|
|
2001
|
+
"legacy release journal changed before retirement",
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
writer.assertIfWriting();
|
|
2005
|
+
fsImpl.unlinkSync(sourcePath);
|
|
2006
|
+
fsyncDirectory(fsImpl, sourceDirectory);
|
|
2007
|
+
checkpoint("after-source-retire", {
|
|
2008
|
+
archiveDigest: archive.archiveDigest,
|
|
2009
|
+
dispositionPlanDigest: disposition.planDigest,
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
writer.assertIfWriting();
|
|
2013
|
+
return deepFreeze({ archive, archivePath, created, sourceRetired: true });
|
|
2014
|
+
});
|
|
1975
2015
|
}
|
|
1976
2016
|
|
|
1977
2017
|
/**
|
|
@@ -2472,6 +2512,7 @@ const REGISTRY_OPTION_KEYS = new Set([
|
|
|
2472
2512
|
"randomToken",
|
|
2473
2513
|
"rootDir",
|
|
2474
2514
|
"secure",
|
|
2515
|
+
"storePolicyBinding",
|
|
2475
2516
|
"tenantId",
|
|
2476
2517
|
"transactionLedger",
|
|
2477
2518
|
]);
|
|
@@ -2659,6 +2700,7 @@ function normalizeRegistryOptions(options) {
|
|
|
2659
2700
|
data.rootDir ??
|
|
2660
2701
|
path.join(getHomeDir(), "evolution", "registry", "releases"),
|
|
2661
2702
|
secure: data.secure ?? true,
|
|
2703
|
+
storePolicyBinding: data.storePolicyBinding,
|
|
2662
2704
|
tenantId: tenantId(data.tenantId),
|
|
2663
2705
|
transactionLedger: data.transactionLedger,
|
|
2664
2706
|
};
|
|
@@ -2696,6 +2738,9 @@ export class SkillReleaseRegistry {
|
|
|
2696
2738
|
#ledgerQuery;
|
|
2697
2739
|
|
|
2698
2740
|
#pins = new WeakMap();
|
|
2741
|
+
#writerControl;
|
|
2742
|
+
#storeBinding;
|
|
2743
|
+
#runtimeMode = "initialized";
|
|
2699
2744
|
|
|
2700
2745
|
constructor(options) {
|
|
2701
2746
|
const {
|
|
@@ -2706,9 +2751,32 @@ export class SkillReleaseRegistry {
|
|
|
2706
2751
|
randomToken,
|
|
2707
2752
|
rootDir,
|
|
2708
2753
|
secure,
|
|
2754
|
+
storePolicyBinding,
|
|
2709
2755
|
tenantId: ownerTenantId,
|
|
2710
2756
|
transactionLedger,
|
|
2711
2757
|
} = normalizeRegistryOptions(options);
|
|
2758
|
+
const requestedBase = path.resolve(rootDir);
|
|
2759
|
+
if (storePolicyBinding !== undefined) {
|
|
2760
|
+
this.#storeBinding =
|
|
2761
|
+
captureRrsiRegistryComponentBinding(storePolicyBinding);
|
|
2762
|
+
const descriptor = this.#storeBinding.descriptor;
|
|
2763
|
+
if (
|
|
2764
|
+
new.target !== SkillReleaseRegistry ||
|
|
2765
|
+
fsImpl !== fs ||
|
|
2766
|
+
secure !== true ||
|
|
2767
|
+
descriptor.component !== TENANT_MARKER_COMPONENT ||
|
|
2768
|
+
descriptor.tenantId !== ownerTenantId ||
|
|
2769
|
+
!samePath(descriptor.baseDir, requestedBase)
|
|
2770
|
+
)
|
|
2771
|
+
throw failure(
|
|
2772
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
2773
|
+
"release v2 binding or native storage options differ",
|
|
2774
|
+
);
|
|
2775
|
+
// Authenticate the original port object before reading any caller method.
|
|
2776
|
+
this.#storeBinding.assertTransactionLedger(transactionLedger);
|
|
2777
|
+
this.#storeBinding.recheckForOpen();
|
|
2778
|
+
this.#runtimeMode = "uninitialized";
|
|
2779
|
+
} else assertOutsideRrsiRegistryNamespace(requestedBase);
|
|
2712
2780
|
if (
|
|
2713
2781
|
!transactionLedger ||
|
|
2714
2782
|
typeof transactionLedger.prepare !== "function" ||
|
|
@@ -2742,57 +2810,103 @@ export class SkillReleaseRegistry {
|
|
|
2742
2810
|
this.#now = now;
|
|
2743
2811
|
this.#leaseTtlMs = leaseTtlMs;
|
|
2744
2812
|
this.#crashHook = crashHook;
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
? transactionLedger.migrate.bind(transactionLedger)
|
|
2813
|
+
const captureTransactionMethod = (name) => {
|
|
2814
|
+
const method = transactionLedger[name];
|
|
2815
|
+
return typeof method === "function"
|
|
2816
|
+
? (...args) => Reflect.apply(method, transactionLedger, args)
|
|
2750
2817
|
: null;
|
|
2751
|
-
|
|
2818
|
+
};
|
|
2819
|
+
this.#ledgerPrepare = captureTransactionMethod("prepare");
|
|
2820
|
+
this.#ledgerFinalize = captureTransactionMethod("finalize");
|
|
2821
|
+
this.#ledgerMigrate = captureTransactionMethod("migrate");
|
|
2822
|
+
this.#ledgerQuery = captureTransactionMethod("query");
|
|
2752
2823
|
Object.freeze(transactionLedger);
|
|
2753
2824
|
|
|
2754
2825
|
this.tenantId = ownerTenantId;
|
|
2755
2826
|
this.tenantKey = deriveSkillReleaseTenantKey(ownerTenantId);
|
|
2756
|
-
const requestedBase = path.resolve(rootDir);
|
|
2757
2827
|
try {
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
this.#
|
|
2787
|
-
|
|
2828
|
+
if (this.#storeBinding) {
|
|
2829
|
+
this.#boundaries = this.#storeBinding.boundaries;
|
|
2830
|
+
this.baseDir = this.#boundaries.base.path;
|
|
2831
|
+
this.rootDir = this.#boundaries.tenantRoot.path;
|
|
2832
|
+
this.#rootIdentity = this.#boundaries.tenantRoot.identity;
|
|
2833
|
+
this.#markerPath = path.join(this.rootDir, TENANT_MARKER_FILE);
|
|
2834
|
+
this.#markerIdentity = this.#storeBinding.marker.identity;
|
|
2835
|
+
this.#directories = deepFreeze({});
|
|
2836
|
+
this.#assertBoundary();
|
|
2837
|
+
} else {
|
|
2838
|
+
const base = this.#initializeDirectory(requestedBase, {
|
|
2839
|
+
recursive: true,
|
|
2840
|
+
harden: false,
|
|
2841
|
+
});
|
|
2842
|
+
this.baseDir = base.path;
|
|
2843
|
+
this.#assertUnboundNamespace();
|
|
2844
|
+
this.#assertNoLegacyBaseLayout();
|
|
2845
|
+
const tenants = this.#initializeDirectory(
|
|
2846
|
+
path.join(this.baseDir, "tenants"),
|
|
2847
|
+
{ parent: base, harden: false },
|
|
2848
|
+
);
|
|
2849
|
+
const tenantRoot = this.#initializeDirectory(
|
|
2850
|
+
path.join(tenants.path, this.tenantKey),
|
|
2851
|
+
{ parent: tenants, harden: false },
|
|
2852
|
+
);
|
|
2853
|
+
this.rootDir = tenantRoot.path;
|
|
2854
|
+
this.#assertUnboundNamespace();
|
|
2855
|
+
this.#boundaries = deepFreeze({ base, tenantRoot, tenants });
|
|
2856
|
+
this.#rootIdentity = tenantRoot.identity;
|
|
2857
|
+
this.#markerPath = path.join(this.rootDir, TENANT_MARKER_FILE);
|
|
2858
|
+
this.#writerControl = createSkillRegistryWriterControl({
|
|
2859
|
+
rootDir: this.rootDir,
|
|
2860
|
+
tenantId: this.tenantId,
|
|
2861
|
+
component: TENANT_MARKER_COMPONENT,
|
|
2862
|
+
fsImpl: this.#fs,
|
|
2863
|
+
});
|
|
2864
|
+
this.#writerControl.runSync(() => {
|
|
2865
|
+
if (this.#secure) {
|
|
2866
|
+
for (const [entry, parent] of [
|
|
2867
|
+
[base, null],
|
|
2868
|
+
[tenants, base],
|
|
2869
|
+
[tenantRoot, tenants],
|
|
2870
|
+
]) {
|
|
2871
|
+
this.#writerControl.assertIfWriting();
|
|
2872
|
+
const hardened = this.#initializeDirectory(entry.path, {
|
|
2873
|
+
parent,
|
|
2874
|
+
});
|
|
2875
|
+
if (
|
|
2876
|
+
hardened.identity !== entry.identity ||
|
|
2877
|
+
!samePath(hardened.path, entry.path)
|
|
2878
|
+
)
|
|
2879
|
+
throw failure(
|
|
2880
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
2881
|
+
"release bootstrap identity changed before permission hardening",
|
|
2882
|
+
);
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
this.#writerControl.assertIfWriting();
|
|
2886
|
+
this.#initializeTenantMarker();
|
|
2887
|
+
this.#directories = {};
|
|
2888
|
+
for (const name of [
|
|
2889
|
+
"artifacts",
|
|
2890
|
+
"active",
|
|
2891
|
+
"journals",
|
|
2892
|
+
"locks",
|
|
2893
|
+
"state-migrations",
|
|
2894
|
+
"staging",
|
|
2895
|
+
]) {
|
|
2896
|
+
const directory = path.join(this.rootDir, name);
|
|
2897
|
+
this.#directories[name] = this.#initializeDirectory(directory, {
|
|
2898
|
+
parent: tenantRoot,
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
this.#directories = deepFreeze(this.#directories);
|
|
2902
|
+
this.#assertBoundary();
|
|
2903
|
+
this.#assertNoLegacyTenantSchemas();
|
|
2904
|
+
this.#recoverAll();
|
|
2905
|
+
this.#cleanupDebris();
|
|
2788
2906
|
});
|
|
2789
2907
|
}
|
|
2790
|
-
this.#directories = deepFreeze(this.#directories);
|
|
2791
|
-
this.#assertBoundary();
|
|
2792
|
-
this.#assertNoLegacyTenantSchemas();
|
|
2793
|
-
this.#recoverAll();
|
|
2794
|
-
this.#cleanupDebris();
|
|
2795
2908
|
} catch (cause) {
|
|
2909
|
+
if (cause?.code === RRSI_REGISTRY_STORE_POLICY_HOLD_CODE) throw cause;
|
|
2796
2910
|
if (cause instanceof SkillReleaseRegistryError) throw cause;
|
|
2797
2911
|
throw failure(
|
|
2798
2912
|
"SKILL_RELEASE_STORE_UNSAFE",
|
|
@@ -2804,20 +2918,28 @@ export class SkillReleaseRegistry {
|
|
|
2804
2918
|
this,
|
|
2805
2919
|
Object.freeze({
|
|
2806
2920
|
tenantId: ownerTenantId,
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2921
|
+
storePolicyDescriptor: this.#storeBinding?.descriptor ?? null,
|
|
2922
|
+
matchesStorePolicyBinding: (value) =>
|
|
2923
|
+
value === storePolicyBinding && this.#storeBinding !== undefined,
|
|
2924
|
+
// Private reads never dispatch through a genuine subclass or mutable
|
|
2925
|
+
// function.bind property. Constructor recovery uses these paths too.
|
|
2926
|
+
readActive: (name) => this.#readActive(name),
|
|
2927
|
+
readState: (name) => this.#readState(name),
|
|
2928
|
+
readRelease: (value) => this.#readRelease(value),
|
|
2929
|
+
readInventory: () => this.#readInventory(),
|
|
2811
2930
|
matchesTransactionLedger: (value) => value === transactionLedger,
|
|
2812
2931
|
}),
|
|
2813
2932
|
);
|
|
2933
|
+
REGISTRY_WRITERS.set(this, this.#writerControl);
|
|
2814
2934
|
Object.freeze(this);
|
|
2815
2935
|
}
|
|
2816
2936
|
|
|
2817
2937
|
#initializeDirectory(
|
|
2818
2938
|
requestedPath,
|
|
2819
|
-
{ parent = null, recursive = false } = {},
|
|
2939
|
+
{ parent = null, recursive = false, harden = true } = {},
|
|
2820
2940
|
) {
|
|
2941
|
+
this.#assertMutationNamespace();
|
|
2942
|
+
const namespaceTarget = assertOutsideRrsiRegistryNamespace(requestedPath);
|
|
2821
2943
|
const before = lstatOrNull(this.#fs, requestedPath);
|
|
2822
2944
|
if (before && (!before.isDirectory() || before.isSymbolicLink())) {
|
|
2823
2945
|
throw failure(
|
|
@@ -2826,6 +2948,16 @@ export class SkillReleaseRegistry {
|
|
|
2826
2948
|
);
|
|
2827
2949
|
}
|
|
2828
2950
|
if (!before) {
|
|
2951
|
+
if (
|
|
2952
|
+
!samePath(
|
|
2953
|
+
assertOutsideRrsiRegistryNamespace(requestedPath),
|
|
2954
|
+
namespaceTarget,
|
|
2955
|
+
)
|
|
2956
|
+
)
|
|
2957
|
+
throw failure(
|
|
2958
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
2959
|
+
"release initialization ancestor changed",
|
|
2960
|
+
);
|
|
2829
2961
|
this.#fs.mkdirSync(requestedPath, { recursive, mode: 0o700 });
|
|
2830
2962
|
}
|
|
2831
2963
|
let stat = this.#fs.lstatSync(requestedPath);
|
|
@@ -2837,6 +2969,12 @@ export class SkillReleaseRegistry {
|
|
|
2837
2969
|
}
|
|
2838
2970
|
const capturedIdentity = identity(stat);
|
|
2839
2971
|
const canonical = realpath(this.#fs, requestedPath);
|
|
2972
|
+
assertOutsideRrsiRegistryNamespace(canonical);
|
|
2973
|
+
if (!samePath(canonical, namespaceTarget))
|
|
2974
|
+
throw failure(
|
|
2975
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
2976
|
+
"release canonical initialization target changed",
|
|
2977
|
+
);
|
|
2840
2978
|
const canonicalStat = this.#fs.lstatSync(canonical);
|
|
2841
2979
|
const canonicalParent = realpath(this.#fs, path.dirname(requestedPath));
|
|
2842
2980
|
const expectedCanonical = path.join(
|
|
@@ -2857,7 +2995,7 @@ export class SkillReleaseRegistry {
|
|
|
2857
2995
|
"release registry directory escaped its canonical parent",
|
|
2858
2996
|
);
|
|
2859
2997
|
}
|
|
2860
|
-
if (this.#secure) {
|
|
2998
|
+
if (this.#secure && harden) {
|
|
2861
2999
|
ensurePrivateDirectory(canonical, {
|
|
2862
3000
|
applyWindowsAcl: true,
|
|
2863
3001
|
failIfUnavailable: true,
|
|
@@ -2878,6 +3016,18 @@ export class SkillReleaseRegistry {
|
|
|
2878
3016
|
return deepFreeze({ path: canonical, identity: capturedIdentity });
|
|
2879
3017
|
}
|
|
2880
3018
|
|
|
3019
|
+
#assertUnboundNamespace() {
|
|
3020
|
+
if (this.#storeBinding) return;
|
|
3021
|
+
for (const directory of [this.baseDir, this.rootDir])
|
|
3022
|
+
if (directory !== undefined)
|
|
3023
|
+
assertOutsideRrsiRegistryNamespace(directory);
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
#assertMutationNamespace() {
|
|
3027
|
+
this.#storeBinding?.assertMutationAllowed();
|
|
3028
|
+
this.#assertUnboundNamespace();
|
|
3029
|
+
}
|
|
3030
|
+
|
|
2881
3031
|
#assertNoLegacyBaseLayout() {
|
|
2882
3032
|
for (const entry of this.#fs.readdirSync(this.baseDir)) {
|
|
2883
3033
|
if (entry !== "tenants") {
|
|
@@ -2890,6 +3040,7 @@ export class SkillReleaseRegistry {
|
|
|
2890
3040
|
}
|
|
2891
3041
|
|
|
2892
3042
|
#initializeTenantMarker() {
|
|
3043
|
+
this.#assertMutationNamespace();
|
|
2893
3044
|
return withEvolutionFileIdentity(
|
|
2894
3045
|
this.#fs,
|
|
2895
3046
|
this.#markerPath,
|
|
@@ -2899,6 +3050,7 @@ export class SkillReleaseRegistry {
|
|
|
2899
3050
|
}
|
|
2900
3051
|
|
|
2901
3052
|
#initializeTenantMarkerWithIdentity(sameStable) {
|
|
3053
|
+
this.#assertMutationNamespace();
|
|
2902
3054
|
const existing = lstatOrNull(this.#fs, this.#markerPath);
|
|
2903
3055
|
if (existing) {
|
|
2904
3056
|
const verified = this.#readAndVerifyTenantMarker();
|
|
@@ -3002,6 +3154,10 @@ export class SkillReleaseRegistry {
|
|
|
3002
3154
|
}
|
|
3003
3155
|
|
|
3004
3156
|
#readAndVerifyTenantMarker() {
|
|
3157
|
+
if (this.#storeBinding) {
|
|
3158
|
+
this.#storeBinding.recheck();
|
|
3159
|
+
return this.#storeBinding.marker;
|
|
3160
|
+
}
|
|
3005
3161
|
try {
|
|
3006
3162
|
const stored = readBoundedSingleLinkFile(
|
|
3007
3163
|
this.#fs,
|
|
@@ -3053,6 +3209,12 @@ export class SkillReleaseRegistry {
|
|
|
3053
3209
|
}
|
|
3054
3210
|
|
|
3055
3211
|
#assertBoundary() {
|
|
3212
|
+
if (this.#storeBinding) {
|
|
3213
|
+
this.#storeBinding.recheck();
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
this.#assertUnboundNamespace();
|
|
3217
|
+
this.#writerControl?.assertIfWriting();
|
|
3056
3218
|
for (const entry of [
|
|
3057
3219
|
...Object.values(this.#boundaries),
|
|
3058
3220
|
...Object.values(this.#directories),
|
|
@@ -3253,6 +3415,7 @@ export class SkillReleaseRegistry {
|
|
|
3253
3415
|
}
|
|
3254
3416
|
|
|
3255
3417
|
#writeTemporary(fileName, value) {
|
|
3418
|
+
this.#assertMutationNamespace();
|
|
3256
3419
|
if (!TEMP_PATTERN.test(fileName)) {
|
|
3257
3420
|
throw failure("SKILL_RELEASE_STORE_UNSAFE", "temporary name is invalid");
|
|
3258
3421
|
}
|
|
@@ -3263,6 +3426,7 @@ export class SkillReleaseRegistry {
|
|
|
3263
3426
|
}
|
|
3264
3427
|
|
|
3265
3428
|
#writeTemporaryWithIdentity(filePath, value, sameStable) {
|
|
3429
|
+
this.#assertMutationNamespace();
|
|
3266
3430
|
const bytes = serialize(value);
|
|
3267
3431
|
let descriptor = null;
|
|
3268
3432
|
let writtenIdentity = null;
|
|
@@ -3312,6 +3476,7 @@ export class SkillReleaseRegistry {
|
|
|
3312
3476
|
}
|
|
3313
3477
|
|
|
3314
3478
|
#atomicWrite(area, destination, value) {
|
|
3479
|
+
this.#assertMutationNamespace();
|
|
3315
3480
|
const expectedBytes = serialize(value);
|
|
3316
3481
|
const name = `.write-${process.pid}-${this.#token()}.tmp`;
|
|
3317
3482
|
const temporary = this.#writeTemporary(name, value);
|
|
@@ -3357,6 +3522,7 @@ export class SkillReleaseRegistry {
|
|
|
3357
3522
|
}
|
|
3358
3523
|
|
|
3359
3524
|
#unlink(filePath, directory) {
|
|
3525
|
+
this.#assertMutationNamespace();
|
|
3360
3526
|
try {
|
|
3361
3527
|
this.#fs.unlinkSync(filePath);
|
|
3362
3528
|
fsyncDirectory(this.#fs, directory);
|
|
@@ -3366,6 +3532,7 @@ export class SkillReleaseRegistry {
|
|
|
3366
3532
|
}
|
|
3367
3533
|
|
|
3368
3534
|
#publishRelease(release) {
|
|
3535
|
+
this.#assertMutationNamespace();
|
|
3369
3536
|
const destination = this.#releasePath(release.releaseDigest);
|
|
3370
3537
|
const name = `.release-${process.pid}-${this.#token()}.tmp`;
|
|
3371
3538
|
const temporary = this.#writeTemporary(name, release);
|
|
@@ -3377,7 +3544,7 @@ export class SkillReleaseRegistry {
|
|
|
3377
3544
|
this.#fs.linkSync(temporary, destination);
|
|
3378
3545
|
} catch (error) {
|
|
3379
3546
|
if (error?.code !== "EEXIST") throw error;
|
|
3380
|
-
const existing = this
|
|
3547
|
+
const existing = this.#readRelease(release.releaseDigest);
|
|
3381
3548
|
if (!serialize(existing).equals(bytes)) {
|
|
3382
3549
|
throw failure("SKILL_RELEASE_CONFLICT", "release digest collision");
|
|
3383
3550
|
}
|
|
@@ -3428,17 +3595,26 @@ export class SkillReleaseRegistry {
|
|
|
3428
3595
|
}
|
|
3429
3596
|
|
|
3430
3597
|
#createRelease(input) {
|
|
3598
|
+
this.#assertMutationNamespace();
|
|
3431
3599
|
const release = buildSkillRelease(input);
|
|
3432
3600
|
const created = this.#publishRelease(release);
|
|
3433
3601
|
return deepFreeze({ release, created });
|
|
3434
3602
|
}
|
|
3435
3603
|
|
|
3436
3604
|
migrateLegacyRelease(input, migrationAuthority) {
|
|
3605
|
+
this.#assertMutationNamespace();
|
|
3437
3606
|
if (arguments.length !== 2) {
|
|
3438
3607
|
throw migrationRequired(
|
|
3439
3608
|
"legacy release migration requires input and a durable audit authority",
|
|
3440
3609
|
);
|
|
3441
3610
|
}
|
|
3611
|
+
return this.#writerControl.runSync(() =>
|
|
3612
|
+
this.#migrateLegacyRelease(input, migrationAuthority),
|
|
3613
|
+
);
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
#migrateLegacyRelease(input, migrationAuthority) {
|
|
3617
|
+
this.#assertMutationNamespace();
|
|
3442
3618
|
assertExactKeys(
|
|
3443
3619
|
migrationAuthority,
|
|
3444
3620
|
RELEASE_MIGRATION_AUTHORITY_KEYS,
|
|
@@ -3575,11 +3751,19 @@ export class SkillReleaseRegistry {
|
|
|
3575
3751
|
}
|
|
3576
3752
|
|
|
3577
3753
|
migrateLegacyState(planInput, migrationAuthority) {
|
|
3754
|
+
this.#assertMutationNamespace();
|
|
3578
3755
|
if (arguments.length !== 2 || this.#ledgerMigrate === null) {
|
|
3579
3756
|
throw migrationRequired(
|
|
3580
3757
|
"legacy state migration requires a plan, durable authority, and authenticated ledger migration port",
|
|
3581
3758
|
);
|
|
3582
3759
|
}
|
|
3760
|
+
return this.#writerControl.runSync(() =>
|
|
3761
|
+
this.#migrateLegacyState(planInput, migrationAuthority),
|
|
3762
|
+
);
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
#migrateLegacyState(planInput, migrationAuthority) {
|
|
3766
|
+
this.#assertMutationNamespace();
|
|
3583
3767
|
const plan = verifySkillReleaseStateMigrationPlan(planInput);
|
|
3584
3768
|
if (plan.tenantId !== this.tenantId) {
|
|
3585
3769
|
throw migrationRequired("state migration plan belongs to another tenant");
|
|
@@ -3607,8 +3791,8 @@ export class SkillReleaseRegistry {
|
|
|
3607
3791
|
migrationAuthority.handlerArtifactDigest,
|
|
3608
3792
|
"stateMigrationAuthority.handlerArtifactDigest",
|
|
3609
3793
|
);
|
|
3610
|
-
const active = this
|
|
3611
|
-
const lastKnownGood = this
|
|
3794
|
+
const active = this.#readRelease(plan.activeReleaseDigest);
|
|
3795
|
+
const lastKnownGood = this.#readRelease(plan.lastKnownGoodReleaseDigest);
|
|
3612
3796
|
if (
|
|
3613
3797
|
active.skillName !== plan.skillName ||
|
|
3614
3798
|
lastKnownGood.skillName !== plan.skillName ||
|
|
@@ -3681,7 +3865,7 @@ export class SkillReleaseRegistry {
|
|
|
3681
3865
|
{ stateMigrationDigest: plan.stateMigrationDigest },
|
|
3682
3866
|
);
|
|
3683
3867
|
}
|
|
3684
|
-
const existing = this
|
|
3868
|
+
const existing = this.#readState(plan.skillName);
|
|
3685
3869
|
if (existing.revision > 0) {
|
|
3686
3870
|
if (
|
|
3687
3871
|
existing.transactionId !== plan.stateMigrationDigest ||
|
|
@@ -3783,7 +3967,15 @@ export class SkillReleaseRegistry {
|
|
|
3783
3967
|
}
|
|
3784
3968
|
|
|
3785
3969
|
readRelease(releaseDigest) {
|
|
3970
|
+
return this.#readRelease(releaseDigest);
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
#readRelease(releaseDigest) {
|
|
3786
3974
|
const expected = digest(releaseDigest, "releaseDigest");
|
|
3975
|
+
if (this.#runtimeMode === "uninitialized") {
|
|
3976
|
+
this.#assertBoundary();
|
|
3977
|
+
throw failure("SKILL_RELEASE_NOT_FOUND", "release was not found");
|
|
3978
|
+
}
|
|
3787
3979
|
let value;
|
|
3788
3980
|
try {
|
|
3789
3981
|
value = this.#readJson(
|
|
@@ -3819,6 +4011,10 @@ export class SkillReleaseRegistry {
|
|
|
3819
4011
|
|
|
3820
4012
|
#readStateRaw(name) {
|
|
3821
4013
|
const normalizedName = skillName(name);
|
|
4014
|
+
if (this.#runtimeMode === "uninitialized") {
|
|
4015
|
+
this.#assertBoundary();
|
|
4016
|
+
return initialState(normalizedName, this.tenantId);
|
|
4017
|
+
}
|
|
3822
4018
|
try {
|
|
3823
4019
|
const state = verifySkillReleaseState(
|
|
3824
4020
|
this.#readJson(
|
|
@@ -3888,8 +4084,8 @@ export class SkillReleaseRegistry {
|
|
|
3888
4084
|
"active pointer is not finalized by the trusted transaction ledger",
|
|
3889
4085
|
);
|
|
3890
4086
|
}
|
|
3891
|
-
const active = this
|
|
3892
|
-
const lkg = this
|
|
4087
|
+
const active = this.#readRelease(state.activeReleaseDigest);
|
|
4088
|
+
const lkg = this.#readRelease(state.lastKnownGoodReleaseDigest);
|
|
3893
4089
|
if (
|
|
3894
4090
|
active.skillName !== state.skillName ||
|
|
3895
4091
|
lkg.skillName !== state.skillName ||
|
|
@@ -3907,17 +4103,25 @@ export class SkillReleaseRegistry {
|
|
|
3907
4103
|
}
|
|
3908
4104
|
|
|
3909
4105
|
readState(name) {
|
|
4106
|
+
return this.#readState(name);
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
#readState(name) {
|
|
3910
4110
|
const state = this.#readStateRaw(name);
|
|
3911
4111
|
return state.revision === 0 ? state : this.#verifyCommittedState(state);
|
|
3912
4112
|
}
|
|
3913
4113
|
|
|
3914
4114
|
readActive(name) {
|
|
3915
|
-
|
|
4115
|
+
return this.#readActive(name);
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
#readActive(name) {
|
|
4119
|
+
const state = this.#readState(name);
|
|
3916
4120
|
return state.revision === 0
|
|
3917
4121
|
? null
|
|
3918
4122
|
: deepFreeze({
|
|
3919
4123
|
state,
|
|
3920
|
-
release: this
|
|
4124
|
+
release: this.#readRelease(state.activeReleaseDigest),
|
|
3921
4125
|
});
|
|
3922
4126
|
}
|
|
3923
4127
|
|
|
@@ -3981,7 +4185,13 @@ export class SkillReleaseRegistry {
|
|
|
3981
4185
|
}
|
|
3982
4186
|
|
|
3983
4187
|
readInventory() {
|
|
4188
|
+
return this.#readInventory();
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
#readInventory() {
|
|
3984
4192
|
this.#assertBoundary();
|
|
4193
|
+
if (this.#runtimeMode === "uninitialized")
|
|
4194
|
+
return deepFreeze({ active: [], releases: [] });
|
|
3985
4195
|
const releasePattern = /^[a-f0-9]{64}\.json$/u;
|
|
3986
4196
|
const statePattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.json$/u;
|
|
3987
4197
|
const beforeReleases = this.#readInventoryNames(
|
|
@@ -3999,11 +4209,11 @@ export class SkillReleaseRegistry {
|
|
|
3999
4209
|
);
|
|
4000
4210
|
}
|
|
4001
4211
|
const releases = beforeReleases.map((name) =>
|
|
4002
|
-
this
|
|
4212
|
+
this.#readRelease(`sha256:${name.slice(0, -".json".length)}`),
|
|
4003
4213
|
);
|
|
4004
4214
|
const active = beforeStates.map((name) => {
|
|
4005
4215
|
const skill = name.slice(0, -".json".length);
|
|
4006
|
-
const value = this
|
|
4216
|
+
const value = this.#readActive(skill);
|
|
4007
4217
|
if (value === null) {
|
|
4008
4218
|
throw failure(
|
|
4009
4219
|
"SKILL_RELEASE_STATE_CORRUPT",
|
|
@@ -4041,7 +4251,7 @@ export class SkillReleaseRegistry {
|
|
|
4041
4251
|
const afterReleases = this.#readInventoryNames("artifacts", releasePattern);
|
|
4042
4252
|
const afterStates = this.#readInventoryNames("active", statePattern);
|
|
4043
4253
|
const afterStateDigests = afterStates.map(
|
|
4044
|
-
(name) => this
|
|
4254
|
+
(name) => this.#readState(name.slice(0, -".json".length)).stateDigest,
|
|
4045
4255
|
);
|
|
4046
4256
|
if (
|
|
4047
4257
|
canonicalJson(beforeReleases) !== canonicalJson(afterReleases) ||
|
|
@@ -4060,7 +4270,7 @@ export class SkillReleaseRegistry {
|
|
|
4060
4270
|
}
|
|
4061
4271
|
|
|
4062
4272
|
pinActive(name) {
|
|
4063
|
-
const active = this
|
|
4273
|
+
const active = this.#readActive(name);
|
|
4064
4274
|
if (!active)
|
|
4065
4275
|
throw failure("SKILL_RELEASE_NOT_ACTIVE", "Skill has no active release");
|
|
4066
4276
|
const pin = Object.freeze(Object.create(null));
|
|
@@ -4094,7 +4304,7 @@ export class SkillReleaseRegistry {
|
|
|
4094
4304
|
"pinned activation is not committed",
|
|
4095
4305
|
);
|
|
4096
4306
|
}
|
|
4097
|
-
return this
|
|
4307
|
+
return this.#readRelease(binding.releaseDigest);
|
|
4098
4308
|
}
|
|
4099
4309
|
|
|
4100
4310
|
#owner(kind, token, { skill = null, transactionId = null, fence = 0 } = {}) {
|
|
@@ -4210,6 +4420,7 @@ export class SkillReleaseRegistry {
|
|
|
4210
4420
|
}
|
|
4211
4421
|
|
|
4212
4422
|
#createLock(lockPath, owner) {
|
|
4423
|
+
this.#assertMutationNamespace();
|
|
4213
4424
|
this.#fs.mkdirSync(lockPath, { mode: 0o700 });
|
|
4214
4425
|
let descriptor = null;
|
|
4215
4426
|
try {
|
|
@@ -4239,6 +4450,7 @@ export class SkillReleaseRegistry {
|
|
|
4239
4450
|
}
|
|
4240
4451
|
|
|
4241
4452
|
#removeLock(lockPath, expectedToken = null) {
|
|
4453
|
+
this.#assertMutationNamespace();
|
|
4242
4454
|
let owner;
|
|
4243
4455
|
try {
|
|
4244
4456
|
owner = this.#readOwner(lockPath);
|
|
@@ -4300,6 +4512,7 @@ export class SkillReleaseRegistry {
|
|
|
4300
4512
|
}
|
|
4301
4513
|
|
|
4302
4514
|
#renewLease(lease) {
|
|
4515
|
+
this.#assertMutationNamespace();
|
|
4303
4516
|
const current = this.#readOwner(lease.lockPath);
|
|
4304
4517
|
if (
|
|
4305
4518
|
current.token !== lease.token ||
|
|
@@ -4338,6 +4551,7 @@ export class SkillReleaseRegistry {
|
|
|
4338
4551
|
}
|
|
4339
4552
|
|
|
4340
4553
|
#acquireLease(name, transactionId, expectedRevision, expectedParentDigest) {
|
|
4554
|
+
this.#assertMutationNamespace();
|
|
4341
4555
|
const journalPath = this.#journalPath(name);
|
|
4342
4556
|
if (this.#exists(journalPath)) {
|
|
4343
4557
|
this.#withRecoveryLock(() => this.#recoverSkill(name));
|
|
@@ -4350,11 +4564,11 @@ export class SkillReleaseRegistry {
|
|
|
4350
4564
|
}
|
|
4351
4565
|
const lockPath = this.#leasePath(name);
|
|
4352
4566
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4353
|
-
const state = this
|
|
4567
|
+
const state = this.#readState(name);
|
|
4354
4568
|
const active =
|
|
4355
4569
|
state.activeReleaseDigest === null
|
|
4356
4570
|
? null
|
|
4357
|
-
: this
|
|
4571
|
+
: this.#readRelease(state.activeReleaseDigest);
|
|
4358
4572
|
const actualParent = active?.contentDigest ?? EMPTY_SKILL_ACTIVE_DIGEST;
|
|
4359
4573
|
if (
|
|
4360
4574
|
state.revision !== expectedRevision ||
|
|
@@ -4805,6 +5019,7 @@ export class SkillReleaseRegistry {
|
|
|
4805
5019
|
}
|
|
4806
5020
|
|
|
4807
5021
|
#prepare(intent, targetRelease) {
|
|
5022
|
+
this.#assertMutationNamespace();
|
|
4808
5023
|
let projection;
|
|
4809
5024
|
try {
|
|
4810
5025
|
projection = this.#ledgerPrepare(intent, targetRelease);
|
|
@@ -4836,6 +5051,7 @@ export class SkillReleaseRegistry {
|
|
|
4836
5051
|
}
|
|
4837
5052
|
|
|
4838
5053
|
#finalize(journal, prepareReceipt) {
|
|
5054
|
+
this.#assertMutationNamespace();
|
|
4839
5055
|
const request = deepFreeze({
|
|
4840
5056
|
authorityReceiptDigest: journal.intent.authorityReceiptDigest,
|
|
4841
5057
|
expectedPrepareReceiptDigest: prepareReceipt.receiptDigest,
|
|
@@ -4882,6 +5098,7 @@ export class SkillReleaseRegistry {
|
|
|
4882
5098
|
}
|
|
4883
5099
|
|
|
4884
5100
|
#writePointer(state, stagedFile) {
|
|
5101
|
+
this.#assertMutationNamespace();
|
|
4885
5102
|
const stagedPath = this.#path("staging", stagedFile);
|
|
4886
5103
|
if (!this.#exists(stagedPath)) this.#writeTemporary(stagedFile, state);
|
|
4887
5104
|
const expectedBytes = serialize(state);
|
|
@@ -4994,7 +5211,7 @@ export class SkillReleaseRegistry {
|
|
|
4994
5211
|
);
|
|
4995
5212
|
}
|
|
4996
5213
|
|
|
4997
|
-
const verifiedRelease = this
|
|
5214
|
+
const verifiedRelease = this.#readRelease(release.releaseDigest);
|
|
4998
5215
|
if (
|
|
4999
5216
|
verifiedRelease.tenantId !== this.tenantId ||
|
|
5000
5217
|
verifiedRelease.skillName !== state.skillName ||
|
|
@@ -5022,7 +5239,7 @@ export class SkillReleaseRegistry {
|
|
|
5022
5239
|
#assertJournalFinalizationArtifacts(journal) {
|
|
5023
5240
|
let release;
|
|
5024
5241
|
try {
|
|
5025
|
-
release = this
|
|
5242
|
+
release = this.#readRelease(journal.nextState.activeReleaseDigest);
|
|
5026
5243
|
} catch (cause) {
|
|
5027
5244
|
throw failure(
|
|
5028
5245
|
"SKILL_RELEASE_FINALIZATION_INPUT_INVALID",
|
|
@@ -5091,6 +5308,7 @@ export class SkillReleaseRegistry {
|
|
|
5091
5308
|
}
|
|
5092
5309
|
|
|
5093
5310
|
#recoverJournal(journal) {
|
|
5311
|
+
this.#assertMutationNamespace();
|
|
5094
5312
|
const expected = {
|
|
5095
5313
|
authorityReceiptDigest: journal.intent.authorityReceiptDigest,
|
|
5096
5314
|
intentDigest: journal.intent.intentDigest,
|
|
@@ -5132,6 +5350,7 @@ export class SkillReleaseRegistry {
|
|
|
5132
5350
|
}
|
|
5133
5351
|
|
|
5134
5352
|
#recoverSkill(name, { force = false } = {}) {
|
|
5353
|
+
this.#assertMutationNamespace();
|
|
5135
5354
|
const journalPath = this.#journalPath(name);
|
|
5136
5355
|
const lockPath = this.#leasePath(name);
|
|
5137
5356
|
if (!this.#exists(journalPath)) {
|
|
@@ -5174,6 +5393,7 @@ export class SkillReleaseRegistry {
|
|
|
5174
5393
|
}
|
|
5175
5394
|
|
|
5176
5395
|
#recoverStateMigration(journal, { force = false } = {}) {
|
|
5396
|
+
this.#assertMutationNamespace();
|
|
5177
5397
|
const lockPath = this.#leasePath(journal.skillName);
|
|
5178
5398
|
if (this.#exists(lockPath)) {
|
|
5179
5399
|
const owner = this.#readOwner(lockPath);
|
|
@@ -5222,13 +5442,14 @@ export class SkillReleaseRegistry {
|
|
|
5222
5442
|
journal.state,
|
|
5223
5443
|
);
|
|
5224
5444
|
}
|
|
5225
|
-
const release = this
|
|
5445
|
+
const release = this.#readRelease(journal.state.activeReleaseDigest);
|
|
5226
5446
|
this.#assertFinalizationArtifacts(journal.state, release);
|
|
5227
5447
|
this.#cleanupStateMigration(journal);
|
|
5228
5448
|
return "committed";
|
|
5229
5449
|
}
|
|
5230
5450
|
|
|
5231
5451
|
#recoverAll() {
|
|
5452
|
+
this.#assertMutationNamespace();
|
|
5232
5453
|
this.#withRecoveryLock(() => {
|
|
5233
5454
|
for (const name of this.#fs.readdirSync(
|
|
5234
5455
|
this.#directories["state-migrations"].path,
|
|
@@ -5275,6 +5496,7 @@ export class SkillReleaseRegistry {
|
|
|
5275
5496
|
}
|
|
5276
5497
|
|
|
5277
5498
|
#cleanupDebris() {
|
|
5499
|
+
this.#assertMutationNamespace();
|
|
5278
5500
|
const referenced = new Set();
|
|
5279
5501
|
for (const name of this.#fs.readdirSync(this.#directories.journals.path)) {
|
|
5280
5502
|
if (name.endsWith(".json")) {
|
|
@@ -5463,6 +5685,14 @@ export class SkillReleaseRegistry {
|
|
|
5463
5685
|
}
|
|
5464
5686
|
|
|
5465
5687
|
async applyTransition(capability) {
|
|
5688
|
+
this.#assertMutationNamespace();
|
|
5689
|
+
return this.#writerControl.runAsync(() =>
|
|
5690
|
+
this.#applyTransition(capability),
|
|
5691
|
+
);
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
async #applyTransition(capability) {
|
|
5695
|
+
this.#assertMutationNamespace();
|
|
5466
5696
|
const payload = this.#validateTransitionPayload(
|
|
5467
5697
|
consumeRegistryTransitionCapability(capability, this),
|
|
5468
5698
|
);
|
|
@@ -5477,7 +5707,7 @@ export class SkillReleaseRegistry {
|
|
|
5477
5707
|
target = created.release;
|
|
5478
5708
|
releaseCreated = created.created;
|
|
5479
5709
|
} else {
|
|
5480
|
-
target = this
|
|
5710
|
+
target = this.#readRelease(payload.targetReleaseDigest);
|
|
5481
5711
|
}
|
|
5482
5712
|
if (
|
|
5483
5713
|
target.skillName !== payload.skillName ||
|