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
|
@@ -5,6 +5,12 @@ import { types as utilTypes } from "node:util";
|
|
|
5
5
|
import { getHomeDir } from "../paths.js";
|
|
6
6
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
7
7
|
import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
|
|
8
|
+
import { createSkillRegistryWriterControl } from "./skill-registry-writer-control.js";
|
|
9
|
+
import {
|
|
10
|
+
captureRrsiRegistryComponentBinding,
|
|
11
|
+
assertOutsideRrsiRegistryNamespace,
|
|
12
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
13
|
+
} from "./rrsi-registry-store-policy.js";
|
|
8
14
|
import {
|
|
9
15
|
verifySkillDependencyLock,
|
|
10
16
|
verifySkillRuntimeManifest,
|
|
@@ -171,6 +177,7 @@ const REGISTRY_OPTION_KEYS = new Set([
|
|
|
171
177
|
"randomToken",
|
|
172
178
|
"rootDir",
|
|
173
179
|
"secure",
|
|
180
|
+
"storePolicyBinding",
|
|
174
181
|
"tenantId",
|
|
175
182
|
"targetMatrixAdmissionAuthority",
|
|
176
183
|
]);
|
|
@@ -1519,6 +1526,11 @@ function normalizeRegistryOptions(options) {
|
|
|
1519
1526
|
randomToken,
|
|
1520
1527
|
rootDir,
|
|
1521
1528
|
secure,
|
|
1529
|
+
storePolicyBinding: ownData(
|
|
1530
|
+
options,
|
|
1531
|
+
"storePolicyBinding",
|
|
1532
|
+
"candidate registry options",
|
|
1533
|
+
),
|
|
1522
1534
|
targetMatrixAdmissionAuthority,
|
|
1523
1535
|
tenantId,
|
|
1524
1536
|
};
|
|
@@ -1531,6 +1543,7 @@ function normalizeRegistryOptions(options) {
|
|
|
1531
1543
|
* promotion controller can consume verified drafts through read().
|
|
1532
1544
|
*/
|
|
1533
1545
|
const CANDIDATE_READERS = new WeakMap();
|
|
1546
|
+
const CANDIDATE_WRITERS = new WeakMap();
|
|
1534
1547
|
|
|
1535
1548
|
export function captureSkillCandidateRegistryReader(registry) {
|
|
1536
1549
|
const reader = CANDIDATE_READERS.get(registry);
|
|
@@ -1543,16 +1556,48 @@ export function captureSkillCandidateRegistryReader(registry) {
|
|
|
1543
1556
|
return reader;
|
|
1544
1557
|
}
|
|
1545
1558
|
|
|
1559
|
+
export function captureSkillCandidateRegistryWriterControl(registry) {
|
|
1560
|
+
captureSkillCandidateRegistryReader(registry);
|
|
1561
|
+
const control = CANDIDATE_WRITERS.get(registry);
|
|
1562
|
+
if (!control)
|
|
1563
|
+
throw new TypeError(
|
|
1564
|
+
"a genuine candidate Registry writer control is required",
|
|
1565
|
+
);
|
|
1566
|
+
return control;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1546
1569
|
export class SkillCandidateRegistry {
|
|
1570
|
+
#writerControl;
|
|
1571
|
+
#storeBinding;
|
|
1547
1572
|
constructor(options) {
|
|
1548
1573
|
const {
|
|
1549
1574
|
fsImpl,
|
|
1550
1575
|
randomToken,
|
|
1551
1576
|
rootDir,
|
|
1552
1577
|
secure,
|
|
1578
|
+
storePolicyBinding,
|
|
1553
1579
|
targetMatrixAdmissionAuthority,
|
|
1554
1580
|
tenantId,
|
|
1555
1581
|
} = normalizeRegistryOptions(options);
|
|
1582
|
+
const requestedBase = path.resolve(rootDir);
|
|
1583
|
+
if (storePolicyBinding !== undefined) {
|
|
1584
|
+
this.#storeBinding =
|
|
1585
|
+
captureRrsiRegistryComponentBinding(storePolicyBinding);
|
|
1586
|
+
const descriptor = this.#storeBinding.descriptor;
|
|
1587
|
+
if (
|
|
1588
|
+
new.target !== SkillCandidateRegistry ||
|
|
1589
|
+
fsImpl !== fs ||
|
|
1590
|
+
secure !== true ||
|
|
1591
|
+
descriptor.component !== TENANT_MARKER_COMPONENT ||
|
|
1592
|
+
descriptor.tenantId !== tenantId ||
|
|
1593
|
+
!samePath(descriptor.baseDir, requestedBase)
|
|
1594
|
+
)
|
|
1595
|
+
throw registryError(
|
|
1596
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
1597
|
+
"candidate v2 binding or native storage options differ",
|
|
1598
|
+
);
|
|
1599
|
+
this.#storeBinding.recheckForOpen();
|
|
1600
|
+
} else assertOutsideRrsiRegistryNamespace(requestedBase);
|
|
1556
1601
|
this._fs = fsImpl;
|
|
1557
1602
|
this._secure = secure;
|
|
1558
1603
|
this._randomToken = randomToken;
|
|
@@ -1561,29 +1606,72 @@ export class SkillCandidateRegistry {
|
|
|
1561
1606
|
targetMatrixAdmissionAuthority.descriptor;
|
|
1562
1607
|
this.tenantId = tenantId;
|
|
1563
1608
|
this.tenantKey = deriveSkillCandidateTenantKey(tenantId);
|
|
1564
|
-
const requestedBase = path.resolve(rootDir);
|
|
1565
1609
|
try {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1610
|
+
if (this.#storeBinding) {
|
|
1611
|
+
this._directories = this.#storeBinding.boundaries;
|
|
1612
|
+
this.baseDir = this._directories.base.path;
|
|
1613
|
+
this.rootDir = this._directories.tenantRoot.path;
|
|
1614
|
+
this._markerPath = path.join(this.rootDir, TENANT_MARKER_FILE);
|
|
1615
|
+
this._markerIdentity = this.#storeBinding.marker.identity;
|
|
1616
|
+
this._assertBoundary();
|
|
1617
|
+
} else {
|
|
1618
|
+
const base = this._initializeDirectory(requestedBase, {
|
|
1619
|
+
recursive: true,
|
|
1620
|
+
harden: false,
|
|
1621
|
+
});
|
|
1622
|
+
this.baseDir = base.path;
|
|
1623
|
+
this.#assertUnboundNamespace();
|
|
1624
|
+
this._assertNoLegacyBaseLayout();
|
|
1625
|
+
const tenants = this._initializeDirectory(
|
|
1626
|
+
path.join(this.baseDir, "tenants"),
|
|
1627
|
+
{ parent: base, harden: false },
|
|
1628
|
+
);
|
|
1629
|
+
const tenantRoot = this._initializeDirectory(
|
|
1630
|
+
path.join(tenants.path, this.tenantKey),
|
|
1631
|
+
{ parent: tenants, harden: false },
|
|
1632
|
+
);
|
|
1633
|
+
this.rootDir = tenantRoot.path;
|
|
1634
|
+
this.#assertUnboundNamespace();
|
|
1635
|
+
this._directories = deepFreeze({ base, tenantRoot, tenants });
|
|
1636
|
+
this._assertDirectories();
|
|
1637
|
+
this._markerPath = path.join(this.rootDir, TENANT_MARKER_FILE);
|
|
1638
|
+
this.#writerControl = createSkillRegistryWriterControl({
|
|
1639
|
+
rootDir: this.rootDir,
|
|
1640
|
+
tenantId: this.tenantId,
|
|
1641
|
+
component: TENANT_MARKER_COMPONENT,
|
|
1642
|
+
fsImpl: this._fs,
|
|
1643
|
+
});
|
|
1644
|
+
this.#writerControl.runSync(() => {
|
|
1645
|
+
// Bootstrap may create missing directories; existing-path ACL repair
|
|
1646
|
+
// waits for writer exclusion and must preserve the captured identities.
|
|
1647
|
+
if (this._secure) {
|
|
1648
|
+
for (const [entry, parent] of [
|
|
1649
|
+
[base, null],
|
|
1650
|
+
[tenants, base],
|
|
1651
|
+
[tenantRoot, tenants],
|
|
1652
|
+
]) {
|
|
1653
|
+
this.#writerControl.assertIfWriting();
|
|
1654
|
+
const hardened = this._initializeDirectory(entry.path, {
|
|
1655
|
+
parent,
|
|
1656
|
+
});
|
|
1657
|
+
if (
|
|
1658
|
+
hardened.identity !== entry.identity ||
|
|
1659
|
+
!samePath(hardened.path, entry.path)
|
|
1660
|
+
)
|
|
1661
|
+
throw registryError(
|
|
1662
|
+
"SKILL_CANDIDATE_STORE_UNSAFE",
|
|
1663
|
+
"candidate bootstrap identity changed before permission hardening",
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
this.#writerControl.assertIfWriting();
|
|
1668
|
+
this._initializeTenantMarker();
|
|
1669
|
+
this._assertBoundary();
|
|
1670
|
+
this._assertNoMixedTenantArtifacts();
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1586
1673
|
} catch (error) {
|
|
1674
|
+
if (error?.code === RRSI_REGISTRY_STORE_POLICY_HOLD_CODE) throw error;
|
|
1587
1675
|
if (error instanceof SkillCandidateRegistryError) throw error;
|
|
1588
1676
|
throw registryError(
|
|
1589
1677
|
"SKILL_CANDIDATE_STORE_UNSAFE",
|
|
@@ -1596,18 +1684,31 @@ export class SkillCandidateRegistry {
|
|
|
1596
1684
|
this,
|
|
1597
1685
|
Object.freeze({
|
|
1598
1686
|
tenantId,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1687
|
+
storePolicyDescriptor: this.#storeBinding?.descriptor ?? null,
|
|
1688
|
+
matchesStorePolicyBinding: Object.freeze(
|
|
1689
|
+
(value) =>
|
|
1690
|
+
value === storePolicyBinding && this.#storeBinding !== undefined,
|
|
1602
1691
|
),
|
|
1692
|
+
read: Object.freeze((value) => this.#read(value)),
|
|
1693
|
+
readInventory: Object.freeze(() => this.#readInventory()),
|
|
1603
1694
|
}),
|
|
1604
1695
|
);
|
|
1696
|
+
CANDIDATE_WRITERS.set(this, this.#writerControl);
|
|
1605
1697
|
}
|
|
1606
1698
|
|
|
1607
1699
|
_initializeDirectory(
|
|
1608
1700
|
requestedPath,
|
|
1609
|
-
{ parent = null, recursive = false } = {},
|
|
1701
|
+
{ parent = null, recursive = false, harden = true } = {},
|
|
1610
1702
|
) {
|
|
1703
|
+
if (this.#storeBinding) {
|
|
1704
|
+
this.#storeBinding.recheck();
|
|
1705
|
+
const entry = Object.values(this.#storeBinding.boundaries).find((value) =>
|
|
1706
|
+
samePath(value.path, path.resolve(requestedPath)),
|
|
1707
|
+
);
|
|
1708
|
+
if (!entry) this.#storeBinding.assertMutationAllowed();
|
|
1709
|
+
return entry;
|
|
1710
|
+
}
|
|
1711
|
+
const namespaceTarget = assertOutsideRrsiRegistryNamespace(requestedPath);
|
|
1611
1712
|
const before = lstatOrNull(this._fs, requestedPath);
|
|
1612
1713
|
if (before && (!before.isDirectory() || before.isSymbolicLink())) {
|
|
1613
1714
|
throw registryError(
|
|
@@ -1616,6 +1717,16 @@ export class SkillCandidateRegistry {
|
|
|
1616
1717
|
);
|
|
1617
1718
|
}
|
|
1618
1719
|
if (!before) {
|
|
1720
|
+
if (
|
|
1721
|
+
!samePath(
|
|
1722
|
+
assertOutsideRrsiRegistryNamespace(requestedPath),
|
|
1723
|
+
namespaceTarget,
|
|
1724
|
+
)
|
|
1725
|
+
)
|
|
1726
|
+
throw registryError(
|
|
1727
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
1728
|
+
"candidate initialization ancestor changed",
|
|
1729
|
+
);
|
|
1619
1730
|
this._fs.mkdirSync(requestedPath, { recursive, mode: 0o700 });
|
|
1620
1731
|
}
|
|
1621
1732
|
let stat = this._fs.lstatSync(requestedPath);
|
|
@@ -1627,6 +1738,12 @@ export class SkillCandidateRegistry {
|
|
|
1627
1738
|
}
|
|
1628
1739
|
const capturedIdentity = entryIdentity(stat);
|
|
1629
1740
|
const canonical = realpath(this._fs, requestedPath);
|
|
1741
|
+
assertOutsideRrsiRegistryNamespace(canonical);
|
|
1742
|
+
if (!samePath(canonical, namespaceTarget))
|
|
1743
|
+
throw registryError(
|
|
1744
|
+
RRSI_REGISTRY_STORE_POLICY_HOLD_CODE,
|
|
1745
|
+
"candidate canonical initialization target changed",
|
|
1746
|
+
);
|
|
1630
1747
|
const canonicalStat = this._fs.lstatSync(canonical);
|
|
1631
1748
|
const canonicalParent = realpath(this._fs, path.dirname(requestedPath));
|
|
1632
1749
|
const expectedCanonical = path.join(
|
|
@@ -1647,7 +1764,7 @@ export class SkillCandidateRegistry {
|
|
|
1647
1764
|
"candidate registry directory escaped its canonical parent",
|
|
1648
1765
|
);
|
|
1649
1766
|
}
|
|
1650
|
-
if (this._secure) {
|
|
1767
|
+
if (this._secure && harden) {
|
|
1651
1768
|
ensurePrivateDirectory(canonical, {
|
|
1652
1769
|
applyWindowsAcl: true,
|
|
1653
1770
|
failIfUnavailable: true,
|
|
@@ -1668,6 +1785,18 @@ export class SkillCandidateRegistry {
|
|
|
1668
1785
|
return deepFreeze({ path: canonical, identity: capturedIdentity });
|
|
1669
1786
|
}
|
|
1670
1787
|
|
|
1788
|
+
#assertUnboundNamespace() {
|
|
1789
|
+
if (this.#storeBinding) return;
|
|
1790
|
+
for (const directory of [this.baseDir, this.rootDir])
|
|
1791
|
+
if (directory !== undefined)
|
|
1792
|
+
assertOutsideRrsiRegistryNamespace(directory);
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
#assertMutationNamespace() {
|
|
1796
|
+
this.#storeBinding?.assertMutationAllowed();
|
|
1797
|
+
this.#assertUnboundNamespace();
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1671
1800
|
_assertNoLegacyBaseLayout() {
|
|
1672
1801
|
const legacyName = this._readDirectoryEntryNamesBounded(
|
|
1673
1802
|
this.baseDir,
|
|
@@ -1794,6 +1923,8 @@ export class SkillCandidateRegistry {
|
|
|
1794
1923
|
}
|
|
1795
1924
|
|
|
1796
1925
|
_initializeTenantMarker() {
|
|
1926
|
+
if (this.#storeBinding) return this._readAndVerifyTenantMarker();
|
|
1927
|
+
this.#assertUnboundNamespace();
|
|
1797
1928
|
return withEvolutionFileIdentity(
|
|
1798
1929
|
this._fs,
|
|
1799
1930
|
this._markerPath,
|
|
@@ -1803,6 +1934,7 @@ export class SkillCandidateRegistry {
|
|
|
1803
1934
|
}
|
|
1804
1935
|
|
|
1805
1936
|
#initializeTenantMarkerWithIdentity(sameStable) {
|
|
1937
|
+
this.#assertMutationNamespace();
|
|
1806
1938
|
const existing = lstatOrNull(this._fs, this._markerPath);
|
|
1807
1939
|
if (existing) {
|
|
1808
1940
|
const verified = this._readAndVerifyTenantMarker();
|
|
@@ -1980,6 +2112,10 @@ export class SkillCandidateRegistry {
|
|
|
1980
2112
|
}
|
|
1981
2113
|
|
|
1982
2114
|
_readAndVerifyTenantMarker() {
|
|
2115
|
+
if (this.#storeBinding) {
|
|
2116
|
+
this.#storeBinding.recheck();
|
|
2117
|
+
return this.#storeBinding.marker;
|
|
2118
|
+
}
|
|
1983
2119
|
let stored;
|
|
1984
2120
|
try {
|
|
1985
2121
|
stored = this._readBoundedRegularFile(
|
|
@@ -2014,6 +2150,12 @@ export class SkillCandidateRegistry {
|
|
|
2014
2150
|
}
|
|
2015
2151
|
|
|
2016
2152
|
_assertBoundary() {
|
|
2153
|
+
if (this.#storeBinding) {
|
|
2154
|
+
this.#storeBinding.recheck();
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
this.#assertUnboundNamespace();
|
|
2158
|
+
this.#writerControl?.assertIfWriting();
|
|
2017
2159
|
this._assertDirectories();
|
|
2018
2160
|
const verified = this._readAndVerifyTenantMarker();
|
|
2019
2161
|
if (
|
|
@@ -2179,11 +2321,23 @@ export class SkillCandidateRegistry {
|
|
|
2179
2321
|
}
|
|
2180
2322
|
|
|
2181
2323
|
migrateLegacy(legacyCandidate, executionArtifacts, migrationAuthority) {
|
|
2324
|
+
this.#assertMutationNamespace();
|
|
2182
2325
|
if (arguments.length !== 3) {
|
|
2183
2326
|
throw migrationRequired(
|
|
2184
2327
|
"legacy migration requires candidate, execution artifacts, and a durable audit authority",
|
|
2185
2328
|
);
|
|
2186
2329
|
}
|
|
2330
|
+
return this.#writerControl.runSync(() =>
|
|
2331
|
+
this.#migrateLegacy(
|
|
2332
|
+
legacyCandidate,
|
|
2333
|
+
executionArtifacts,
|
|
2334
|
+
migrationAuthority,
|
|
2335
|
+
),
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
#migrateLegacy(legacyCandidate, executionArtifacts, migrationAuthority) {
|
|
2340
|
+
this.#assertMutationNamespace();
|
|
2187
2341
|
const legacy = verifyLegacySkillCandidateDraft(legacyCandidate);
|
|
2188
2342
|
assertDataRecord(
|
|
2189
2343
|
executionArtifacts,
|
|
@@ -2268,7 +2422,7 @@ export class SkillCandidateRegistry {
|
|
|
2268
2422
|
);
|
|
2269
2423
|
}
|
|
2270
2424
|
|
|
2271
|
-
const publication = this
|
|
2425
|
+
const publication = this.#create({
|
|
2272
2426
|
tenantId: this.tenantId,
|
|
2273
2427
|
skillName: legacy.skillName,
|
|
2274
2428
|
parentDigest: legacy.parentDigest,
|
|
@@ -2407,6 +2561,7 @@ export class SkillCandidateRegistry {
|
|
|
2407
2561
|
resolveExecutionArtifacts,
|
|
2408
2562
|
migrationAuthority,
|
|
2409
2563
|
) {
|
|
2564
|
+
this.#assertMutationNamespace();
|
|
2410
2565
|
if (
|
|
2411
2566
|
arguments.length !== 3 ||
|
|
2412
2567
|
typeof resolveExecutionArtifacts !== "function"
|
|
@@ -2415,6 +2570,21 @@ export class SkillCandidateRegistry {
|
|
|
2415
2570
|
"legacy store migration requires one source root, one execution resolver, and one audit authority",
|
|
2416
2571
|
);
|
|
2417
2572
|
}
|
|
2573
|
+
return this.#writerControl.runSync(() =>
|
|
2574
|
+
this.#migrateLegacyStore(
|
|
2575
|
+
sourceRootDir,
|
|
2576
|
+
resolveExecutionArtifacts,
|
|
2577
|
+
migrationAuthority,
|
|
2578
|
+
),
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
#migrateLegacyStore(
|
|
2583
|
+
sourceRootDir,
|
|
2584
|
+
resolveExecutionArtifacts,
|
|
2585
|
+
migrationAuthority,
|
|
2586
|
+
) {
|
|
2587
|
+
this.#assertMutationNamespace();
|
|
2418
2588
|
assertDataRecord(
|
|
2419
2589
|
migrationAuthority,
|
|
2420
2590
|
MIGRATION_AUTHORITY_KEYS,
|
|
@@ -2599,7 +2769,7 @@ export class SkillCandidateRegistry {
|
|
|
2599
2769
|
},
|
|
2600
2770
|
);
|
|
2601
2771
|
}
|
|
2602
|
-
const migrated = this
|
|
2772
|
+
const migrated = this.#migrateLegacy(
|
|
2603
2773
|
source.legacy,
|
|
2604
2774
|
executionArtifacts,
|
|
2605
2775
|
capturedMigrationAuthority,
|
|
@@ -2658,12 +2828,18 @@ export class SkillCandidateRegistry {
|
|
|
2658
2828
|
}
|
|
2659
2829
|
|
|
2660
2830
|
create(input) {
|
|
2831
|
+
this.#assertMutationNamespace();
|
|
2661
2832
|
if (arguments.length !== 1) {
|
|
2662
2833
|
throw registryError(
|
|
2663
2834
|
"SKILL_CANDIDATE_INVALID",
|
|
2664
2835
|
"candidate create accepts only input; admission context is registry-owned",
|
|
2665
2836
|
);
|
|
2666
2837
|
}
|
|
2838
|
+
return this.#writerControl.runSync(() => this.#create(input));
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
#create(input) {
|
|
2842
|
+
this.#assertMutationNamespace();
|
|
2667
2843
|
return withEvolutionFileIdentity(
|
|
2668
2844
|
this._fs,
|
|
2669
2845
|
this._markerPath,
|
|
@@ -2672,6 +2848,7 @@ export class SkillCandidateRegistry {
|
|
|
2672
2848
|
}
|
|
2673
2849
|
|
|
2674
2850
|
#createWithIdentity(input, sameStable) {
|
|
2851
|
+
this.#assertMutationNamespace();
|
|
2675
2852
|
this._assertNoMixedTenantArtifacts();
|
|
2676
2853
|
const verificationContext = this._resolveAdmissionContext(input);
|
|
2677
2854
|
const candidate = buildSkillCandidateDraft(input, verificationContext);
|
|
@@ -2879,12 +3056,18 @@ export class SkillCandidateRegistry {
|
|
|
2879
3056
|
}
|
|
2880
3057
|
|
|
2881
3058
|
read(candidateId) {
|
|
3059
|
+
return this.#read(candidateId);
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
#read(candidateId) {
|
|
2882
3063
|
const normalizedId = normalizeCandidateId(candidateId);
|
|
3064
|
+
this.#storeBinding?.recheck();
|
|
2883
3065
|
const filePath = this._candidatePath(normalizedId);
|
|
2884
3066
|
let bytes;
|
|
2885
3067
|
try {
|
|
2886
3068
|
bytes = this._readBytes(filePath);
|
|
2887
3069
|
} catch (error) {
|
|
3070
|
+
if (error?.code === RRSI_REGISTRY_STORE_POLICY_HOLD_CODE) throw error;
|
|
2888
3071
|
if (error instanceof SkillCandidateRegistryError) throw error;
|
|
2889
3072
|
if (error?.code === "ENOENT") {
|
|
2890
3073
|
throw registryError(
|
|
@@ -2955,12 +3138,16 @@ export class SkillCandidateRegistry {
|
|
|
2955
3138
|
}
|
|
2956
3139
|
|
|
2957
3140
|
readInventory() {
|
|
3141
|
+
return this.#readInventory();
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
#readInventory() {
|
|
2958
3145
|
const before = this._assertNoMixedTenantArtifacts()
|
|
2959
3146
|
.filter((name) => CANDIDATE_FILE_PATTERN.test(name))
|
|
2960
3147
|
.sort();
|
|
2961
3148
|
const candidates = before.map((name) => {
|
|
2962
3149
|
const match = CANDIDATE_FILE_PATTERN.exec(name);
|
|
2963
|
-
return this
|
|
3150
|
+
return this.#read(`sha256:${match[1]}`);
|
|
2964
3151
|
});
|
|
2965
3152
|
const after = this._assertNoMixedTenantArtifacts()
|
|
2966
3153
|
.filter((name) => CANDIDATE_FILE_PATTERN.test(name))
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Live exclusion of participating Registry writers; no legacy drain or origin admission. */
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
import { captureSkillCandidateRegistryWriterControl } from "./skill-candidate-registry.js";
|
|
4
|
+
import { captureSkillReleaseRegistryWriterControl } from "./skill-release-registry.js";
|
|
5
|
+
|
|
6
|
+
export async function withSkillRegistryMaintenance(input, operation) {
|
|
7
|
+
const names = ["candidateRegistry", "releaseRegistry"];
|
|
8
|
+
if (
|
|
9
|
+
!input ||
|
|
10
|
+
utilTypes.isProxy(input) ||
|
|
11
|
+
Object.getPrototypeOf(input) !== Object.prototype ||
|
|
12
|
+
Reflect.ownKeys(input).length !== names.length ||
|
|
13
|
+
typeof operation !== "function" ||
|
|
14
|
+
utilTypes.isProxy(operation)
|
|
15
|
+
)
|
|
16
|
+
throw new TypeError(
|
|
17
|
+
"Registry maintenance requires plain own registries and an operation",
|
|
18
|
+
);
|
|
19
|
+
const registries = Object.fromEntries(
|
|
20
|
+
names.map((name) => {
|
|
21
|
+
const field = Object.getOwnPropertyDescriptor(input, name);
|
|
22
|
+
if (!field?.enumerable || !("value" in field))
|
|
23
|
+
throw new TypeError("Registry maintenance cannot use option accessors");
|
|
24
|
+
return [name, field.value];
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
const candidate = captureSkillCandidateRegistryWriterControl(
|
|
28
|
+
registries.candidateRegistry,
|
|
29
|
+
);
|
|
30
|
+
const release = captureSkillReleaseRegistryWriterControl(
|
|
31
|
+
registries.releaseRegistry,
|
|
32
|
+
);
|
|
33
|
+
if (
|
|
34
|
+
candidate.descriptor.tenantId !== release.descriptor.tenantId ||
|
|
35
|
+
candidate.orderKey === release.orderKey
|
|
36
|
+
)
|
|
37
|
+
throw new TypeError(
|
|
38
|
+
"Registry maintenance requires independent stores for the same tenant",
|
|
39
|
+
);
|
|
40
|
+
const ordered = [candidate, release].sort((left, right) =>
|
|
41
|
+
left.orderKey < right.orderKey ? -1 : 1,
|
|
42
|
+
);
|
|
43
|
+
return ordered[0].maintainAsync(() =>
|
|
44
|
+
ordered[1].maintainAsync(async () => {
|
|
45
|
+
let open = true;
|
|
46
|
+
const assertCurrent = () => {
|
|
47
|
+
if (!open)
|
|
48
|
+
throw Object.assign(
|
|
49
|
+
new Error("Registry maintenance capability is no longer live"),
|
|
50
|
+
{ code: "CC_SKILL_REGISTRY_MAINTENANCE_EXPIRED" },
|
|
51
|
+
);
|
|
52
|
+
candidate.assertIfWriting();
|
|
53
|
+
release.assertIfWriting();
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
const context = Object.freeze({
|
|
57
|
+
descriptor: Object.freeze({
|
|
58
|
+
schema: "chainlesschain.skill-registry-maintenance/v1",
|
|
59
|
+
tenantId: candidate.descriptor.tenantId,
|
|
60
|
+
candidateWriter: candidate.descriptor,
|
|
61
|
+
releaseWriter: release.descriptor,
|
|
62
|
+
participatingWriterExclusionVerified: true,
|
|
63
|
+
preTransitionBinaryWritersExcluded: false,
|
|
64
|
+
persistentStoreIdentityAuthenticated: false,
|
|
65
|
+
originCutoverAuthenticated: false,
|
|
66
|
+
qualifiesForPromotion: false,
|
|
67
|
+
}),
|
|
68
|
+
assertCurrent,
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
assertCurrent();
|
|
72
|
+
const result = await operation(context);
|
|
73
|
+
assertCurrent();
|
|
74
|
+
return result;
|
|
75
|
+
} finally {
|
|
76
|
+
open = false;
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** Cooperative writer exclusion for the Registry transition release. */
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { types as utilTypes } from "node:util";
|
|
5
|
+
import { withFileLock, withFileLockAsync } from "../with-file-lock.js";
|
|
6
|
+
|
|
7
|
+
export const SKILL_REGISTRY_WRITER_PROTOCOL_SCHEMA =
|
|
8
|
+
"chainlesschain.skill-registry-writer-protocol/v1";
|
|
9
|
+
export const SKILL_REGISTRY_WRITER_BUSY_CODE = "CC_SKILL_REGISTRY_WRITER_BUSY";
|
|
10
|
+
const ACTIVE = new Map();
|
|
11
|
+
const canonicalPath = (value) =>
|
|
12
|
+
process.platform === "win32" ? value.toLowerCase() : value;
|
|
13
|
+
const identity = (stat) => `${String(stat.dev)}:${String(stat.ino)}`;
|
|
14
|
+
function fail(message, code = "CC_SKILL_REGISTRY_WRITER_UNSAFE") {
|
|
15
|
+
throw Object.assign(new Error(message), { code });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Called by real Registry constructors; this generic lock is not an origin authority. */
|
|
19
|
+
export function createSkillRegistryWriterControl({
|
|
20
|
+
rootDir,
|
|
21
|
+
tenantId,
|
|
22
|
+
component,
|
|
23
|
+
fsImpl,
|
|
24
|
+
}) {
|
|
25
|
+
const root = path.resolve(rootDir),
|
|
26
|
+
parent = path.dirname(root);
|
|
27
|
+
const realpath = fsImpl.realpathSync?.native ?? fsImpl.realpathSync;
|
|
28
|
+
if (typeof realpath !== "function")
|
|
29
|
+
fail("Registry writer requires canonical filesystem paths");
|
|
30
|
+
function capture(directory) {
|
|
31
|
+
const stat = fsImpl.lstatSync(directory);
|
|
32
|
+
if (
|
|
33
|
+
!stat.isDirectory() ||
|
|
34
|
+
stat.isSymbolicLink() ||
|
|
35
|
+
canonicalPath(path.resolve(realpath(directory))) !==
|
|
36
|
+
canonicalPath(directory)
|
|
37
|
+
)
|
|
38
|
+
fail("Registry writer directory is not canonical");
|
|
39
|
+
return Object.freeze({ path: directory, identity: identity(stat) });
|
|
40
|
+
}
|
|
41
|
+
const directories = [capture(root), capture(parent)];
|
|
42
|
+
function assertDirectories() {
|
|
43
|
+
for (const directory of directories) {
|
|
44
|
+
const current = capture(directory.path);
|
|
45
|
+
if (current.identity !== directory.identity)
|
|
46
|
+
fail("Registry writer directory identity changed");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const rootPathDigest = createHash("sha256")
|
|
50
|
+
.update(canonicalPath(root))
|
|
51
|
+
.digest("hex");
|
|
52
|
+
// A sibling of the tenant root: existing artifact formats and strict root
|
|
53
|
+
// inventories stay unchanged. Canonical physical paths unify reopen/aliases.
|
|
54
|
+
const target = path.join(parent, `.registry-writer-${rootPathDigest}`);
|
|
55
|
+
const key = canonicalPath(target);
|
|
56
|
+
let held = null;
|
|
57
|
+
function assertIfWriting() {
|
|
58
|
+
if (held) {
|
|
59
|
+
held.context.assertOwnership();
|
|
60
|
+
assertDirectories();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function enter(role) {
|
|
64
|
+
assertDirectories();
|
|
65
|
+
// Sync reentry must never block a same-process async owner from finishing.
|
|
66
|
+
if (ACTIVE.has(key))
|
|
67
|
+
fail(
|
|
68
|
+
"Registry writer or maintenance operation is already active",
|
|
69
|
+
SKILL_REGISTRY_WRITER_BUSY_CODE,
|
|
70
|
+
);
|
|
71
|
+
const state = { role, context: null };
|
|
72
|
+
ACTIVE.set(key, state);
|
|
73
|
+
return state;
|
|
74
|
+
}
|
|
75
|
+
function leave(state) {
|
|
76
|
+
held = null;
|
|
77
|
+
if (ACTIVE.get(key) === state) ACTIVE.delete(key);
|
|
78
|
+
}
|
|
79
|
+
function execute(state, context, operation) {
|
|
80
|
+
if (context.locked !== true) fail("Registry writer lock was not acquired");
|
|
81
|
+
state.context = context;
|
|
82
|
+
held = state;
|
|
83
|
+
assertIfWriting();
|
|
84
|
+
return operation();
|
|
85
|
+
}
|
|
86
|
+
function runSync(operation) {
|
|
87
|
+
const state = enter("writer");
|
|
88
|
+
try {
|
|
89
|
+
return withFileLock(
|
|
90
|
+
target,
|
|
91
|
+
(context) => {
|
|
92
|
+
const result = execute(state, context, operation);
|
|
93
|
+
if (utilTypes.isPromise(result))
|
|
94
|
+
fail("Registry synchronous writer cannot return a Promise");
|
|
95
|
+
assertIfWriting();
|
|
96
|
+
return result;
|
|
97
|
+
},
|
|
98
|
+
{ failIfUnavailable: true, timeoutMs: 0 },
|
|
99
|
+
);
|
|
100
|
+
} finally {
|
|
101
|
+
leave(state);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function runAsync(operation, role = "writer") {
|
|
105
|
+
const state = enter(role);
|
|
106
|
+
try {
|
|
107
|
+
return await withFileLockAsync(
|
|
108
|
+
target,
|
|
109
|
+
async (context) => {
|
|
110
|
+
const result = await execute(state, context, operation);
|
|
111
|
+
assertIfWriting();
|
|
112
|
+
return result;
|
|
113
|
+
},
|
|
114
|
+
{ failIfUnavailable: true, timeoutMs: 0 },
|
|
115
|
+
);
|
|
116
|
+
} finally {
|
|
117
|
+
leave(state);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
descriptor: Object.freeze({
|
|
122
|
+
schema: SKILL_REGISTRY_WRITER_PROTOCOL_SCHEMA,
|
|
123
|
+
tenantId,
|
|
124
|
+
component,
|
|
125
|
+
canonicalRootPathDigest: `sha256:${rootPathDigest}`,
|
|
126
|
+
participatingWriterProtocolRevision: 1,
|
|
127
|
+
coversPreTransitionBinaryWriters: false,
|
|
128
|
+
persistentStoreIdentityAuthenticated: false,
|
|
129
|
+
originCutoverAuthenticated: false,
|
|
130
|
+
grantsOriginOrPromotionAuthority: false,
|
|
131
|
+
}),
|
|
132
|
+
orderKey: key,
|
|
133
|
+
runSync,
|
|
134
|
+
runAsync,
|
|
135
|
+
maintainAsync: (operation) => runAsync(operation, "maintenance"),
|
|
136
|
+
assertIfWriting,
|
|
137
|
+
});
|
|
138
|
+
}
|