chainlesschain 0.166.43 → 0.166.45

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.
Files changed (202) hide show
  1. package/README.md +75 -14
  2. package/package.json +3 -3
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BIl-Z62M.js → AIOps-CrMhYNeH.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BgPi_Te6.js → ActionButton-DLY5blZ3.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CaNIzlAG.js → Analytics-C0NhKQB0.js} +2 -2
  7. package/src/assets/web-panel/assets/{AppLayout-D9GSx2hl.js → AppLayout-DLyepgMq.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-Kq7kotzv.js → Artifacts-Depk01Sa.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-e1RTJLco.js → Audit-jKe6NQ3J.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BfyNT0BW.js → BackgroundAgents-CG4GKT9s.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-BEAq7i6S.js → Backup-DYWCkzwM.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-BHuP82DF.js → BaseInput-xm5vodmJ.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-BLPgCQsB.js → Chat-B20_PNd3.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-D4d-eh0F.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-DDJrOqEh.js → Checkbox-CfgwkV_r.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CxyKcM6S.js → Codegen-DbnoWI44.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-BYFh9rE1.js → Col-CfzzhgGw.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-D8JPppoh.js → Community-BU7mGM8p.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-B5VGSqZe.js → Compact-DpYim0an.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-C7KiFcUK.js → Compliance-BNJcO6ra.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-D-F2fgA_.js → Cowork-CecZ-pWj.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-B6TmPqRO.js → Cron-BG_C1fe3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BeGcBXhk.js → Crosschain-BAeILQLL.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-9QklLcmQ.js → DID-CYqOEkXi.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-DIadmsuS.js → Dashboard-BYU8wmL1.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-0I5gks3i.js → Dropdown-DYlJeTTa.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BdTAjT2q.js → EmailListRenderer-C0M6py3K.js} +1 -1
  28. package/src/assets/web-panel/assets/EvolutionSettings-_QnjPFZJ.js +1 -0
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DQUQWwUY.js → FamilyGuardDashboard-Du90iDch.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-Drgi--Zn.js → Federation-DkFp_hzB.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-BOf-9P8n.js → FormItemContext-BGgtGTfG.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-CBBnIDaW.js → GenericCardRenderer-BIDsSCei.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-BKimGd5P.js → Git--d3Te93J.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DoXCXppu.js → Governance-CZwD0vmO.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-DzOhGhOE.js → Inference-DiDcSFEW.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-DawgmU58.js → KnowledgeGraph-Bi0hSfzg.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-Co3EJL2Y.js → Logs-C8NHSc0K.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-VUfiDRZN.js → MarkdownRenderer-DfDuADqF.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-BqpMHnMW.js → Marketplace-D6kD0bNf.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-C7ZSokcR.js → McpTools-mTSamCr5.js} +2 -2
  41. package/src/assets/web-panel/assets/{Memory-DuJrpeDx.js → Memory-YQBXfLjU.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-CaM744NU.js → MobileBridge-D2awuupp.js} +1 -1
  43. package/src/assets/web-panel/assets/MobileProjects-Bl7lZc_-.js +1 -0
  44. package/src/assets/web-panel/assets/{Mtc-5oUB7lPh.js → Mtc-CBqjLK2Q.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-D9DNF6DB.js → MtcAudit-C4poRInZ.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-CfMpFQSr.js → Multisig-C_J0zXYZ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CuhPZQ7q.js → NLProgramming-Ci43sWOU.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-NtSeMoyM.js → Notes-DN7A-GFZ.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-CB4e7V83.js → NotificationSettings-D5jNm71V.js} +1 -1
  50. package/src/assets/web-panel/assets/OrderTableRenderer-C9UsPOG8.js +1 -0
  51. package/src/assets/web-panel/assets/{Organization-K_0bjm7y.js → Organization-BapYMJrP.js} +2 -2
  52. package/src/assets/web-panel/assets/{Overflow-D2vwxiPv.js → Overflow-BwxwjhFw.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-WQMHBu8q.js → P2P-TObXXOu3.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-CTZVBMZZ.js → PdhVaultBrowser-UwJvMbFO.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-DcLy9Nfp.js → Permissions-Damnxv-O.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-zc-cm-02.js → PersonalDataHub-d_7jlBb-.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-CCnI8peM.js → Pipeline-DA87VjLw.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-DO1Xor8L.js → Privacy-DzO_UvWn.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-CZqC-jyt.js → ProjectInit-CwqECDhs.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-DqJtyavX.js → ProjectSettings-CT5YdIl6.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-Dx14NAO3.js → Projects-D3KdZPCv.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-DH13a6ro.js → Providers-BjBIS8kk.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-C9bwzVfA.js → QrScannerModal-mMFqiKkI.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-Cbt-oIPf.js → QuickAsk-BwGi9ZU1.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-BYIimGcY.js → Recommend-BY65SR5s.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-C8OWTG1H.js → RemoteSession-BGsVH_XU.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-DpUGT_jD.js → Reputation-DpPbxSgI.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-DZ5Izi2A.js → Row-B5YBYoVz.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CAn94No1.js → RssFeed-DvM5jaoh.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-BqNaxcgj.js → Search-6RX39EPO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-Ck6vFTWn.js → Security-Beal-7cM.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-Y3RdvvDj.js → Services-D6rjVLe-.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-Cx9Iqcp7.js → Skeleton-Dur7R7vz.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-Oiqz7wLM.js → Skills-D1QkGwMk.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-DaKJQADm.js → Sla-3ObrX8Es.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-BWoOpPW4.js → SpeechSettings-DG_KAiH1.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-CgFle8GX.js → SyncSettings-BaHq0SJ2.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-C1tQNvWx.js → Tasks-BDpAkBAB.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-CvvycaWi.js → Templates-tYdoRd-c.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-D1FwaD12.js → Tenant-yGS5zIQ1.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-oorFjObO.js → Terminal-B3QbmVFa.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-BjMs48Sj.js → TimelineRenderer-B_7Pr5dS.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-BkRb95xG.js → Tokens-DPvpDa53.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-B94RYYAF.js → Trigger-DcSAD0GZ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-DnBVpOWt.js → Trust-BBzEq-uy.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-DouHO6Up.js → UkeySign-Dssav7-5.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-DDgRerWV.js → VideoEditing-CPOZf4yi.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CQN9Un7X.js → Wallet-BzBVRXEq.js} +3 -3
  89. package/src/assets/web-panel/assets/{WebAuthn-DyHl2G3J.js → WebAuthn-DdSGaCCh.js} +2 -2
  90. package/src/assets/web-panel/assets/{WorkflowEditor-BQlN77Ey.js → WorkflowEditor-CKjBFoSs.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-0jOqe7ZD.js → chat-B9aMGLg0.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-kp5TAaKx.js → colors-GFCMctSV.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DqF6Rml7.js → compact-item-BqSDFAYJ.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-lDyLBjy5.js → createContext-BwUm3Ffp.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-LxtRBsCm.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-CkiF91kH.js → hasIn-DN1kMJOT.js} +1 -1
  97. package/src/assets/web-panel/assets/index-36hxBH-o.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BvFFY-2V.js → index-7W1e9Vi4.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DQGk8bV8.js → index-B4MfxM5W.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-kenTha3h.js → index-B4dI_csl.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B6V9l2P-.js → index-B7Yu5wwV.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bq2s5obn.js → index-B9Pe-EyB.js} +3 -3
  103. package/src/assets/web-panel/assets/{index-aHKeys8c.js → index-BBMW8bQO.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPwtIqRo.js → index-BCnbC2z7.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-ByggSxTQ.js → index-BDL2MwxN.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BPcjLn2w.js → index-BHUxz4dy.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Bu14YcwM.js → index-BL_0ndQc.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DrVVi5jX.js → index-BPGlcDvs.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DzU_SfdF.js → index-BeycSy7n.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DnI__Fwu.js → index-BpiR6uzH.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CI-H0ljO.js → index-BwssFWI1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DPHmcQWh.js → index-By9GbJhu.js} +1 -1
  113. package/src/assets/web-panel/assets/index-C0b78zWZ.js +1 -0
  114. package/src/assets/web-panel/assets/{index-DX40v3aL.js → index-CEMl-Qte.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BRu6Pe98.js → index-CH2wK5dx.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-8Hxbv1SG.js → index-CRikhGgx.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DgGNW2ep.js → index-Ci0zIpSX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BUqE17wu.js → index-Cli0VREv.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BAtpuJQ7.js → index-CmEHzOsw.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DK47iA2d.js → index-Cwih67e8.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-mkwJZMBT.js → index-D8jcp9Rj.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DSDM-VNz.js → index-DAK28GY8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CNPCAYHk.js → index-DI90Wo_P.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLchRTsS.js → index-DO-IKERp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BKFL4DfP.js → index-DaFr7XB1.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DduPk1RS.js → index-DbDhKroq.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-X1l5-ive.js → index-DnJt9DaX.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-INxX1TMz.js → index-DvvJWwT8.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DHnFhMOL.js → index-PJt1Zrx2.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DHVNtkrR.js → index-PKwZkQDC.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DZU13W4o.js → index-THsr_UUJ.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C-CS_-jC.js → index-bwb4cg_D.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BSAeSPQe.js → index-rCTtYCGu.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BnU8hi3c.js → index-tMF_deZb.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DTA_B9gU.js → index-y7cfh9rP.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-CYYQ0SI1.js → initDefaultProps-B6e5CZ5p.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion--Tyo36te.js → motion-riMM1KfJ.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-D2BjgQv7.js → move-C1NSDMjc.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-dyIu5RU_.js → mtc-parser-Bibuny9B.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-CDEAhC5H.js → omit-Cmf6FoRn.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DLKDlIQs.js → pickAttrs-BSsYBUCY.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BveC8fe2.js → placementArrow-gSB7-GAS.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-51urgYrW.js → responsiveObserve-qaqttvKa.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-CCHbhZg-.js → slide-RX2gxl3k.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-W4mtUXRP.js → statusUtils-DEJmkhqw.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-D4Drrz2a.js → styleChecker-CIWmJ78O.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-BBCd4kCM.js → useFlexGapSupport-D-UPSQBE.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-4Khxvw5Y.js → useFs-9QFtqADc.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-7c72tleZ.js → usePersonalDataHub-DM6kyift.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DzZ7Y0LN.js → vnode-C-q6FNSJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-C5jhaET3.js → zoom-D4WJY2m8.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +1 -1
  154. package/src/commands/agent.js +19 -2
  155. package/src/commands/ask.js +13 -8
  156. package/src/commands/chat.js +8 -5
  157. package/src/commands/cowork.js +18 -9
  158. package/src/commands/eval.js +100 -45
  159. package/src/commands/evolution-deployment.js +58 -0
  160. package/src/commands/hub.js +13 -7
  161. package/src/commands/setup.js +31 -1
  162. package/src/constants.js +2 -2
  163. package/src/data/changelog.json +188 -762
  164. package/src/gateways/ws/personal-data-hub-protocol.js +12 -5
  165. package/src/lazy-dispatch.js +42 -6
  166. package/src/lib/agent-router.js +25 -23
  167. package/src/lib/chat-intent-service.js +3 -13
  168. package/src/lib/context-memory-kernel/provider-context.js +19 -3
  169. package/src/lib/cowork-adapter.js +9 -4
  170. package/src/lib/doctor-checkup.js +42 -0
  171. package/src/lib/eval/evidence.js +203 -0
  172. package/src/lib/eval/runner.js +16 -2
  173. package/src/lib/eval/trend.js +4 -3
  174. package/src/lib/evolution/evolution-deployment-config.js +217 -10
  175. package/src/lib/evolution/evolution-deployment-descriptor-revocations.js +131 -0
  176. package/src/lib/evolution/evolution-deployment-loader.js +23 -2
  177. package/src/lib/evolution/evolution-deployment-profile.js +281 -37
  178. package/src/lib/evolution/evolution-deployment-root-rotation.js +154 -0
  179. package/src/lib/evolution/evolution-file-identity.js +42 -0
  180. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +242 -0
  181. package/src/lib/evolution/evolution-ledger-manifest-chain.js +74 -18
  182. package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +135 -20
  183. package/src/lib/evolution/evolution-ledger.js +353 -209
  184. package/src/lib/evolution/governed-hub-llm.js +18 -12
  185. package/src/lib/evolution/governed-hub-skill.js +4 -17
  186. package/src/lib/evolution/governed-model-turn.js +7 -8
  187. package/src/lib/llm-providers.js +1 -0
  188. package/src/lib/orchestrator.js +14 -8
  189. package/src/lib/personal-data-hub-wiring.js +7 -1
  190. package/src/lib/sub-agent-context.js +5 -1
  191. package/src/lib/task-model-selector.js +1 -1
  192. package/src/repl/agent-repl.js +1 -1
  193. package/src/repl/chat-repl.js +8 -4
  194. package/src/runtime/agent-core.js +36 -10
  195. package/src/runtime/headless-runner.js +16 -0
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-DWXPJh-J.js +0 -1
  197. package/src/assets/web-panel/assets/EvolutionSettings-C7_tq643.js +0 -1
  198. package/src/assets/web-panel/assets/MobileProjects-Bd78jDwm.js +0 -1
  199. package/src/assets/web-panel/assets/OrderTableRenderer-Bu8C6Qlj.js +0 -1
  200. package/src/assets/web-panel/assets/devWarning-IfrMTwCK.js +0 -1
  201. package/src/assets/web-panel/assets/index-C7nsIbMa.js +0 -1
  202. package/src/assets/web-panel/assets/index-DFXFLdKN.js +0 -1
@@ -1,5 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { readFile, rename, unlink, writeFile } from "node:fs/promises";
2
+ import { readFile } from "node:fs/promises";
3
+ import {
4
+ readFileSync,
5
+ renameSync,
6
+ unlinkSync,
7
+ writeFileSync,
8
+ } from "node:fs";
3
9
  import { dirname, isAbsolute, join } from "node:path";
4
10
  import { resolveConfigDataRoot } from "../paths.js";
5
11
  import {
@@ -7,9 +13,19 @@ import {
7
13
  ensurePrivateDirectory,
8
14
  ensurePrivateFile,
9
15
  } from "../secure-fs.js";
16
+ import { withFileLock } from "../with-file-lock.js";
10
17
 
11
18
  export const EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA =
19
+ "chainlesschain.evolution-deployment-profile/v4";
20
+
21
+ const LEGACY_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA =
12
22
  "chainlesschain.evolution-deployment-profile/v1";
23
+ const PREVIOUS_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA =
24
+ "chainlesschain.evolution-deployment-profile/v2";
25
+ const PRIOR_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA =
26
+ "chainlesschain.evolution-deployment-profile/v3";
27
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
28
+ const MAX_TRUST_ROOT_REVISION_FLOORS = 64;
13
29
 
14
30
  export function getEvolutionDeploymentProfilePath({
15
31
  env = process.env,
@@ -26,10 +42,48 @@ function normalizeProfile(value) {
26
42
  if (!value || typeof value !== "object" || Array.isArray(value))
27
43
  throw new TypeError("evolution deployment profile must be an object");
28
44
  const keys = Object.keys(value).sort();
29
- const expected = ["descriptorPath", "enabled", "schema", "trustRootPath"];
45
+ const legacy = [
46
+ LEGACY_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
47
+ PREVIOUS_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
48
+ PRIOR_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
49
+ ].includes(value.schema);
50
+ const expected =
51
+ value.schema === LEGACY_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
52
+ ? ["descriptorPath", "enabled", "schema", "trustRootPath"]
53
+ : value.schema === PREVIOUS_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
54
+ ? [
55
+ "descriptorPath",
56
+ "enabled",
57
+ "revisionFloors",
58
+ "schema",
59
+ "trustRootPath",
60
+ ]
61
+ : value.schema === PRIOR_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
62
+ ? [
63
+ "activeTrustRootDigest",
64
+ "descriptorPath",
65
+ "enabled",
66
+ "revisionFloors",
67
+ "schema",
68
+ "trustRootPath",
69
+ ]
70
+ : [
71
+ "activeTrustRootDigest",
72
+ "descriptorPath",
73
+ "enabled",
74
+ "revisionFloors",
75
+ "revokedDescriptorRevisions",
76
+ "schema",
77
+ "trustRootPath",
78
+ ];
30
79
  if (JSON.stringify(keys) !== JSON.stringify(expected.sort()))
31
80
  throw new TypeError("evolution deployment profile has unexpected fields");
32
- if (value.schema !== EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA)
81
+ if (
82
+ value.schema !== EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA &&
83
+ value.schema !== LEGACY_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA &&
84
+ value.schema !== PREVIOUS_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA &&
85
+ value.schema !== PRIOR_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
86
+ )
33
87
  throw new TypeError("evolution deployment profile schema is invalid");
34
88
  if (typeof value.enabled !== "boolean")
35
89
  throw new TypeError("evolution deployment profile enabled is invalid");
@@ -42,73 +96,263 @@ function normalizeProfile(value) {
42
96
  `evolution deployment profile ${name} must be absolute`,
43
97
  );
44
98
  }
99
+ const revisionFloors =
100
+ value.schema === LEGACY_EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
101
+ ? {}
102
+ : normalizeRevisionFloors(value.revisionFloors);
103
+ const activeTrustRootDigest = legacy
104
+ ? null
105
+ : normalizeTrustRootDigest(value.activeTrustRootDigest);
106
+ const revokedDescriptorRevisions =
107
+ value.schema === EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA
108
+ ? normalizeRevokedDescriptorRevisions(value.revokedDescriptorRevisions)
109
+ : {};
45
110
  return Object.freeze({
46
- schema: value.schema,
111
+ schema: EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
47
112
  enabled: value.enabled,
48
113
  descriptorPath: value.descriptorPath,
49
114
  trustRootPath: value.trustRootPath,
115
+ revisionFloors,
116
+ activeTrustRootDigest,
117
+ revokedDescriptorRevisions,
50
118
  });
51
119
  }
52
120
 
53
- export async function readEvolutionDeploymentProfile(options = {}) {
54
- const filePath =
55
- options.filePath || getEvolutionDeploymentProfilePath(options);
56
- try {
57
- const profile = normalizeProfile(
58
- JSON.parse(await readFile(filePath, "utf8")),
121
+ function normalizeRevokedDescriptorRevisions(value) {
122
+ if (!value || typeof value !== "object" || Array.isArray(value))
123
+ throw new TypeError(
124
+ "evolution deployment profile revokedDescriptorRevisions is invalid",
59
125
  );
60
- return Object.freeze({ filePath, profile, error: null });
126
+ const result = {};
127
+ for (const [trustRootDigest, revisions] of Object.entries(value)) {
128
+ if (!DIGEST.test(trustRootDigest) || !Array.isArray(revisions))
129
+ throw new TypeError(
130
+ "evolution deployment profile revokedDescriptorRevisions is invalid",
131
+ );
132
+ const normalized = [...revisions].sort((left, right) => left - right);
133
+ if (
134
+ normalized.length > 1024 ||
135
+ normalized.some(
136
+ (revision) => !Number.isSafeInteger(revision) || revision < 1,
137
+ ) ||
138
+ new Set(normalized).size !== normalized.length
139
+ ) {
140
+ throw new TypeError(
141
+ "evolution deployment profile revokedDescriptorRevisions is invalid",
142
+ );
143
+ }
144
+ result[trustRootDigest] = Object.freeze(normalized);
145
+ }
146
+ return Object.freeze(result);
147
+ }
148
+
149
+ function normalizeTrustRootDigest(value) {
150
+ if (!DIGEST.test(value || ""))
151
+ throw new TypeError(
152
+ "evolution deployment profile activeTrustRootDigest is invalid",
153
+ );
154
+ return value;
155
+ }
156
+
157
+ function normalizeRevisionFloors(value) {
158
+ if (!value || typeof value !== "object" || Array.isArray(value))
159
+ throw new TypeError("evolution deployment profile revisionFloors is invalid");
160
+ const entries = Object.entries(value).sort(([left], [right]) =>
161
+ left.localeCompare(right),
162
+ );
163
+ if (entries.length > MAX_TRUST_ROOT_REVISION_FLOORS)
164
+ throw new TypeError("evolution deployment profile revisionFloors is too large");
165
+ for (const [trustRootDigest, revision] of entries) {
166
+ if (!DIGEST.test(trustRootDigest))
167
+ throw new TypeError(
168
+ "evolution deployment profile revisionFloors trust root is invalid",
169
+ );
170
+ if (!Number.isSafeInteger(revision) || revision < 1)
171
+ throw new TypeError(
172
+ "evolution deployment profile revisionFloors revision is invalid",
173
+ );
174
+ }
175
+ return Object.freeze(Object.fromEntries(entries));
176
+ }
177
+
178
+ function profilePath(options = {}) {
179
+ return options.filePath || getEvolutionDeploymentProfilePath(options);
180
+ }
181
+
182
+ function readProfileSync(filePath) {
183
+ try {
184
+ return {
185
+ profile: normalizeProfile(JSON.parse(readFileSync(filePath, "utf8"))),
186
+ error: null,
187
+ };
61
188
  } catch (error) {
62
- if (error?.code === "ENOENT")
63
- return Object.freeze({ filePath, profile: null, error: null });
64
- return Object.freeze({
65
- filePath,
189
+ if (error?.code === "ENOENT") return { profile: null, error: null };
190
+ return {
66
191
  profile: null,
67
192
  error: error instanceof Error ? error.message : String(error),
68
- });
193
+ };
69
194
  }
70
195
  }
71
196
 
72
- export async function writeEvolutionDeploymentProfile(input, options = {}) {
73
- const profile = normalizeProfile({
74
- schema: EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
75
- enabled: input?.enabled === true,
76
- descriptorPath: input?.descriptorPath,
77
- trustRootPath: input?.trustRootPath,
78
- });
79
- const filePath =
80
- options.filePath || getEvolutionDeploymentProfilePath(options);
197
+ function writeProfileSync(profile, filePath) {
81
198
  ensurePrivateDirectory(dirname(filePath));
82
199
  const temporaryPath = join(
83
200
  dirname(filePath),
84
201
  `.deployment-profile.${randomUUID()}.tmp`,
85
202
  );
86
203
  try {
87
- await writeFile(temporaryPath, `${JSON.stringify(profile, null, 2)}\n`, {
204
+ writeFileSync(temporaryPath, `${JSON.stringify(profile, null, 2)}\n`, {
88
205
  encoding: "utf8",
89
206
  mode: PRIVATE_FILE_MODE,
90
207
  flag: "wx",
91
208
  });
92
209
  ensurePrivateFile(temporaryPath);
93
- await rename(temporaryPath, filePath);
210
+ renameSync(temporaryPath, filePath);
94
211
  ensurePrivateFile(filePath);
95
212
  } catch (error) {
96
- await unlink(temporaryPath).catch(() => {});
213
+ try {
214
+ unlinkSync(temporaryPath);
215
+ } catch {
216
+ // The temporary path either was not created or has already been renamed.
217
+ }
97
218
  throw error;
98
219
  }
99
- return Object.freeze({ filePath, profile });
220
+ }
221
+
222
+ function profileFromInput(input) {
223
+ return normalizeProfile({
224
+ schema: EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
225
+ enabled: input?.enabled === true,
226
+ descriptorPath: input?.descriptorPath,
227
+ trustRootPath: input?.trustRootPath,
228
+ revisionFloors: input?.revisionFloors || {},
229
+ activeTrustRootDigest: input?.activeTrustRootDigest,
230
+ revokedDescriptorRevisions: input?.revokedDescriptorRevisions || {},
231
+ });
232
+ }
233
+
234
+ export async function readEvolutionDeploymentProfile(options = {}) {
235
+ const filePath = profilePath(options);
236
+ try {
237
+ const profile = normalizeProfile(
238
+ JSON.parse(await readFile(filePath, "utf8")),
239
+ );
240
+ return Object.freeze({ filePath, profile, error: null });
241
+ } catch (error) {
242
+ if (error?.code === "ENOENT")
243
+ return Object.freeze({ filePath, profile: null, error: null });
244
+ return Object.freeze({
245
+ filePath,
246
+ profile: null,
247
+ error: error instanceof Error ? error.message : String(error),
248
+ });
249
+ }
250
+ }
251
+
252
+ export async function writeEvolutionDeploymentProfile(input, options = {}) {
253
+ const proposed = profileFromInput(input);
254
+ return updateEvolutionDeploymentProfile((current) => ({
255
+ ...proposed,
256
+ // This lower-level writer cannot authenticate a descriptor, so it never
257
+ // lowers or erases a floor established by configureEvolutionDeployment().
258
+ revisionFloors: Object.fromEntries(
259
+ Object.keys({
260
+ ...(current?.revisionFloors || {}),
261
+ ...proposed.revisionFloors,
262
+ }).map((trustRootDigest) => [
263
+ trustRootDigest,
264
+ Math.max(
265
+ current?.revisionFloors?.[trustRootDigest] || 0,
266
+ proposed.revisionFloors[trustRootDigest] || 0,
267
+ ),
268
+ ]),
269
+ ),
270
+ activeTrustRootDigest:
271
+ proposed.activeTrustRootDigest || current?.activeTrustRootDigest,
272
+ revokedDescriptorRevisions: {
273
+ ...(current?.revokedDescriptorRevisions || {}),
274
+ ...(proposed.revokedDescriptorRevisions || {}),
275
+ },
276
+ }), options);
277
+ }
278
+
279
+ /**
280
+ * Atomically read, validate and replace the owner-only profile. The callback is
281
+ * synchronous by design: `withFileLock` is a cross-process lock and releasing
282
+ * it while an async callback is pending would reintroduce lost high-water
283
+ * updates.
284
+ */
285
+ export function updateEvolutionDeploymentProfile(update, options = {}) {
286
+ if (typeof update !== "function")
287
+ throw new TypeError(
288
+ "evolution deployment profile update must be a function",
289
+ );
290
+ const filePath = profilePath(options);
291
+ ensurePrivateDirectory(dirname(filePath));
292
+ return withFileLock(
293
+ filePath,
294
+ () => {
295
+ const current = readProfileSync(filePath);
296
+ if (current.error) throw new Error(current.error);
297
+ const profile = profileFromInput(update(current.profile));
298
+ writeProfileSync(profile, filePath);
299
+ return Object.freeze({ filePath, profile });
300
+ },
301
+ { failIfUnavailable: true },
302
+ );
303
+ }
304
+
305
+ export function assertEvolutionDeploymentRevisionFloor(profile, descriptor) {
306
+ const floor = profile?.revisionFloors?.[descriptor?.trustRootDigest];
307
+ if (floor === undefined) return null;
308
+ if (
309
+ !Number.isSafeInteger(descriptor?.revision) ||
310
+ descriptor.revision < floor
311
+ ) {
312
+ const error = new Error(
313
+ `evolution deployment descriptor revision rollback rejected: received ${descriptor?.revision}, floor ${floor}`,
314
+ );
315
+ error.code = "EVOLUTION_DEPLOYMENT_REVISION_ROLLBACK";
316
+ throw error;
317
+ }
318
+ return floor;
319
+ }
320
+
321
+ export function assertEvolutionDeploymentActiveTrustRoot(profile, descriptor) {
322
+ const activeTrustRootDigest = profile?.activeTrustRootDigest;
323
+ if (!activeTrustRootDigest) return null;
324
+ if (descriptor?.trustRootDigest !== activeTrustRootDigest) {
325
+ const error = new Error(
326
+ "evolution deployment descriptor trust root does not match the active profile root",
327
+ );
328
+ error.code = "EVOLUTION_DEPLOYMENT_TRUST_ROOT_MISMATCH";
329
+ throw error;
330
+ }
331
+ return activeTrustRootDigest;
332
+ }
333
+
334
+ export function assertEvolutionDeploymentDescriptorNotRevoked(
335
+ profile,
336
+ descriptor,
337
+ ) {
338
+ const revoked = profile?.revokedDescriptorRevisions?.[
339
+ descriptor?.trustRootDigest
340
+ ];
341
+ if (!revoked?.includes(descriptor?.revision)) return;
342
+ const error = new Error(
343
+ `evolution deployment descriptor revision ${descriptor.revision} is revoked`,
344
+ );
345
+ error.code = "EVOLUTION_DEPLOYMENT_DESCRIPTOR_REVOKED";
346
+ throw error;
100
347
  }
101
348
 
102
349
  export async function setEvolutionDeploymentProfileEnabled(
103
350
  enabled,
104
351
  options = {},
105
352
  ) {
106
- const current = await readEvolutionDeploymentProfile(options);
107
- if (current.error) throw new Error(current.error);
108
- if (!current.profile)
109
- throw new Error("evolution deployment profile is not configured");
110
- return writeEvolutionDeploymentProfile(
111
- { ...current.profile, enabled: enabled === true },
112
- { ...options, filePath: current.filePath },
113
- );
353
+ return updateEvolutionDeploymentProfile((current) => {
354
+ if (!current)
355
+ throw new Error("evolution deployment profile is not configured");
356
+ return { ...current, enabled: enabled === true };
357
+ }, options);
114
358
  }
@@ -0,0 +1,154 @@
1
+ import { createHash, createPublicKey, verify } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { isAbsolute } from "node:path";
4
+
5
+ export const EVOLUTION_DEPLOYMENT_ROOT_ROTATION_SCHEMA =
6
+ "chainlesschain.evolution-deployment-root-rotation/v1";
7
+
8
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
9
+ const MAX_ROTATION_BYTES = 64 * 1024;
10
+
11
+ function canonical(value) {
12
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
13
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
14
+ return `{${Object.keys(value)
15
+ .sort()
16
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
17
+ .join(",")}}`;
18
+ }
19
+
20
+ function digest(bytes) {
21
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
22
+ }
23
+
24
+ function normalizeRotation(value) {
25
+ if (!value || typeof value !== "object" || Array.isArray(value))
26
+ throw new TypeError("evolution deployment root rotation must be an object");
27
+ const expected = [
28
+ "fromTrustRootDigest",
29
+ "minimumDescriptorRevision",
30
+ "schema",
31
+ "signature",
32
+ "toTrustRootDigest",
33
+ ];
34
+ if (canonical(Object.keys(value).sort()) !== canonical(expected))
35
+ throw new TypeError(
36
+ "evolution deployment root rotation has unexpected or missing fields",
37
+ );
38
+ if (value.schema !== EVOLUTION_DEPLOYMENT_ROOT_ROTATION_SCHEMA)
39
+ throw new TypeError("evolution deployment root rotation schema is invalid");
40
+ for (const key of ["fromTrustRootDigest", "toTrustRootDigest"]) {
41
+ if (!DIGEST.test(value[key] || ""))
42
+ throw new TypeError(`evolution deployment root rotation ${key} is invalid`);
43
+ }
44
+ if (value.fromTrustRootDigest === value.toTrustRootDigest)
45
+ throw new TypeError("evolution deployment root rotation must change trust root");
46
+ if (
47
+ !Number.isSafeInteger(value.minimumDescriptorRevision) ||
48
+ value.minimumDescriptorRevision < 1
49
+ ) {
50
+ throw new TypeError(
51
+ "evolution deployment root rotation minimumDescriptorRevision is invalid",
52
+ );
53
+ }
54
+ if (
55
+ typeof value.signature !== "string" ||
56
+ !/^[A-Za-z0-9+/]+={0,2}$/u.test(value.signature) ||
57
+ value.signature.length === 0
58
+ ) {
59
+ throw new TypeError("evolution deployment root rotation signature is invalid");
60
+ }
61
+ return Object.freeze({ ...value });
62
+ }
63
+
64
+ export function serializeEvolutionDeploymentRootRotationPayload(rotation) {
65
+ const value = normalizeRotation({ ...rotation, signature: "AA==" });
66
+ return canonical({
67
+ schema: value.schema,
68
+ fromTrustRootDigest: value.fromTrustRootDigest,
69
+ toTrustRootDigest: value.toTrustRootDigest,
70
+ minimumDescriptorRevision: value.minimumDescriptorRevision,
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Verify a root transition while the profile lock is held. The old root is
76
+ * authenticated by its exact bytes before it may authorize a new root.
77
+ */
78
+ export function verifyEvolutionDeploymentRootRotationSync({
79
+ rotationPath,
80
+ fromTrustRootPath,
81
+ expectedFromTrustRootDigest,
82
+ expectedToTrustRootDigest,
83
+ destinationRevision,
84
+ }) {
85
+ if (
86
+ typeof rotationPath !== "string" ||
87
+ !isAbsolute(rotationPath) ||
88
+ typeof fromTrustRootPath !== "string" ||
89
+ !isAbsolute(fromTrustRootPath)
90
+ ) {
91
+ throw new Error("evolution deployment root rotation paths must be absolute");
92
+ }
93
+ if (!DIGEST.test(expectedFromTrustRootDigest || ""))
94
+ throw new Error("evolution deployment root rotation source digest is invalid");
95
+ if (!DIGEST.test(expectedToTrustRootDigest || ""))
96
+ throw new Error("evolution deployment root rotation destination digest is invalid");
97
+ if (!Number.isSafeInteger(destinationRevision) || destinationRevision < 1)
98
+ throw new Error("evolution deployment root rotation destination revision is invalid");
99
+
100
+ let rotation;
101
+ let fromTrustRootBytes;
102
+ try {
103
+ const rotationBytes = readFileSync(rotationPath);
104
+ if (
105
+ rotationBytes.byteLength === 0 ||
106
+ rotationBytes.byteLength > MAX_ROTATION_BYTES
107
+ ) {
108
+ throw new Error("evolution deployment root rotation size is invalid");
109
+ }
110
+ rotation = normalizeRotation(JSON.parse(rotationBytes.toString("utf8")));
111
+ fromTrustRootBytes = readFileSync(fromTrustRootPath);
112
+ } catch (error) {
113
+ throw new Error(
114
+ "evolution deployment root rotation could not be read",
115
+ error instanceof Error ? { cause: error } : undefined,
116
+ );
117
+ }
118
+ if (digest(fromTrustRootBytes) !== expectedFromTrustRootDigest)
119
+ throw new Error(
120
+ "evolution deployment root rotation source trust root changed",
121
+ );
122
+ if (rotation.fromTrustRootDigest !== expectedFromTrustRootDigest)
123
+ throw new Error(
124
+ "evolution deployment root rotation source does not match profile",
125
+ );
126
+ if (rotation.toTrustRootDigest !== expectedToTrustRootDigest)
127
+ throw new Error(
128
+ "evolution deployment root rotation destination does not match descriptor",
129
+ );
130
+ if (destinationRevision < rotation.minimumDescriptorRevision)
131
+ throw new Error(
132
+ "evolution deployment root rotation destination revision is below the authorized minimum",
133
+ );
134
+ let publicKey;
135
+ try {
136
+ publicKey = createPublicKey(fromTrustRootBytes);
137
+ } catch {
138
+ throw new Error("evolution deployment root rotation source trust root is invalid");
139
+ }
140
+ if (
141
+ !verify(
142
+ null,
143
+ Buffer.from(
144
+ serializeEvolutionDeploymentRootRotationPayload(rotation),
145
+ "utf8",
146
+ ),
147
+ publicKey,
148
+ Buffer.from(rotation.signature, "base64"),
149
+ )
150
+ ) {
151
+ throw new Error("evolution deployment root rotation signature rejected");
152
+ }
153
+ return rotation;
154
+ }
@@ -1,3 +1,5 @@
1
+ import path from "node:path";
2
+
1
3
  import {
2
4
  isAffectedWindowsZeroDeviceStatRuntime,
3
5
  sameFileStatIdentity,
@@ -48,3 +50,43 @@ export function withEvolutionFileIdentity(
48
50
  { runtime },
49
51
  );
50
52
  }
53
+
54
+ // Verify any number of children while holding one authenticated parent
55
+ // directory identity. This keeps the affected Windows/libuv device bridge
56
+ // exact without reopening the volume root and parent once per child.
57
+ export function withEvolutionDirectoryFileIdentity(
58
+ fsImpl,
59
+ directoryPath,
60
+ operation,
61
+ runtime = undefined,
62
+ ) {
63
+ if (typeof operation !== "function") {
64
+ throw new TypeError("evolution directory identity operation is required");
65
+ }
66
+ const sentinel = path.join(path.resolve(directoryPath), ".identity-scope");
67
+ return withTrustedFileParentSync(
68
+ fsImpl,
69
+ sentinel,
70
+ ({ parentDevice, parentPath }) =>
71
+ operation(
72
+ isAffectedWindowsZeroDeviceStatRuntime(runtime)
73
+ ? (pathStat, handleStat) =>
74
+ samePathHandleFileIdentity(
75
+ pathStat,
76
+ handleStat,
77
+ parentDevice,
78
+ runtime,
79
+ )
80
+ : sameFileStatIdentity,
81
+ (pathStat, handleStat) =>
82
+ samePathHandleStableFileIdentity(
83
+ pathStat,
84
+ handleStat,
85
+ parentDevice,
86
+ runtime,
87
+ ),
88
+ Object.freeze({ parentPath }),
89
+ ),
90
+ { runtime },
91
+ );
92
+ }