chainlesschain 0.166.109 → 0.166.112

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 (206) hide show
  1. package/README.md +6 -4
  2. package/package.json +3 -2
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-DkrNbDzS.js → AIOps-sOceeExN.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-DJjFXDhv.js → ActionButton-BQs-vRUr.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-DyUbagRn.js → Analytics-sfAHHMhA.js} +2 -2
  8. package/src/assets/web-panel/assets/{AppLayout-p7kJWSvn.js → AppLayout-0gAvE0c8.js} +4 -4
  9. package/src/assets/web-panel/assets/{Artifacts-C3rVh4Ak.js → Artifacts-Dh9s2Qmd.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-Bch_L_YO.js → Audit-CITm26SU.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BNHy8zP6.js → BackgroundAgents-BiMbBOh0.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-BUaye_Ys.js → Backup-CNpnhI3h.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-D49mzAVz.js → BaseInput-BI_lltkp.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-2MDzM_us.js → Chat-BgfB88xY.js} +5 -5
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-BDP3fGoz.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-5p1T_KEq.js → Checkbox-4xy9rS3j.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-TKtiLaoZ.js → Codegen-D93h9ihS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-BVCMG9mS.js → Col-BRf3STY-.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-D09-QczH.js → Community-f187ziSL.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-QuRMCnj7.js → Compact-D4hXWuo1.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-CN0PV7fk.js → Compliance-C6cYKI6H.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-5N2zhAty.js → Cowork-C1i1f8LD.js} +2 -2
  23. package/src/assets/web-panel/assets/{Cron-BxT8UZ0S.js → Cron-BacCE4LA.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-Dp-noQl4.js → Crosschain-DXztxOoH.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-B7MZFkjG.js → DID-rpf4HN8x.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-DulJ3rKV.js → Dashboard-h4bgtH7L.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-DV4ov4zF.js → Dropdown-CyQnoHmu.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-DB2-ooz6.js → EmailListRenderer-FCtp4AcB.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-B1Rb6p7q.js → EvolutionSettings-JbISHTWR.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-U8aHayx-.js → FamilyGuardDashboard-DCIQLyfK.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-BkQULBHY.js → Federation-DDbFpwja.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-CtviG260.js → FormItemContext-CXF0jtXb.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DnPirB24.js → GenericCardRenderer-CUaNfAM0.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CK57Oro6.js → Git-Bhoz2gDf.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DbI-LB8-.js → Governance-FiiXHe8e.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-sEG4T1Jg.js → Inference-brpTW2Q3.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-9SLJR3h9.js → KnowledgeGraph-BCWP-f6k.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BIZYcISQ.js → Logs-CxYcduoK.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-12vD1nC1.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-CRmLLwAE.js → Marketplace-DANdmW7l.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-DkOFkSO1.js → McpTools-Bi_EKkb5.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-M29Kd9TU.js → Memory-DecoMbYw.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-Tl6JvPw_.js → MobileBridge-DrOhFKp0.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-rRwP16dN.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-DkUjX1F5.js → Mtc-SLPII1iw.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-BCqr1I4q.js → MtcAudit-DWoVORYy.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-DmoqYGQ5.js → Multisig-Dxpnmq-m.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DNRQeRke.js → NLProgramming-kNTpNqCP.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-CQSBeiY6.js → Notes-D1uXCfVd.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-gNY9bbH6.js → NotificationSettings-D3Qt8Gkl.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-DW0CNjh8.js → OrderTableRenderer-qvaah2-m.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-CthnbKag.js → Organization-EhRl4JoJ.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-_GQ3sbXu.js → Overflow-BTt5C_5W.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-BTr7ea6W.js → P2P-CeR2jyb0.js} +2 -2
  55. package/src/assets/web-panel/assets/PdhVaultBrowser-BzD3OHWq.js +7 -0
  56. package/src/assets/web-panel/assets/{Permissions-DMny2Cjs.js → Permissions-BOPyeRlz.js} +3 -3
  57. package/src/assets/web-panel/assets/{PersonalDataHub-CyYlMSoI.js → PersonalDataHub-DcmNNsfO.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-FASu2Oc4.js → Pipeline-DuL3kwWC.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-9bC5FnsJ.js → Privacy-CkQRdlSU.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BOYZwB5G.js → ProjectInit-moE_vQ1P.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-45DViNG-.js → ProjectSettings-PnTWi4NQ.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-C2fZzMZc.js → Projects-DZvbT3D7.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-BgjvZx9F.js → Providers-CkcrHYy9.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-Brw-3csB.js → QrScannerModal-nYNFGf_H.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-DWG-A5zB.js → QuickAsk-53WNo7xT.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-DuQGxjIR.js → Recommend-CmYN_NaS.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-T1cdSSRL.js → RemoteSession-BuOdNEjA.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-9FT2dI4G.js → Reputation-BgkheZou.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-yNIbNftX.js → Row-DvA3klj1.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-BIchNoPd.js → RssFeed-CgkIL3PG.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-msDJ8UTZ.js → Search-Cv2twJdL.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-DE46oBYT.js → Security-XrD2UNU6.js} +2 -2
  73. package/src/assets/web-panel/assets/{Services-Dori7vc3.js → Services-LujCntCK.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-D3O3sUFz.js → Skeleton-ZZCOsxPX.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-BV0ct8SO.js → Skills-BEXGkOrU.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-DflIVOrh.js → Sla-CIqmCgdD.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Cpbv2Zz6.js → SpeechSettings-DkVbwJox.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-Dgg6-wbe.js → SyncSettings-BVGgUs0p.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-Cb6e4Klx.js → Tasks-C1lh68l5.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-GfdAATGz.js → Templates-BG4zNAFx.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-D0gecLXl.js → Tenant-t2o6i5fY.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-BFRPJt-0.js → Terminal-CH8UDdVO.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-BobtChxX.js → TimelineRenderer-B5sYY1IO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-CifeZtHV.js → Tokens-D0y7QkG5.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-BHBvbBCf.js → Trigger-BA2nKko0.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-6lvqjKYp.js → Trust-CrFKCQIs.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CqzMc-G5.js → UkeySign-B3x_-Zun.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-OIiRG582.js → VideoEditing-TEwxbLH6.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-4MeKq0q_.js → Wallet-Larp5rrt.js} +2 -2
  90. package/src/assets/web-panel/assets/{WebAuthn-eHBRjGQG.js → WebAuthn-DfWbEIZe.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-DlzYdEqW.js → WorkflowEditor-8LKiKExY.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-CBcjOzkX.js → chat-ByxYOvYW.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-zgwsYQSa.js → colors-Dh4yAClh.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-Dcd9H5Bn.js → compact-item-f5n2j1WZ.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-DR1tO-8r.js → createContext-DIprYuXL.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-DRIge35Z.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CtfbUH_p.js → hasIn-CvYeap2I.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D1GxtY-h.js → index-3ROY-lYB.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CT7zIkqP.js → index-6VEWxOyC.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Cdhy5ckh.js → index-7eG1J-GT.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DvPOpvvH.js → index-B0AXUhiJ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cur8LNNn.js → index-BS_dpgzu.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DWG98orp.js → index-BaXSBxy-.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C5XGvJb0.js → index-Bk8oWRk6.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DA2-Bhy8.js → index-BlImvgb7.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CwHgaYVH.js → index-BoayvJ48.js} +1 -1
  107. package/src/assets/web-panel/assets/index-Bq24rTYx.js +1 -0
  108. package/src/assets/web-panel/assets/{index-DHEiuvhA.js → index-BsU46c7D.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvcz0RO.js → index-BvyFQF8I.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D7ZOiIKb.js → index-C2RGykST.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CmzQzTY5.js → index-C5aH2Pj2.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-oWOlF7CM.js → index-CD0s5ESV.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DVpp9YSQ.js → index-CIZQtz8G.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DWuCGhHa.js → index-CJjc8ObW.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-JSfldpw4.js → index-CTqJNul4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-dvF6C39V.js → index-CcvA1nae.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CmVIM-m2.js → index-CpR0zD38.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CaGNqkY0.js → index-CzabQZ7I.js} +1 -1
  119. package/src/assets/web-panel/assets/index-D2f18WsK.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BXfGHUz_.js → index-DGyTzElU.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-LN7Hkl1C.js → index-DI8jC7gQ.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DfzOYmOj.js → index-DUj-sL9U.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BoxowMYS.js → index-DaZVtafQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CSQCHpDo.js → index-DjvON8oh.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B3QUK1ks.js → index-DlJCDpeN.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-J-ORW1i6.js → index-DlTwZIeX.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-z8lrkcb-.js → index-DlULGx8u.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-hTcre7U9.js → index-DmcAL4hd.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DYPMJ5Mb.js → index-DshB3-Up.js} +3 -3
  130. package/src/assets/web-panel/assets/{index-5XaB_Tjg.js → index-Du435yzt.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-C0kP88Pb.js → index-bt-0zMmH.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D8WPohye.js → index-c_CxUQl3.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DeAi4CNt.js → index-fpWv4u3Z.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BndPtS9D.js → index-hwbaD3Nk.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DTx4FyLU.js → index-uDiVjYrS.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CPLLgPxA.js → index-xU0eHLIH.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-CVMtDrWZ.js → initDefaultProps-DRuE92W6.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-Dye3IU8k.js → motion-6Sgma5Vd.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-C251Zo34.js → move-BbQsH8sb.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-BDiOORoQ.js → mtc-parser-BdC4INal.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-DTDA_RHv.js → omit-DeC5m_bi.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-CqEUTPcT.js → pickAttrs-DuehpBws.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-BOB8QWH3.js → placementArrow-CJEhv2ij.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-CZDXP1d9.js → responsiveObserve-Dyw3lDGr.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-DPaRw3E9.js → slide-BMaLXTbS.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-Ba9d7XZ7.js → statusUtils-c1K_SdJH.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-mZKU3lvW.js → styleChecker-CefBry8k.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-VcauLq0M.js → useFlexGapSupport-Dj2IsZpo.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-Cal9KRcx.js → useFs-RLfB3yqb.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-DISoRyQB.js → usePersonalDataHub-DbRVH_NA.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-B58C6w6B.js → vnode-DTn_QVJz.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-BPlBAMKB.js → zoom-DuFR0E7i.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +1 -1
  155. package/src/commands/agent.js +12 -5
  156. package/src/commands/ask.js +96 -21
  157. package/src/commands/complete.js +14 -10
  158. package/src/commands/llm.js +23 -41
  159. package/src/commands/session.js +2 -2
  160. package/src/commands/stream.js +9 -5
  161. package/src/commands/ui.js +30 -2
  162. package/src/data/changelog.json +39 -0
  163. package/src/gateways/ws/llm-chat-protocol.js +18 -10
  164. package/src/gateways/ws/llm-creds.js +29 -79
  165. package/src/gateways/ws/ws-agent-handler.js +1 -1
  166. package/src/gateways/ws/ws-session-gateway.js +26 -12
  167. package/src/lib/__tests__/provider-stream.test.js +70 -4
  168. package/src/lib/chat-core.js +186 -24
  169. package/src/lib/cowork-adapter.js +29 -15
  170. package/src/lib/evolution/agent-evolution-runtime-composition.js +10 -7
  171. package/src/lib/evolution/evolution-deployment-config.js +27 -4
  172. package/src/lib/evolution/evolution-deployment-loader.js +58 -3
  173. package/src/lib/evolution/evolution-deployment-profile.js +2 -2
  174. package/src/lib/evolution/evolution-evidence-projector.js +18 -3
  175. package/src/lib/evolution/evolution-file-witness.js +10 -0
  176. package/src/lib/evolution/evolution-ledger.js +237 -28
  177. package/src/lib/evolution/evolution-windows-directory-durability.js +322 -0
  178. package/src/lib/evolution/governed-hub-llm.js +2 -1
  179. package/src/lib/evolution/governed-model-turn.js +4 -1
  180. package/src/lib/evolution/local-ui-evolution-deployment.js +682 -0
  181. package/src/lib/evolution/local-ui-evolution-host.mjs +26 -0
  182. package/src/lib/evolution/local-ui-evolution-runtime.js +601 -0
  183. package/src/lib/evolution/opaque-hex-id.js +28 -0
  184. package/src/lib/hub-llm-client.js +22 -14
  185. package/src/lib/image-input.js +19 -3
  186. package/src/lib/llm-config-defaults.js +19 -30
  187. package/src/lib/llm-connection-config.js +4 -0
  188. package/src/lib/llm-connection-probe.js +5 -1
  189. package/src/lib/llm-connection-resolution.js +94 -0
  190. package/src/lib/llm-providers.js +33 -11
  191. package/src/lib/ollama-base-url.js +14 -0
  192. package/src/lib/provider-protocol.js +104 -0
  193. package/src/lib/provider-stream.js +163 -4
  194. package/src/lib/runnable-provider.js +1 -0
  195. package/src/repl/chat-repl.js +58 -11
  196. package/src/runtime/agent-core.js +26 -43
  197. package/src/runtime/headless-runner.js +11 -4
  198. package/src/runtime/headless-stream.js +10 -4
  199. package/src/runtime/policies/agent-policy.js +17 -11
  200. package/src/assets/web-panel/assets/ChatBubbleRenderer-DOX3Y5EJ.js +0 -1
  201. package/src/assets/web-panel/assets/MarkdownRenderer-9nfWupWP.js +0 -1
  202. package/src/assets/web-panel/assets/MobileProjects-Bq-FPDzS.js +0 -1
  203. package/src/assets/web-panel/assets/PdhVaultBrowser-D9McHF9I.js +0 -7
  204. package/src/assets/web-panel/assets/devWarning-DYhpRtve.js +0 -1
  205. package/src/assets/web-panel/assets/index-DPJXFIY7.js +0 -1
  206. package/src/assets/web-panel/assets/index-DjBhR5M_.js +0 -1
@@ -0,0 +1,682 @@
1
+ import {
2
+ createPrivateKey,
3
+ createPublicKey,
4
+ generateKeyPairSync,
5
+ sign,
6
+ verify,
7
+ randomBytes,
8
+ randomUUID,
9
+ } from "node:crypto";
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import {
14
+ ensurePrivateDirectory,
15
+ ensurePrivateFile,
16
+ repairPrivatePaths,
17
+ } from "../secure-fs.js";
18
+ import {
19
+ ensureDirectoryTreeDurable,
20
+ createLocalUiEvolutionFileSystem,
21
+ flushWindowsLocalNtfsDirectory,
22
+ } from "./evolution-windows-directory-durability.js";
23
+ import { withFileLockAsync } from "../with-file-lock.js";
24
+ import { formatOpaqueHexId } from "./opaque-hex-id.js";
25
+ import {
26
+ configureEvolutionDeployment,
27
+ getEvolutionDeploymentStatus,
28
+ } from "./evolution-deployment-config.js";
29
+ import {
30
+ computeEvolutionDeploymentDigest,
31
+ EVOLUTION_DEPLOYMENT_DESCRIPTOR_SCHEMA,
32
+ serializeEvolutionDeploymentDescriptorPayload,
33
+ verifyEvolutionDeployment,
34
+ } from "./evolution-deployment-loader.js";
35
+ import {
36
+ assertEvolutionDeploymentActiveTrustRoot,
37
+ assertEvolutionDeploymentDescriptorNotRevoked,
38
+ assertEvolutionDeploymentRevisionFloor,
39
+ readEvolutionDeploymentProfile,
40
+ } from "./evolution-deployment-profile.js";
41
+
42
+ export const LOCAL_UI_EVOLUTION_SCHEMA =
43
+ "chainlesschain.local-ui-evolution-host/v1";
44
+ const LOCAL_UI_BOOTSTRAP_STATE_SCHEMA =
45
+ "chainlesschain.local-ui-evolution-bootstrap-state/v1";
46
+
47
+ const LOCAL_UI_HOST_MODULE_PATH = fileURLToPath(
48
+ new URL("./local-ui-evolution-host.mjs", import.meta.url),
49
+ );
50
+ const SIGNING_KEY_NAMES = Object.freeze([
51
+ "deployment-root",
52
+ "artifact",
53
+ "ledger",
54
+ "witness",
55
+ "attestation",
56
+ "source",
57
+ ]);
58
+ const SECRET_KEY_NAMES = Object.freeze(["commitment", "raw-encryption"]);
59
+ const localWindowsAclSnapshots = new Map();
60
+ const deploymentFileSystem = createLocalUiEvolutionFileSystem();
61
+
62
+ function canonical(value) {
63
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
64
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
65
+ return `{${Object.keys(value)
66
+ .sort()
67
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
68
+ .join(",")}}`;
69
+ }
70
+
71
+ function privateDirectory(directory) {
72
+ ensureDirectoryTreeDurable(deploymentFileSystem, directory);
73
+ ensurePrivateDirectory(directory, {
74
+ applyWindowsAcl: true,
75
+ failIfUnavailable: true,
76
+ });
77
+ if (process.platform === "win32") {
78
+ flushWindowsLocalNtfsDirectory(path.dirname(directory));
79
+ flushWindowsLocalNtfsDirectory(directory);
80
+ }
81
+ }
82
+
83
+ function flushDeploymentDirectory(directory) {
84
+ if (process.platform === "win32") {
85
+ return flushWindowsLocalNtfsDirectory(directory);
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function writePrivateFile(filePath, value) {
91
+ const directory = path.dirname(filePath);
92
+ const temporaryPath = path.join(
93
+ directory,
94
+ `.${path.basename(filePath)}.${randomUUID()}.tmp`,
95
+ );
96
+ let descriptor = null;
97
+ try {
98
+ descriptor = fs.openSync(
99
+ temporaryPath,
100
+ fs.constants.O_WRONLY |
101
+ fs.constants.O_CREAT |
102
+ fs.constants.O_EXCL |
103
+ Number(fs.constants.O_NOFOLLOW || 0),
104
+ 0o600,
105
+ );
106
+ fs.writeFileSync(descriptor, value, "utf8");
107
+ fs.fsyncSync(descriptor);
108
+ fs.closeSync(descriptor);
109
+ descriptor = null;
110
+ ensurePrivateFile(temporaryPath, {
111
+ applyWindowsAcl: false,
112
+ failIfUnavailable: true,
113
+ });
114
+ fs.renameSync(temporaryPath, filePath);
115
+ ensurePrivateFile(filePath, {
116
+ applyWindowsAcl: false,
117
+ failIfUnavailable: true,
118
+ });
119
+ flushDeploymentDirectory(directory);
120
+ } catch (error) {
121
+ if (descriptor !== null) fs.closeSync(descriptor);
122
+ try {
123
+ fs.unlinkSync(temporaryPath);
124
+ } catch {
125
+ // Preserve the original persistence error.
126
+ }
127
+ throw error;
128
+ }
129
+ }
130
+
131
+ function readPrivateFile(filePath) {
132
+ ensurePrivateFile(filePath, {
133
+ applyWindowsAcl: false,
134
+ failIfUnavailable: true,
135
+ });
136
+ const stat = fs.lstatSync(filePath);
137
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
138
+ throw new Error("Local Evolution key file is not a private regular file");
139
+ }
140
+ return fs.readFileSync(filePath, "utf8");
141
+ }
142
+
143
+ function keyPaths(directory) {
144
+ const keysDirectory = path.join(directory, "keys");
145
+ return Object.freeze({
146
+ keysDirectory,
147
+ signing: Object.freeze(
148
+ Object.fromEntries(
149
+ SIGNING_KEY_NAMES.map((name) => [
150
+ name,
151
+ path.join(keysDirectory, `${name}.private.pem`),
152
+ ]),
153
+ ),
154
+ ),
155
+ secrets: Object.freeze(
156
+ Object.fromEntries(
157
+ SECRET_KEY_NAMES.map((name) => [
158
+ name,
159
+ path.join(keysDirectory, `${name}.secret`),
160
+ ]),
161
+ ),
162
+ ),
163
+ });
164
+ }
165
+
166
+ function generateKeyPairPem() {
167
+ const pair = generateKeyPairSync("ed25519");
168
+ return Object.freeze({
169
+ privateKey: pair.privateKey.export({ type: "pkcs8", format: "pem" }),
170
+ publicKey: pair.publicKey.export({ type: "spki", format: "pem" }),
171
+ });
172
+ }
173
+
174
+ function writeKeys(directory) {
175
+ const paths = keyPaths(directory);
176
+ privateDirectory(paths.keysDirectory);
177
+ const keys = Object.create(null);
178
+ for (const name of SIGNING_KEY_NAMES) {
179
+ const pair = generateKeyPairPem();
180
+ keys[name] = pair;
181
+ writePrivateFile(paths.signing[name], pair.privateKey);
182
+ }
183
+ for (const name of SECRET_KEY_NAMES) {
184
+ const secret = randomBytes(32);
185
+ keys[name] = secret;
186
+ writePrivateFile(paths.secrets[name], secret.toString("base64"));
187
+ }
188
+ return Object.freeze({ paths, keys });
189
+ }
190
+
191
+ function readKeys(directory) {
192
+ const paths = keyPaths(directory);
193
+ const keys = Object.create(null);
194
+ for (const name of SIGNING_KEY_NAMES) {
195
+ const privateKey = readPrivateFile(paths.signing[name]);
196
+ const publicKey = createPublicKey(createPrivateKey(privateKey)).export({
197
+ type: "spki",
198
+ format: "pem",
199
+ });
200
+ keys[name] = Object.freeze({ privateKey, publicKey });
201
+ }
202
+ for (const name of SECRET_KEY_NAMES) {
203
+ const raw = Buffer.from(readPrivateFile(paths.secrets[name]).trim(), "base64");
204
+ if (raw.length !== 32) throw new Error("Local Evolution secret key is invalid");
205
+ keys[name] = raw;
206
+ }
207
+ return Object.freeze({ paths, keys: Object.freeze(keys) });
208
+ }
209
+
210
+ function keyBindings(keys) {
211
+ return Object.freeze({
212
+ signing: Object.freeze(
213
+ Object.fromEntries(
214
+ SIGNING_KEY_NAMES.map((name) => [
215
+ name,
216
+ computeEvolutionDeploymentDigest(
217
+ Buffer.from(keys[name].publicKey, "utf8"),
218
+ ),
219
+ ]),
220
+ ),
221
+ ),
222
+ secrets: Object.freeze(
223
+ Object.fromEntries(
224
+ SECRET_KEY_NAMES.map((name) => [
225
+ name,
226
+ computeEvolutionDeploymentDigest(keys[name]),
227
+ ]),
228
+ ),
229
+ ),
230
+ });
231
+ }
232
+
233
+ function signedValue(privateKeyPem, value) {
234
+ return sign(null, Buffer.from(canonical(value), "utf8"), privateKeyPem).toString(
235
+ "base64url",
236
+ );
237
+ }
238
+
239
+ function verifySignedValue(publicKeyPem, value, signature) {
240
+ return (
241
+ typeof signature === "string" &&
242
+ verify(
243
+ null,
244
+ Buffer.from(canonical(value), "utf8"),
245
+ publicKeyPem,
246
+ Buffer.from(signature, "base64url"),
247
+ )
248
+ );
249
+ }
250
+
251
+ function pathSnapshot(paths) {
252
+ return paths.map((filePath) => {
253
+ const stat = fs.lstatSync(filePath);
254
+ return [
255
+ path.resolve(filePath),
256
+ stat.dev,
257
+ stat.ino,
258
+ stat.nlink,
259
+ stat.size,
260
+ stat.mtimeMs,
261
+ stat.ctimeMs,
262
+ ];
263
+ });
264
+ }
265
+
266
+ function secureLocalPaths(paths) {
267
+ const root = path.resolve(paths[0]);
268
+ if (process.platform === "win32") {
269
+ const previous = localWindowsAclSnapshots.get(root);
270
+ if (previous) {
271
+ try {
272
+ const current = pathSnapshot(paths);
273
+ if (JSON.stringify(current) === JSON.stringify(previous)) return;
274
+ } catch {
275
+ // A missing/replaced path must pass through the full safe repair.
276
+ }
277
+ }
278
+ }
279
+ repairPrivatePaths(paths);
280
+ if (process.platform === "win32") {
281
+ localWindowsAclSnapshots.set(root, pathSnapshot(paths));
282
+ }
283
+ }
284
+
285
+ function bootstrapStatePath(profilePath) {
286
+ return path.join(path.dirname(profilePath), "local-ui-host.state.json");
287
+ }
288
+
289
+ function bootstrapStateValue(deployment, phase) {
290
+ const core = {
291
+ schema: LOCAL_UI_BOOTSTRAP_STATE_SCHEMA,
292
+ phase,
293
+ tenantId: deployment.tenantId,
294
+ moduleDigest: deployment.moduleDigest,
295
+ trustRootDigest: deployment.descriptor.trustRootDigest,
296
+ };
297
+ return {
298
+ ...core,
299
+ signature: signedValue(
300
+ deployment.keys["deployment-root"].privateKey,
301
+ core,
302
+ ),
303
+ };
304
+ }
305
+
306
+ function writeBootstrapState(profilePath, deployment, phase) {
307
+ writePrivateFile(
308
+ bootstrapStatePath(profilePath),
309
+ `${JSON.stringify(bootstrapStateValue(deployment, phase), null, 2)}\n`,
310
+ );
311
+ }
312
+
313
+ function writeDeployment(directory, finalDirectory, tenantId) {
314
+ const { keys } = writeKeys(directory);
315
+ const publicRoot = keys["deployment-root"].publicKey;
316
+ const publicRootPath = path.join(finalDirectory, "deployment-public.pem");
317
+ const descriptorPath = path.join(finalDirectory, "deployment.json");
318
+ const modulePath = path.join(finalDirectory, "ui-host.mjs");
319
+ const moduleBytes = fs.readFileSync(LOCAL_UI_HOST_MODULE_PATH);
320
+ writePrivateFile(path.join(directory, "ui-host.mjs"), moduleBytes);
321
+ const descriptor = {
322
+ schema: EVOLUTION_DEPLOYMENT_DESCRIPTOR_SCHEMA,
323
+ revision: 1,
324
+ modulePath,
325
+ moduleDigest: computeEvolutionDeploymentDigest(moduleBytes),
326
+ trustRootDigest: computeEvolutionDeploymentDigest(publicRoot),
327
+ commands: ["ui"],
328
+ };
329
+ descriptor.signature = sign(
330
+ null,
331
+ Buffer.from(serializeEvolutionDeploymentDescriptorPayload(descriptor), "utf8"),
332
+ keys["deployment-root"].privateKey,
333
+ ).toString("base64");
334
+
335
+ writePrivateFile(path.join(directory, "deployment-public.pem"), publicRoot);
336
+ writePrivateFile(
337
+ path.join(directory, "deployment.json"),
338
+ `${JSON.stringify(descriptor, null, 2)}\n`,
339
+ );
340
+ const core = {
341
+ schema: LOCAL_UI_EVOLUTION_SCHEMA,
342
+ tenantId,
343
+ modulePath,
344
+ moduleDigest: descriptor.moduleDigest,
345
+ trustRootDigest: descriptor.trustRootDigest,
346
+ keyBindings: keyBindings(keys),
347
+ descriptorDigest: computeEvolutionDeploymentDigest(
348
+ Buffer.from(JSON.stringify(descriptor, null, 2) + "\n", "utf8"),
349
+ ),
350
+ };
351
+ const manifest = {
352
+ ...core,
353
+ signature: signedValue(keys["deployment-root"].privateKey, core),
354
+ };
355
+ writePrivateFile(
356
+ path.join(directory, "local-host-manifest.json"),
357
+ `${JSON.stringify(manifest, null, 2)}\n`,
358
+ );
359
+ const deployment = Object.freeze({
360
+ descriptorPath: path.join(finalDirectory, "deployment.json"),
361
+ trustRootPath: publicRootPath,
362
+ tenantId,
363
+ moduleDigest: descriptor.moduleDigest,
364
+ descriptor,
365
+ keys,
366
+ });
367
+ writeBootstrapState(
368
+ path.join(path.dirname(finalDirectory), "deployment-profile.json"),
369
+ deployment,
370
+ "prepared",
371
+ );
372
+ return deployment;
373
+ }
374
+
375
+ async function verifyLocalDeployment(
376
+ directory,
377
+ expectedDirectory,
378
+ { repairPermissions = true } = {},
379
+ ) {
380
+ const descriptorPath = path.join(directory, "deployment.json");
381
+ const trustRootPath = path.join(directory, "deployment-public.pem");
382
+ const manifestPath = path.join(directory, "local-host-manifest.json");
383
+ const paths = keyPaths(directory);
384
+ const modulePath = path.join(expectedDirectory, "ui-host.mjs");
385
+ const storedModulePath = path.join(directory, "ui-host.mjs");
386
+ const statePath = bootstrapStatePath(
387
+ path.join(path.dirname(expectedDirectory), "deployment-profile.json"),
388
+ );
389
+ const protectedPaths = [
390
+ directory,
391
+ paths.keysDirectory,
392
+ ...Object.values(paths.signing),
393
+ ...Object.values(paths.secrets),
394
+ trustRootPath,
395
+ descriptorPath,
396
+ manifestPath,
397
+ storedModulePath,
398
+ ...(fs.existsSync(statePath) ? [statePath] : []),
399
+ ];
400
+ if (repairPermissions) {
401
+ secureLocalPaths(protectedPaths);
402
+ }
403
+ const rootPublic = readPrivateFile(trustRootPath);
404
+ const rootPrivate = readPrivateFile(paths.signing["deployment-root"]);
405
+ const keyChallenge = randomBytes(32);
406
+ if (
407
+ !verify(
408
+ null,
409
+ keyChallenge,
410
+ rootPublic,
411
+ sign(null, keyChallenge, rootPrivate),
412
+ )
413
+ ) {
414
+ throw new Error("Local Evolution root signing key does not match its trust root");
415
+ }
416
+ const manifest = JSON.parse(readPrivateFile(manifestPath));
417
+ const { signature, ...core } = manifest;
418
+ if (
419
+ manifest.schema !== LOCAL_UI_EVOLUTION_SCHEMA ||
420
+ typeof manifest.tenantId !== "string" ||
421
+ !manifest.tenantId.startsWith("local-ui-") ||
422
+ !verifySignedValue(rootPublic, core, signature)
423
+ ) {
424
+ throw new Error("Local Evolution host manifest signature is invalid");
425
+ }
426
+ const descriptorBytes = Buffer.from(readPrivateFile(descriptorPath), "utf8");
427
+ const verified = await verifyEvolutionDeployment(
428
+ { descriptorPath, trustRootPath },
429
+ {
430
+ resolveRealPath: async (candidate) =>
431
+ path.resolve(candidate) === path.resolve(modulePath)
432
+ ? modulePath
433
+ : fs.promises.realpath(candidate),
434
+ read: async (candidate) =>
435
+ path.resolve(candidate) === path.resolve(modulePath)
436
+ ? fs.promises.readFile(storedModulePath)
437
+ : fs.promises.readFile(candidate),
438
+ },
439
+ );
440
+ const descriptor = verified.descriptor;
441
+ const moduleDigest = computeEvolutionDeploymentDigest(
442
+ fs.readFileSync(storedModulePath),
443
+ );
444
+ const loadedKeys = readKeys(directory);
445
+ let bootstrapPhase = null;
446
+ if (fs.existsSync(statePath)) {
447
+ const state = JSON.parse(readPrivateFile(statePath));
448
+ const { signature: stateSignature, ...stateCore } = state;
449
+ if (
450
+ state.schema !== LOCAL_UI_BOOTSTRAP_STATE_SCHEMA ||
451
+ !["prepared", "active"].includes(state.phase) ||
452
+ state.tenantId !== manifest.tenantId ||
453
+ state.moduleDigest !== moduleDigest ||
454
+ state.trustRootDigest !== descriptor.trustRootDigest ||
455
+ !verifySignedValue(rootPublic, stateCore, stateSignature)
456
+ ) {
457
+ throw new Error("Local Evolution bootstrap state signature is invalid");
458
+ }
459
+ bootstrapPhase = state.phase;
460
+ }
461
+ const isStagedAtExpectedSibling =
462
+ path.basename(directory) === "local-ui-host.init" &&
463
+ path.dirname(path.resolve(directory)) ===
464
+ path.dirname(path.resolve(expectedDirectory));
465
+ if (
466
+ (path.resolve(expectedDirectory) !== path.resolve(directory) &&
467
+ !isStagedAtExpectedSibling) ||
468
+ manifest.modulePath !== modulePath ||
469
+ manifest.moduleDigest !== moduleDigest ||
470
+ manifest.trustRootDigest !== descriptor.trustRootDigest ||
471
+ canonical(manifest.keyBindings) !== canonical(keyBindings(loadedKeys.keys)) ||
472
+ manifest.descriptorDigest !== computeEvolutionDeploymentDigest(descriptorBytes) ||
473
+ descriptor.modulePath !== modulePath ||
474
+ descriptor.moduleDigest !== moduleDigest ||
475
+ descriptor.commands.length !== 1 ||
476
+ descriptor.commands[0] !== "ui"
477
+ ) {
478
+ throw new Error("Local Evolution host files do not match their signed identity");
479
+ }
480
+ return Object.freeze({
481
+ descriptorPath,
482
+ trustRootPath,
483
+ tenantId: manifest.tenantId,
484
+ descriptor,
485
+ moduleDigest,
486
+ bootstrapPhase,
487
+ keys: loadedKeys.keys,
488
+ });
489
+ }
490
+
491
+ function localDirectory(profilePath) {
492
+ return path.join(path.dirname(profilePath), "local-ui-host");
493
+ }
494
+
495
+ function hasDeploymentOverrides(env) {
496
+ return Boolean(
497
+ env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_DESCRIPTOR ||
498
+ env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_TRUST_ROOT,
499
+ );
500
+ }
501
+
502
+ function removeIncompleteStage(stagePath, expectedParent) {
503
+ if (!fs.existsSync(stagePath)) return;
504
+ const resolvedStage = path.resolve(stagePath);
505
+ const resolvedParent = path.resolve(expectedParent);
506
+ if (
507
+ path.dirname(resolvedStage) !== resolvedParent ||
508
+ path.basename(resolvedStage) !== "local-ui-host.init"
509
+ ) {
510
+ throw new Error("Local Evolution initialization path is unsafe");
511
+ }
512
+ const info = fs.lstatSync(resolvedStage);
513
+ if (info.isSymbolicLink() || !info.isDirectory()) {
514
+ throw new Error("Local Evolution initialization state is unsafe");
515
+ }
516
+ fs.rmSync(resolvedStage, { recursive: true, force: true });
517
+ flushDeploymentDirectory(resolvedParent);
518
+ }
519
+
520
+ /** Create a per-user, locally signed UI host only for a genuinely empty profile. */
521
+ export async function initializeDefaultLocalUiEvolutionDeployment(options = {}) {
522
+ const env = options.env || process.env;
523
+ const cwd = options.cwd || process.cwd();
524
+ if (hasDeploymentOverrides(env)) return null;
525
+ const profileResult = await readEvolutionDeploymentProfile({ env, cwd });
526
+ if (profileResult.error) throw new Error(profileResult.error);
527
+ if (profileResult.profile) {
528
+ if (profileResult.profile.deploymentMode !== "local") return null;
529
+ if (!profileResult.profile.enabled) return null;
530
+ const current = await loadLocalUiEvolutionDeployment(
531
+ localDirectory(profileResult.filePath),
532
+ { env, cwd },
533
+ );
534
+ return Object.freeze({
535
+ status: await getEvolutionDeploymentStatus({ env, cwd }),
536
+ deployment: current,
537
+ });
538
+ }
539
+
540
+ const profilePath = profileResult.filePath;
541
+ const parent = path.dirname(profilePath);
542
+ const destination = localDirectory(profilePath);
543
+ const stagePath = path.join(parent, "local-ui-host.init");
544
+ const statePath = bootstrapStatePath(profilePath);
545
+ privateDirectory(parent);
546
+ return withFileLockAsync(
547
+ `${profilePath}.local-init`,
548
+ async () => {
549
+ const latest = await readEvolutionDeploymentProfile({ env, cwd });
550
+ if (latest.error) throw new Error(latest.error);
551
+ if (latest.profile) {
552
+ if (
553
+ latest.profile.deploymentMode !== "local" ||
554
+ !latest.profile.enabled
555
+ )
556
+ return null;
557
+ const current = await loadLocalUiEvolutionDeployment(destination, {
558
+ env,
559
+ cwd,
560
+ });
561
+ return Object.freeze({
562
+ status: await getEvolutionDeploymentStatus({ env, cwd }),
563
+ deployment: current,
564
+ });
565
+ }
566
+
567
+ let deployment = null;
568
+ if (fs.existsSync(destination)) {
569
+ deployment = await verifyLocalDeployment(destination, destination);
570
+ if (deployment.bootstrapPhase !== "prepared") return null;
571
+ } else if (fs.existsSync(statePath)) {
572
+ if (!fs.existsSync(stagePath)) return null;
573
+ deployment = await verifyLocalDeployment(stagePath, destination, {
574
+ repairPermissions: false,
575
+ });
576
+ if (deployment.bootstrapPhase !== "prepared") return null;
577
+ fs.renameSync(stagePath, destination);
578
+ flushDeploymentDirectory(parent);
579
+ deployment = await verifyLocalDeployment(destination, destination);
580
+ } else {
581
+ removeIncompleteStage(stagePath, parent);
582
+ fs.mkdirSync(stagePath, { mode: 0o700 });
583
+ privateDirectory(stagePath);
584
+ const tenantId = `local-ui-${formatOpaqueHexId(
585
+ randomUUID().replaceAll("-", ""),
586
+ )}`;
587
+ const paths = writeDeployment(stagePath, destination, tenantId);
588
+ deployment = await verifyLocalDeployment(stagePath, destination, {
589
+ repairPermissions: false,
590
+ });
591
+ fs.renameSync(stagePath, destination);
592
+ flushDeploymentDirectory(parent);
593
+ deployment = await verifyLocalDeployment(destination, destination);
594
+ if (
595
+ deployment.descriptorPath !== paths.descriptorPath ||
596
+ deployment.trustRootPath !== paths.trustRootPath
597
+ ) {
598
+ throw new Error("Local Evolution deployment paths changed during initialization");
599
+ }
600
+ }
601
+ if (deployment.bootstrapPhase !== "prepared") return null;
602
+
603
+ let status;
604
+ try {
605
+ status = await configureEvolutionDeployment(
606
+ {
607
+ descriptorPath: deployment.descriptorPath,
608
+ trustRootPath: deployment.trustRootPath,
609
+ deploymentMode: "local",
610
+ enabled: true,
611
+ },
612
+ { env, cwd, expectedProfileAbsent: true },
613
+ );
614
+ flushDeploymentDirectory(parent);
615
+ } catch (error) {
616
+ if (
617
+ error?.code === "EVOLUTION_DEPLOYMENT_PROFILE_ALREADY_CONFIGURED"
618
+ ) {
619
+ return null;
620
+ }
621
+ throw error;
622
+ }
623
+ writeBootstrapState(profilePath, deployment, "active");
624
+ return Object.freeze({ status, deployment });
625
+ },
626
+ {
627
+ failIfUnavailable: true,
628
+ timeoutMs: 180_000,
629
+ retryMs: 50,
630
+ maxRetryMs: 250,
631
+ retryJitterMs: 25,
632
+ },
633
+ );
634
+ }
635
+
636
+ /** Reverify the enabled profile and its signed local identity without mutation. */
637
+ export async function loadLocalUiEvolutionDeployment(
638
+ directory,
639
+ { env = process.env, cwd = process.cwd() } = {},
640
+ ) {
641
+ const saved = await readEvolutionDeploymentProfile({ env, cwd });
642
+ if (saved.error || !saved.profile) {
643
+ throw new Error(saved.error || "Local Evolution profile is unavailable");
644
+ }
645
+ if (saved.profile.deploymentMode !== "local" || !saved.profile.enabled) {
646
+ throw new Error("Local Evolution profile is not enabled");
647
+ }
648
+ const expectedDirectory = localDirectory(saved.filePath);
649
+ if (path.resolve(directory) !== path.resolve(expectedDirectory)) {
650
+ throw new Error("Local Evolution profile points to another host directory");
651
+ }
652
+ const deployment = await verifyLocalDeployment(directory, expectedDirectory);
653
+ const [
654
+ profileDescriptorPath,
655
+ profileTrustRootPath,
656
+ deploymentDescriptorPath,
657
+ deploymentTrustRootPath,
658
+ ] = await Promise.all([
659
+ fs.promises.realpath(saved.profile.descriptorPath),
660
+ fs.promises.realpath(saved.profile.trustRootPath),
661
+ fs.promises.realpath(deployment.descriptorPath),
662
+ fs.promises.realpath(deployment.trustRootPath),
663
+ ]);
664
+ if (
665
+ profileDescriptorPath !== deploymentDescriptorPath ||
666
+ profileTrustRootPath !== deploymentTrustRootPath ||
667
+ saved.profile.activeTrustRootDigest !==
668
+ deployment.descriptor.trustRootDigest
669
+ ) {
670
+ throw new Error("Local Evolution profile identity does not match its signed host");
671
+ }
672
+ assertEvolutionDeploymentActiveTrustRoot(saved.profile, deployment.descriptor);
673
+ assertEvolutionDeploymentDescriptorNotRevoked(
674
+ saved.profile,
675
+ deployment.descriptor,
676
+ );
677
+ assertEvolutionDeploymentRevisionFloor(saved.profile, deployment.descriptor);
678
+ if (deployment.bootstrapPhase !== "active") {
679
+ writeBootstrapState(saved.filePath, deployment, "active");
680
+ }
681
+ return deployment;
682
+ }
@@ -0,0 +1,26 @@
1
+ // This entrypoint is signed into a per-user local deployment on first `cc ui`.
2
+ // Its only command capability is the interactive UI model-egress path.
3
+ export async function createChainlessChainCommandDependencies({
4
+ commandName,
5
+ descriptor,
6
+ factories,
7
+ }) {
8
+ if (commandName !== "ui") {
9
+ throw new Error("Local Evolution host only admits the ui command");
10
+ }
11
+ const createComposition = factories?.createLocalUiEvolutionComposition;
12
+ if (typeof createComposition !== "function") {
13
+ throw new Error("Local UI Evolution host factory is unavailable");
14
+ }
15
+ return Object.freeze({
16
+ evolutionCompositionFactory: async (context) => {
17
+ if (context?.mode !== "ws-llm-chat") {
18
+ throw new Error("Local Evolution host only admits Quick Ask model turns");
19
+ }
20
+ return createComposition({
21
+ context,
22
+ handlerArtifactDigest: descriptor.moduleDigest,
23
+ });
24
+ },
25
+ });
26
+ }