@runeya/runeya 2.0.129 → 2.0.131

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 (356) hide show
  1. package/agent/index.js +245 -177
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-QQHA3TUB.js +11 -0
  4. package/{chat-6ETYBINN.js → chat-VIVAFYYS.js} +7 -7
  5. package/{chunk-B7MABGQN.js → chunk-4RMM2OUU.js} +29 -2
  6. package/chunk-4RMM2OUU.js.map +1 -0
  7. package/{chunk-RDE3X3J4.js → chunk-B3QQGYKU.js} +83 -12
  8. package/chunk-B3QQGYKU.js.map +1 -0
  9. package/{chunk-K2KSZRSI.js → chunk-CVVAQTMF.js} +6 -6
  10. package/{chunk-J2L6VONF.js → chunk-GAK3Q3J4.js} +13 -53
  11. package/chunk-GAK3Q3J4.js.map +1 -0
  12. package/{chunk-TTJNHZ6U.js → chunk-GNVQH3VG.js} +2 -2
  13. package/{chunk-7GBSCYXU.js → chunk-IYPYVSY5.js} +79 -14
  14. package/chunk-IYPYVSY5.js.map +1 -0
  15. package/{companion-SH4HLD6G.js → companion-2VDWE5SP.js} +5 -4
  16. package/{desktop-launcher-7UZF7PFH.js → desktop-launcher-RSRAXX6B.js} +2 -2
  17. package/index.js +5 -5
  18. package/mcp-server/index.js +132 -28
  19. package/mcp-server/index.js.map +1 -1
  20. package/{node-ILM6MV56.js → node-JU7A5NDT.js} +6 -2
  21. package/package.json +1 -1
  22. package/{settings-manager-XD6U4QN3.js → settings-manager-3LMN34IT.js} +3 -3
  23. package/{src-BNWVTXCY.js → src-B4AVQCOQ.js} +43 -15
  24. package/src-B4AVQCOQ.js.map +1 -0
  25. package/web/assets/{AgentScalarChatInterface.vue-CSnavtlu.js → AgentScalarChatInterface.vue-CsQIfwOB.js} +3 -3
  26. package/web/assets/AgentTerminalPage-MFiSz_rt.js +1 -0
  27. package/web/assets/AiChatPage-YYiU2Rua.js +1 -0
  28. package/web/assets/AppLayout-CNeI8_tj.js +1 -0
  29. package/web/assets/AuditPage-Dpy84SYO.js +1 -0
  30. package/web/assets/CloudAccountPage-CmsKXPgr.js +1 -0
  31. package/web/assets/CloudAccountPage-vlTrRoPG.js +1 -0
  32. package/web/assets/CloudMessagingPage-DtPDjm-i.js +2 -0
  33. package/web/assets/DashboardPage-BfGv9gYt.js +1 -0
  34. package/web/assets/DirectoryPickerDialog-DrR_sBln.js +1 -0
  35. package/web/assets/{EditorIcon-CtAICHdD.js → EditorIcon-CDOVftY6.js} +2 -2
  36. package/web/assets/EnvironmentForm-CyNV0uZa.js +108 -0
  37. package/web/assets/EnvironmentFormPage-DWDvoFtt.js +1 -0
  38. package/web/assets/EnvironmentListPage-B-2RGzi7.js +1 -0
  39. package/web/assets/KanbanBoardPage-ByPh1kiv.css +1 -0
  40. package/web/assets/KanbanBoardPage-ClTruaQ9.js +4 -0
  41. package/web/assets/KanbanListPage-Bl21zyCc.js +1 -0
  42. package/web/assets/KanbanListPage-pCWYPwBv.css +1 -0
  43. package/web/assets/LaunchProjectPage-Cxx-fhzO.js +1 -0
  44. package/web/assets/LaunchServicesPage-VdZTXZlX.js +1 -0
  45. package/web/assets/LogViewer-MDkKX5-F.js +3 -0
  46. package/web/assets/OrgKeyPrompt-BbPXlqYa.js +1 -0
  47. package/web/assets/OrgLogo-DFdWtiJh.js +1 -0
  48. package/web/assets/ProcessControls-BpwS8iyb.js +1 -0
  49. package/web/assets/ProcessDetailPage-Cx3RFAvr.js +1 -0
  50. package/web/assets/{ProcessMetrics-D4CYOC7y.js → ProcessMetrics-DJ5p8cOH.js} +2 -2
  51. package/web/assets/RandomJobTestPage-CN8v2j2H.js +2 -0
  52. package/web/assets/ScenarioEditorPage-DE4kRBuV.js +14 -0
  53. package/web/assets/SendToChatDialog-8baAtZJn.js +1 -0
  54. package/web/assets/ServiceControlsBar-CvcG0cX2.js +1 -0
  55. package/web/assets/ServiceDetailPage-Dhr3CMPx.js +2 -0
  56. package/web/assets/ServiceFormPage-BpKSXmQj.js +1 -0
  57. package/web/assets/SettingsPage-EHsyWY9H.js +2063 -0
  58. package/web/assets/UnlockPage-BEyjPuiA.js +1 -0
  59. package/web/assets/VaultMigrationPage-BL1-Q6M-.js +1 -0
  60. package/web/assets/VersionBadge-C4M_NoTo.js +1 -0
  61. package/web/assets/WikiPage-BIy78RnQ.js +1 -0
  62. package/web/assets/WikiView-hMl0Jo_9.js +1 -0
  63. package/web/assets/add-scalar-classes-CfYvUPFK.js +387 -0
  64. package/web/assets/{autocomplete-C3lpxqut.js → autocomplete-DwJDpd_3.js} +3 -3
  65. package/web/assets/{basic-shape-diamond.svg-ClLmXFVS.js → basic-shape-diamond.svg-BHIWRSBW.js} +1 -1
  66. package/web/assets/{basic-shape-hexagon.svg-Cag0ncSY.js → basic-shape-hexagon.svg-BCqMu4vI.js} +1 -1
  67. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg--KHtWYXD.js → basic-shape-primary-circle-ellipse-round.svg-DkTApMxk.js} +1 -1
  68. package/web/assets/{basic-shape-primary-square-rectangle.svg-BPCtuGhT.js → basic-shape-primary-square-rectangle.svg-Bk_gOdKn.js} +1 -1
  69. package/web/assets/{basic-shape-shield.svg-CLqZM1B0.js → basic-shape-shield.svg-BEFRYMev.js} +1 -1
  70. package/web/assets/{browser-DIa6q3Vr.js → browser-CRhcCyEg.js} +1 -1
  71. package/web/assets/{browser-C1T_MRFP.js → browser-CrG_hVEM.js} +1 -1
  72. package/web/assets/card-DM6OLGrd.js +32 -0
  73. package/web/assets/{checkbox-_b4TMklW.js → checkbox-BXP57wuC.js} +3 -3
  74. package/web/assets/{chip-B-w-r79z.js → chip-usPF87e4.js} +2 -2
  75. package/web/assets/{client-D3ljCsOD.js → client-DbOX6SLW.js} +2 -2
  76. package/web/assets/cloud-chat.store-D_tehZfH.js +1 -0
  77. package/web/assets/cloud-client-D_2Ghoat.js +1 -0
  78. package/web/assets/cloud-session-5zD-9FVh.js +1 -0
  79. package/web/assets/{cloud-session-DwRT1TUx.js → cloud-session-BjxvzZ5_.js} +2 -2
  80. package/web/assets/column-BpmV6tZf.js +763 -0
  81. package/web/assets/composables-Biguv8GA.js +1 -0
  82. package/web/assets/{computer-device-desktop-monitor.svg-CJd6rSnq.js → computer-device-desktop-monitor.svg-CMCr-EbE.js} +1 -1
  83. package/web/assets/{computer-device-desktop.svg-BKuN8rEx.js → computer-device-desktop.svg-bHjL2yIc.js} +1 -1
  84. package/web/assets/{computer-device-laptop.svg-BnP-6SWj.js → computer-device-laptop.svg-CMIGmOQb.js} +1 -1
  85. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-C_LwWGml.js → computer-device-mobile-phone-android-samsung-back.svg-BmDp3ARt.js} +1 -1
  86. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-DyLuuVRr.js → computer-device-mobile-phone-android-samsung.svg-BE8rx7DK.js} +1 -1
  87. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-CV8PoCn1.js → computer-device-mobile-phone-iphone-x-back.svg-Cp8Dbmih.js} +1 -1
  88. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-Bnnn0L-m.js → computer-device-mobile-phone-iphone-x.svg-CXXBfAWJ.js} +1 -1
  89. package/web/assets/{computer-device-mobile-tablet-touch.svg-P64zwUoG.js → computer-device-mobile-tablet-touch.svg-CeynlokP.js} +1 -1
  90. package/web/assets/{computer-device-mobile-tablet.svg-LMxuwUja.js → computer-device-mobile-tablet.svg-D5XjPVoW.js} +1 -1
  91. package/web/assets/{computer-device-network-ethernet-cat6.svg-DwdZZW5b.js → computer-device-network-ethernet-cat6.svg-Cbs45-9L.js} +1 -1
  92. package/web/assets/{computer-device-network-lan-www.svg-CeDIIipH.js → computer-device-network-lan-www.svg-Rexix5Rm.js} +1 -1
  93. package/web/assets/{computer-device-network-wifi-connection.svg-DbcpscUN.js → computer-device-network-wifi-connection.svg-3vuo7eWK.js} +1 -1
  94. package/web/assets/{computer-device-network-wifi-router.svg-lMEz8IbU.js → computer-device-network-wifi-router.svg-B_MKwO49.js} +1 -1
  95. package/web/assets/{cssMode-BzrUX3y9.js → cssMode-BqcfXzGG.js} +1 -1
  96. package/web/assets/{divider-DYwsSqGG.js → divider-BRuXeRHe.js} +2 -2
  97. package/web/assets/{docx-preview-DUjMjOuO.js → docx-preview-BoM3W7oO.js} +1 -1
  98. package/web/assets/{ecology-science-erlenmeyer-flask.svg-aaOpq7_y.js → ecology-science-erlenmeyer-flask.svg-D1XKaTkC.js} +1 -1
  99. package/web/assets/{fieldset-D69VQ3vX.js → fieldset-CGCve6DD.js} +2 -2
  100. package/web/assets/{freemarker2-Do4liMSN.js → freemarker2-C6nb9fM5.js} +1 -1
  101. package/web/assets/{handlebars-DpU4uL8Y.js → handlebars-DE3S5TjS.js} +1 -1
  102. package/web/assets/{html-B9I8fdv5.js → html-CY7hz01Y.js} +1 -1
  103. package/web/assets/{htmlMode-kOiElG85.js → htmlMode-Ce4CB2KU.js} +1 -1
  104. package/web/assets/{image-flash-lightning.svg-D5c1hsb-.js → image-flash-lightning.svg-b9sFr9AB.js} +1 -1
  105. package/web/assets/{image-picture-flower.svg--K0KlQyl.js → image-picture-flower.svg-Dr_wEXj0.js} +1 -1
  106. package/web/assets/{index-Bul9Aylo.js → index-94C9mIJ-.js} +367 -367
  107. package/web/assets/{interface-alert-exclamation-diamond.svg-C_w5a3q7.js → interface-alert-exclamation-diamond.svg-DqSTZgNx.js} +1 -1
  108. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-DMDyHWRK.js → interface-alert-exclamation-triangle-warning.svg-tR6mkG18.js} +1 -1
  109. package/web/assets/{interface-alert-information-circle.svg-B3w162oJ.js → interface-alert-information-circle.svg-CWK4PaRe.js} +1 -1
  110. package/web/assets/{interface-award-crown.svg-DKX_jl0S.js → interface-award-crown.svg-CH5gPwR6.js} +1 -1
  111. package/web/assets/{interface-bookmark.svg-e90beY5z.js → interface-bookmark-tag.svg-B8EJS7Ye.js} +1 -1
  112. package/web/assets/{interface-bookmark-tag.svg-D0fSBPjO.js → interface-bookmark.svg-DMekWOuD.js} +1 -1
  113. package/web/assets/{interface-calendar-date-one.svg-C6qc0Sl8.js → interface-calendar-date-one.svg-DWVB9WFa.js} +1 -1
  114. package/web/assets/{interface-content-book-open-pages.svg-BnljSGNK.js → interface-content-book-open-pages.svg-B6WD8yc0.js} +1 -1
  115. package/web/assets/{interface-content-book-page.svg-h9r3eRfx.js → interface-content-book-page.svg-Dfh4LPoI.js} +1 -1
  116. package/web/assets/{interface-content-file.svg-CHVFR7pp.js → interface-content-file.svg-DLxukAGD.js} +1 -1
  117. package/web/assets/interface-content-folder.svg-6goG-PKT.js +1 -0
  118. package/web/assets/{interface-content-folder.svg-ClpRom-Y.js → interface-content-folder.svg-mZbluscP.js} +1 -1
  119. package/web/assets/{interface-copy-clipboard.svg-CleRJUrm.js → interface-copy-clipboard.svg-DPWOJX2E.js} +1 -1
  120. package/web/assets/{interface-edit-attachment.svg-CKY3sa16.js → interface-edit-attachment.svg-BR_Sil6I.js} +1 -1
  121. package/web/assets/{interface-edit-binocular.svg-tz_rgKBc.js → interface-edit-binocular.svg-CVEd4vYc.js} +1 -1
  122. package/web/assets/{interface-edit-magic-wand.svg-CpO_jPEw.js → interface-edit-magic-wand.svg-DGp4DsbI.js} +1 -1
  123. package/web/assets/{interface-edit-tool-paint-roller.svg-CfsNGXGA.js → interface-edit-tool-paint-roller.svg-D0WlzqI4.js} +1 -1
  124. package/web/assets/{interface-edit-tool-pencil.svg-DAQ1Ekxm.js → interface-edit-tool-pencil.svg-ehfPBOGD.js} +1 -1
  125. package/web/assets/{interface-favorite-award.svg-D4uWI5Eo.js → interface-favorite-award.svg-Be7yHimT.js} +1 -1
  126. package/web/assets/{interface-favorite-flag.svg-BhDP9KZs.js → interface-favorite-flag.svg-3fARRMMR.js} +1 -1
  127. package/web/assets/{interface-favorite-heart.svg-CCVhz65j.js → interface-favorite-heart.svg-5isgI-U6.js} +1 -1
  128. package/web/assets/{interface-favorite-star.svg-TiGy7_Dz.js → interface-favorite-star.svg-lezozzmW.js} +1 -1
  129. package/web/assets/{interface-favorite-stars-sparkles.svg-Bleiokcx.js → interface-favorite-stars-sparkles.svg-oLtfcK6W.js} +1 -1
  130. package/web/assets/{interface-hierarchy-flowchart.svg-3lRctSSk.js → interface-hierarchy-flowchart.svg-C7k-UAZM.js} +1 -1
  131. package/web/assets/{interface-home-house.svg-DiCjd5wk.js → interface-home-house.svg-m-Ka02ss.js} +1 -1
  132. package/web/assets/{interface-hyperlink.svg-_8aS5L2n.js → interface-hyperlink.svg-Bt31gmVw.js} +1 -1
  133. package/web/assets/{interface-lighting-brightness.svg-w9w5UTai.js → interface-lighting-brightness.svg-n2I1uunc.js} +1 -1
  134. package/web/assets/{interface-lock-closed.svg-DetdV2Gl.js → interface-lock-closed.svg-DeS-CJrc.js} +1 -1
  135. package/web/assets/{interface-lock-open-unlock.svg-BvJFyP6t.js → interface-lock-open-unlock.svg-CdDWPuMJ.js} +1 -1
  136. package/web/assets/{interface-login-key.svg-CIXme1iF.js → interface-login-key.svg-CvEFMs4P.js} +1 -1
  137. package/web/assets/{interface-search.svg-CBDGg8fP.js → interface-search.svg-Cxo-DctG.js} +1 -1
  138. package/web/assets/{interface-setting-cog.svg-BFSZ-Okv.js → interface-setting-cog.svg-9-YFTxcs.js} +1 -1
  139. package/web/assets/{interface-share-megaphone-bullhorn.svg-B7Zua94j.js → interface-share-megaphone-bullhorn.svg-FHVAX6dI.js} +1 -1
  140. package/web/assets/{interface-share-rocket.svg-CzRpGdWQ.js → interface-share-rocket.svg-1jFzVaHL.js} +1 -1
  141. package/web/assets/{interface-share-satellite.svg-BCqYuJ38.js → interface-share-satellite.svg-DJLNcOP6.js} +1 -1
  142. package/web/assets/{interface-share-space-ship.svg-CrVISqJx.js → interface-share-space-ship.svg-pl6k0DBO.js} +1 -1
  143. package/web/assets/{interface-share.svg-CD8UuO99.js → interface-share.svg-ed08i2oG.js} +1 -1
  144. package/web/assets/{interface-signal-square.svg-Gkheru1O.js → interface-signal-square.svg-De4LGj0W.js} +1 -1
  145. package/web/assets/{interface-time-clock-circle.svg-CJcRzzea.js → interface-time-clock-circle.svg-CEJLWUZ3.js} +1 -1
  146. package/web/assets/{interface-time-hour-glass.svg-BsatRAlX.js → interface-time-hour-glass.svg-o76dSnkN.js} +1 -1
  147. package/web/assets/{interface-users-multiple.svg-5CyJhDJN.js → interface-users-multiple.svg-CoqKFb_U.js} +1 -1
  148. package/web/assets/{interface-weather-moon.svg-Bz82ayq0.js → interface-weather-moon.svg-DilewmQl.js} +1 -1
  149. package/web/assets/{javascript-D6oQ3hd5.js → javascript-BXeBAiG8.js} +1 -1
  150. package/web/assets/{jsonMode-B7DwD1nk.js → jsonMode-BqyfnSvw.js} +2 -2
  151. package/web/assets/{liquid-CwyDbpBP.js → liquid-DReW7YnO.js} +1 -1
  152. package/web/assets/{lspLanguageFeatures-T0jAxgTl.js → lspLanguageFeatures-Chp8gGOd.js} +3 -3
  153. package/web/assets/{mail-chat-bubble-square.svg-a_hiTrgs.js → mail-chat-bubble-square.svg-BGfaxE3Z.js} +1 -1
  154. package/web/assets/{mail-send-email-paper-airplane.svg-C-zatHzn.js → mail-send-email-paper-airplane.svg-Behyq64U.js} +1 -1
  155. package/web/assets/{mail-send-envelope.svg-q37OXzBm.js → mail-send-envelope.svg-Bxrgi1uS.js} +1 -1
  156. package/web/assets/{mdx-DHj3k_zA.js → mdx-DlDWnOnw.js} +1 -1
  157. package/web/assets/{minus-BeeT2iDC.js → minus-TTIwv6X7.js} +2 -2
  158. package/web/assets/modal-1yF5hlWc.js +1 -0
  159. package/web/assets/modal-BmOXV_GD.js +5 -0
  160. package/web/assets/{money-cashier-receipt.svg-BvVMygIT.js → money-cashier-receipt.svg-C-cvnfPn.js} +1 -1
  161. package/web/assets/{money-currency-dollar-pay.svg-CRhhH_kZ.js → money-currency-dollar-pay.svg-CvwqB_Us.js} +1 -1
  162. package/web/assets/{money-graph-arrow-increase.svg-CYVM9Jd_.js → money-graph-arrow-increase.svg-U8piy6iJ.js} +1 -1
  163. package/web/assets/{money-graph-bar-chart-increase.svg-Ce0lJBl4.js → money-graph-bar-chart-increase.svg-Czsghch7.js} +1 -1
  164. package/web/assets/multiselect-BtsqfXUl.js +251 -0
  165. package/web/assets/{nature-ecology-leaf.svg-CDM_95dC.js → nature-ecology-leaf.svg-DusYKwpP.js} +1 -1
  166. package/web/assets/{password-CK1h81G7.js → password-D7zz_4x-.js} +4 -4
  167. package/web/assets/{phone-telephone.svg-BVt3gYGw.js → phone-telephone.svg-24q-zljc.js} +1 -1
  168. package/web/assets/{plus-D1CwPS5W.js → plus-QyL2zHhp.js} +6 -6
  169. package/web/assets/process.store-Di7QdGS6.js +1 -0
  170. package/web/assets/{programming-bug.svg-d7SqJEee.js → programming-bug.svg-ljzgC2y_.js} +1 -1
  171. package/web/assets/{programming-cloud.svg-CftGIPF_.js → programming-cloud.svg-DGSPYVKb.js} +1 -1
  172. package/web/assets/{programming-computer-database-server.svg-3O3MINe3.js → programming-computer-database-server.svg-DinsEUs2.js} +1 -1
  173. package/web/assets/{programming-computer-database.svg-lF-wgOh4.js → programming-computer-database.svg-Bi-KIHKo.js} +1 -1
  174. package/web/assets/{programming-module-four-layout.svg--_Np803U.js → programming-module-four-layout.svg-8adkNiA8.js} +1 -1
  175. package/web/assets/{programming-module-three.svg-DMayeKgJ.js → programming-module-three.svg-D05fexpq.js} +1 -1
  176. package/web/assets/{programming-module.svg-DNACg4f8.js → programming-module.svg-Bd_jG5Ld.js} +1 -1
  177. package/web/assets/{programming-script-code.svg-D7nxZDlK.js → programming-script-code.svg-BR0wfmxH.js} +1 -1
  178. package/web/assets/project-launch.store-C5sqtO8-.js +1 -0
  179. package/web/assets/project.store-BHFNvoBC.js +1 -0
  180. package/web/assets/{python-BvMZwnLc.js → python--XCKQ7Mj.js} +1 -1
  181. package/web/assets/{radiobutton-CIVJq1we.js → radiobutton-BGxBny0T.js} +2 -2
  182. package/web/assets/{razor-CzA3B_j-.js → razor-DmUJwoTy.js} +1 -1
  183. package/web/assets/router-BTMrAdGT.js +1 -0
  184. package/web/assets/selectbutton-BeX4M-IG.js +164 -0
  185. package/web/assets/settings-scope-DUOlLSMl.js +1 -0
  186. package/web/assets/settings.store-CbOGUuSM.js +1 -0
  187. package/web/assets/{shopping-cart.svg-B5Jl42Dt.js → shopping-cart.svg-CAlBJM1f.js} +1 -1
  188. package/web/assets/{shopping-gift-present.svg-C04uAz4d.js → shopping-gift-present.svg-CmakOhp5.js} +1 -1
  189. package/web/assets/{shopping-shipping-box-parcel-package.svg-BoumdNF9.js → shopping-shipping-box-parcel-package.svg-DMr-nOJL.js} +1 -1
  190. package/web/assets/src-DztOEh3e.css +1 -0
  191. package/web/assets/src-_lD-wMMD.js +2527 -0
  192. package/web/assets/src-mXN4EdYU.js +1 -0
  193. package/web/assets/{tabpanel-Vb83SrYF.js → tabpanel-DFw1JNiJ.js} +3 -3
  194. package/web/assets/{tag-new-circle.svg-CoaJ9MzB.js → tag-new-circle.svg-BL7H0XEp.js} +1 -1
  195. package/web/assets/theme-E9nfXtHG.js +1 -0
  196. package/web/assets/{toggleswitch-B1JOUIwi.js → toggleswitch-hCIOkIWD.js} +2 -2
  197. package/web/assets/{travel-map-earth-globe.svg-6aDjr2pn.js → travel-map-earth-globe.svg-DOsI_-VD.js} +1 -1
  198. package/web/assets/trpc-BskvL7g_.js +1 -0
  199. package/web/assets/{tsMode-BUqjXsVq.js → tsMode-DLtQZQME.js} +6 -6
  200. package/web/assets/{typescript-C0DrYHIX.js → typescript-BiwtK-KD.js} +1 -1
  201. package/web/assets/useAiChatDrawer-BIkMnJ15.js +1 -0
  202. package/web/assets/{useCloudOrg-tTzYkGcC.js → useCloudOrg-BOcbkFan.js} +1 -1
  203. package/web/assets/useEnvVarAutocomplete-B7JuI0eO.js +1 -0
  204. package/web/assets/{useProjectCatalog-BrszMm46.js → useProjectCatalog-BKnj5RtX.js} +1 -1
  205. package/web/assets/useProjectCloudOrg-DC78dk7F.js +1 -0
  206. package/web/assets/useServiceUrlHost-Bi8nVdMP.js +1 -0
  207. package/web/assets/{xml-urt_37co.js → xml-4zuO3zgc.js} +1 -1
  208. package/web/assets/{yaml-DQgQKRaA.js → yaml-D5Zw4FPr.js} +1 -1
  209. package/web/index.html +1 -1
  210. package/agent-manager-GFRVUJSI.js +0 -10
  211. package/chunk-7GBSCYXU.js.map +0 -1
  212. package/chunk-B7MABGQN.js.map +0 -1
  213. package/chunk-J2L6VONF.js.map +0 -1
  214. package/chunk-RDE3X3J4.js.map +0 -1
  215. package/src-BNWVTXCY.js.map +0 -1
  216. package/web/assets/AgentTerminalPage-C88TWOpv.js +0 -1
  217. package/web/assets/AiChatPage-C7Fwkm3H.js +0 -1
  218. package/web/assets/AppLayout-D5uzw5Lg.js +0 -1
  219. package/web/assets/AuditPage-LGN72fPt.js +0 -1
  220. package/web/assets/CloudAccountPage-NDf1HzKg.js +0 -1
  221. package/web/assets/CloudAccountPage-WIW6QDtJ.js +0 -1
  222. package/web/assets/CloudMessagingPage-CewyN6Br.js +0 -2
  223. package/web/assets/DashboardPage-BW6vTxoh.js +0 -164
  224. package/web/assets/DirectoryPickerDialog-CG1pl1FS.js +0 -1
  225. package/web/assets/EnvironmentForm-VSWiFSCk.js +0 -108
  226. package/web/assets/EnvironmentFormPage-C9ageHqI.js +0 -1
  227. package/web/assets/EnvironmentListPage-CH2lTuZa.js +0 -1
  228. package/web/assets/KanbanBoardPage-C8Wstz1K.css +0 -1
  229. package/web/assets/KanbanBoardPage-Dm0vlVJC.js +0 -4
  230. package/web/assets/KanbanListPage-Bn2e52T0.js +0 -1
  231. package/web/assets/KanbanListPage-C_zN70Af.css +0 -1
  232. package/web/assets/LaunchProjectPage-CsVEC9XK.js +0 -1
  233. package/web/assets/LaunchServicesPage-eqVt9k71.js +0 -1
  234. package/web/assets/LogViewer-BuEfKhGy.js +0 -3
  235. package/web/assets/OrgKeyPrompt-CP_twrjK.js +0 -1
  236. package/web/assets/OrgLogo-C9tIdIKQ.js +0 -1
  237. package/web/assets/ProcessControls-DtHM26HD.js +0 -1
  238. package/web/assets/ProcessDetailPage-CInTvQzk.js +0 -1
  239. package/web/assets/RandomJobTestPage-DvNT-fHM.js +0 -2
  240. package/web/assets/ScenarioEditorPage-Cy-mEocS.js +0 -14
  241. package/web/assets/SendToChatDialog-7yjimfM6.js +0 -1
  242. package/web/assets/ServiceControlsBar-BIw8gvT4.js +0 -1
  243. package/web/assets/ServiceDetailPage-Dh8heavF.js +0 -2
  244. package/web/assets/ServiceFormPage-CPvcHTvF.js +0 -1
  245. package/web/assets/SettingsPage-DIwUGrhc.js +0 -2063
  246. package/web/assets/UnlockPage-CaloUcpR.js +0 -1
  247. package/web/assets/VaultMigrationPage-CXpnpmtH.js +0 -1
  248. package/web/assets/VersionBadge-CDFKMmiB.js +0 -1
  249. package/web/assets/WikiPage-fprCcfQb.js +0 -1
  250. package/web/assets/WikiView-BSstJNUm.js +0 -1
  251. package/web/assets/add-scalar-classes-W7zkEwly.js +0 -387
  252. package/web/assets/card-DoiDmzQ4.js +0 -32
  253. package/web/assets/cloud-chat.store-6ZDTW64E.js +0 -1
  254. package/web/assets/cloud-client-MqmRMv1P.js +0 -1
  255. package/web/assets/cloud-session-CRBMHBfS.js +0 -1
  256. package/web/assets/column-DkPii5Hb.js +0 -763
  257. package/web/assets/interface-content-folder.svg-CDjqJkA6.js +0 -1
  258. package/web/assets/modal-D4uoXhc8.js +0 -1
  259. package/web/assets/modal-D_IYbQwG.js +0 -5
  260. package/web/assets/multiselect-BwtDM_SI.js +0 -251
  261. package/web/assets/process.store-CKag9k1W.js +0 -1
  262. package/web/assets/project-launch.store-Bq1mbmiC.js +0 -1
  263. package/web/assets/project.store-CPpr3pLf.js +0 -1
  264. package/web/assets/router-B5zQ1kEJ.js +0 -1
  265. package/web/assets/settings-scope-7icbsnqh.js +0 -1
  266. package/web/assets/settings.store-Cr09JQn2.js +0 -1
  267. package/web/assets/src-D3VR_y-X.js +0 -1
  268. package/web/assets/src-DUQBbN9_.js +0 -572
  269. package/web/assets/src-KwP86pAZ.css +0 -1
  270. package/web/assets/theme-B9SZBnza.js +0 -1
  271. package/web/assets/trpc-rld4yQko.js +0 -1
  272. package/web/assets/useAiChatDrawer-Co833XxT.js +0 -1
  273. package/web/assets/useEnvVarAutocomplete-D0iCOu68.js +0 -1
  274. package/web/assets/useProjectCloudOrg-CAg2yHZP.js +0 -1
  275. package/web/assets/useServiceUrlHost-DWJP4dsX.js +0 -1
  276. /package/{agent-manager-GFRVUJSI.js.map → agent-manager-QQHA3TUB.js.map} +0 -0
  277. /package/{chat-6ETYBINN.js.map → chat-VIVAFYYS.js.map} +0 -0
  278. /package/{chunk-K2KSZRSI.js.map → chunk-CVVAQTMF.js.map} +0 -0
  279. /package/{chunk-TTJNHZ6U.js.map → chunk-GNVQH3VG.js.map} +0 -0
  280. /package/{companion-SH4HLD6G.js.map → companion-2VDWE5SP.js.map} +0 -0
  281. /package/{desktop-launcher-7UZF7PFH.js.map → desktop-launcher-RSRAXX6B.js.map} +0 -0
  282. /package/{node-ILM6MV56.js.map → node-JU7A5NDT.js.map} +0 -0
  283. /package/{settings-manager-XD6U4QN3.js.map → settings-manager-3LMN34IT.js.map} +0 -0
  284. /package/web/assets/{abap-B8g_2xYy.js → abap-BMzrySmF.js} +0 -0
  285. /package/web/assets/{apex-Bm8w4T5A.js → apex-B-FTsm78.js} +0 -0
  286. /package/web/assets/{azcli-BqdUW6F8.js → azcli--oTP982j.js} +0 -0
  287. /package/web/assets/{bat-Bngf0lQU.js → bat-BoGrdVjd.js} +0 -0
  288. /package/web/assets/{bicep-DzO554JS.js → bicep-CzjrcFxJ.js} +0 -0
  289. /package/web/assets/{browser-BJX5ZrVN.js → browser-TSICzjSV.js} +0 -0
  290. /package/web/assets/{cameligo-Bcy3ohdb.js → cameligo-C5PP9BK8.js} +0 -0
  291. /package/web/assets/{clojure-CZbiqkDV.js → clojure-CQZ7UtVM.js} +0 -0
  292. /package/web/assets/{coffee-CDuhyV-7.js → coffee-D04oxsa9.js} +0 -0
  293. /package/web/assets/{cpp-BklV8f4x.js → cpp-CrdHZict.js} +0 -0
  294. /package/web/assets/{csharp-BelR_XiC.js → csharp-DEojCdJ9.js} +0 -0
  295. /package/web/assets/{csp-DUjeBS2K.js → csp-B_k4_rnT.js} +0 -0
  296. /package/web/assets/{css-CvbYbEko.js → css-pkVaNQ-t.js} +0 -0
  297. /package/web/assets/{cypher-Dwrbk-SP.js → cypher-InDI0ycO.js} +0 -0
  298. /package/web/assets/{dart-BW1E5h9O.js → dart-CMBDu6Jq.js} +0 -0
  299. /package/web/assets/{dockerfile-B41ilpF9.js → dockerfile-Dus6X0qp.js} +0 -0
  300. /package/web/assets/{ecl-BZE6Vxna.js → ecl-DcGwRpWZ.js} +0 -0
  301. /package/web/assets/{elixir-CtYg0bLh.js → elixir-BNgAfgpS.js} +0 -0
  302. /package/web/assets/{flow9-Sgvy48_I.js → flow9-b9BSTtId.js} +0 -0
  303. /package/web/assets/{fsharp-BzAfXfKg.js → fsharp-DsEl3ylC.js} +0 -0
  304. /package/web/assets/{go-D7oonABu.js → go-BVk-2zoJ.js} +0 -0
  305. /package/web/assets/{graphql-BEN-qkl0.js → graphql-CCOPKJOV.js} +0 -0
  306. /package/web/assets/{hcl-CE0OhwCq.js → hcl-Bk9fje7b.js} +0 -0
  307. /package/web/assets/{ini-CCLtcvjA.js → ini-Bfgwaf--.js} +0 -0
  308. /package/web/assets/{java-Bao8w_gp.js → java-X9tlBUoe.js} +0 -0
  309. /package/web/assets/{julia-Dr0hFYrl.js → julia-CSpUiZse.js} +0 -0
  310. /package/web/assets/{kotlin-Cbfu8lcM.js → kotlin-D9ZC3fV-.js} +0 -0
  311. /package/web/assets/{less-BU4gEpXr.js → less-D_gN2wJJ.js} +0 -0
  312. /package/web/assets/{lexon-GpBGGdTS.js → lexon-DQORBJ3L.js} +0 -0
  313. /package/web/assets/{lua-DIPKk37y.js → lua-cfsK9g-g.js} +0 -0
  314. /package/web/assets/{m3-CSOypKGf.js → m3-CqoRZqX5.js} +0 -0
  315. /package/web/assets/{markdown-VDqAgkyM.js → markdown-7m0w-Hon.js} +0 -0
  316. /package/web/assets/{mips-BJWI6dX7.js → mips-Jgck3TtW.js} +0 -0
  317. /package/web/assets/{msdax-BLpuE5Dc.js → msdax-DZzPcvtl.js} +0 -0
  318. /package/web/assets/{mysql-ZsxiEUoY.js → mysql-BDgvhBkY.js} +0 -0
  319. /package/web/assets/{objective-c-DcQa1iPc.js → objective-c-s7vO3TPA.js} +0 -0
  320. /package/web/assets/{pascal-u-hmXdyn.js → pascal-WgQqmw9E.js} +0 -0
  321. /package/web/assets/{pascaligo-DqbP-1C6.js → pascaligo-DdIoMHxz.js} +0 -0
  322. /package/web/assets/{perl-CUeQpRkH.js → perl-DscrjHeC.js} +0 -0
  323. /package/web/assets/{pgsql-CQ7FhAFN.js → pgsql-BoaUrFoQ.js} +0 -0
  324. /package/web/assets/{php-BEf_fu3E.js → php-CozsHsln.js} +0 -0
  325. /package/web/assets/{pla-CXNNbT0w.js → pla-BmgIF2rB.js} +0 -0
  326. /package/web/assets/{postiats-CIXLZwPb.js → postiats-kTIQO6-c.js} +0 -0
  327. /package/web/assets/{powerquery-DnGPASFA.js → powerquery-Bge8QN2f.js} +0 -0
  328. /package/web/assets/{powershell-1Y1kqIZS.js → powershell-C_EAICI2.js} +0 -0
  329. /package/web/assets/{protobuf-Cfo9haoG.js → protobuf-CeVRf2v4.js} +0 -0
  330. /package/web/assets/{pug-BujEZqmy.js → pug-v3OQoW7_.js} +0 -0
  331. /package/web/assets/{qsharp-BL9AqNGD.js → qsharp-B8FD4CzE.js} +0 -0
  332. /package/web/assets/{r-D0dhpGOM.js → r-DoTn9Ot2.js} +0 -0
  333. /package/web/assets/{redis-BMLwD7W9.js → redis-B9eZmNDt.js} +0 -0
  334. /package/web/assets/{redshift-CkHh73og.js → redshift-hkVOq3Na.js} +0 -0
  335. /package/web/assets/{restructuredtext-DZ-LF65W.js → restructuredtext-go0p3fHP.js} +0 -0
  336. /package/web/assets/{ruby-UzPHTzc9.js → ruby-DlJSZQx7.js} +0 -0
  337. /package/web/assets/{rust-ja9NGnXZ.js → rust-CS0ULEAB.js} +0 -0
  338. /package/web/assets/{sb-BNdOJbMY.js → sb-BWXLY6C5.js} +0 -0
  339. /package/web/assets/{scala-LlhkyN6l.js → scala-BnuniK5f.js} +0 -0
  340. /package/web/assets/{scheme-BAj99YIn.js → scheme-rpzgzOI1.js} +0 -0
  341. /package/web/assets/{scss-chS-xxB3.js → scss-CDZa-oHG.js} +0 -0
  342. /package/web/assets/{shared-sse-_VzlL_VX.js → shared-sse-DUHeXaFO.js} +0 -0
  343. /package/web/assets/{shell-CcH7eYIc.js → shell-Ddbp1LTu.js} +0 -0
  344. /package/web/assets/{solidity-cVvnqX-K.js → solidity-XL5oY4Gs.js} +0 -0
  345. /package/web/assets/{sophia-BBT-EdFw.js → sophia-C_FAm0DW.js} +0 -0
  346. /package/web/assets/{sparql-DGEqOrB6.js → sparql-Bkcu5lIB.js} +0 -0
  347. /package/web/assets/{sql-CcTErTxM.js → sql-hd2WP_Pl.js} +0 -0
  348. /package/web/assets/{st-Bjw-B4mN.js → st-DX23wqhB.js} +0 -0
  349. /package/web/assets/{swift-Bid-7bBd.js → swift-Uc9uURgY.js} +0 -0
  350. /package/web/assets/{systemverilog-CXju7Nd-.js → systemverilog-uwEj3oP7.js} +0 -0
  351. /package/web/assets/{tcl-D-Dw7JFz.js → tcl-CW0gNQFo.js} +0 -0
  352. /package/web/assets/{twig-BIPbzEWT.js → twig-C-fOaHGU.js} +0 -0
  353. /package/web/assets/{typespec-UJSutGCV.js → typespec-Cs66OMeg.js} +0 -0
  354. /package/web/assets/{uuid-Cro0aJm3.js → uuid-hYISTbX6.js} +0 -0
  355. /package/web/assets/{vb-oyG_LJw0.js → vb-f4DjbovN.js} +0 -0
  356. /package/web/assets/{wgsl-DXZ6ljB6.js → wgsl-D8hs7PJh.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../server/src/services/agent-manager.ts","../../server/src/services/agent-store.ts","../../server/src/utils/encryption-key.ts","../../server/src/services/service-store.ts","../../server/src/services/project-key.ts","../../server/src/services/vault-registry.ts","../../server/src/services/local-removals.ts","../../server/src/services/vault-conflicts.ts","../../server/src/services/store-disk-coordinator.ts","../../server/src/services/environment-store.ts","../../server/src/services/crypto-helpers.ts","../../server/src/services/agent-spawner.ts","../../server/src/services/instance-registry.ts","../../server/src/services/traefik-manager.ts","../../server/src/services/agent-ws-bridge.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport { z } from 'zod';\nimport { agentStore, type AgentOwner } from './agent-store.js';\nimport { serviceStore } from './service-store.js';\nimport { agentSpawner } from './agent-spawner.js';\nimport { traefikManager } from './traefik-manager.js';\nimport { AgentWSBridge } from './agent-ws-bridge.js';\nimport {\n WS_RECONNECT_MIN,\n WS_RECONNECT_MAX,\n AGENT_HEALTH_POLL_INTERVAL,\n AGENT_FETCH_TIMEOUT,\n TraefikConfigSchema,\n} from '@runeya/packages-shared';\nimport type { AgentConfig, AgentStatus } from '@runeya/packages-shared';\n\nconst HealthMessageSchema = z.object({\n status: z.string(),\n uptime: z.number(),\n runnersAvailable: z.array(z.enum(['native', 'docker'])),\n processCount: z.number(),\n runningCount: z.number(),\n lanIp: z.string().optional(),\n os: z.string().max(64).optional(),\n home: z.string().max(4096).optional(),\n cwd: z.string().max(4096).optional(),\n});\n\ninterface AgentConnection {\n config: AgentConfig;\n status: AgentStatus;\n healthWs?: WebSocket;\n bridge?: AgentWSBridge;\n reconnectTimer?: ReturnType<typeof setTimeout>;\n reconnectDelay: number;\n aliveTimer?: ReturnType<typeof setTimeout>;\n}\n\nconst MIN_RECONNECT_DELAY = WS_RECONNECT_MIN;\nconst MAX_RECONNECT_DELAY = WS_RECONNECT_MAX;\nconst ALIVE_TIMEOUT = AGENT_HEALTH_POLL_INTERVAL;\n\nfunction resolvePassthroughValues(names: string[]): Record<string, string> {\n const values: Record<string, string> = {};\n for (const name of names) {\n if (process.env[name] !== undefined) {\n values[name] = process.env[name]!;\n }\n }\n return values;\n}\n\nclass AgentManager extends EventEmitter {\n private connections: Map<string, AgentConnection> = new Map();\n\n /**\n * In-flight GET calls, keyed by agent + path + input.\n *\n * Several callers legitimately ask the same agent the same question at the\n * same moment — the sidebar and the git panel both read `git.status` for the\n * open service, the chat store, the chat page and the kanban board all read\n * `ai.findPtyBySession` for the same conversation. Sharing the pending\n * promise collapses those into a single round-trip: same response, same\n * instant, one less request queued on the agent. GET only — every tRPC query\n * is a read, whereas replaying a mutation must always hit the agent.\n */\n private inFlightGets: Map<string, Promise<unknown>> = new Map();\n\n constructor() {\n super();\n // Each WS client subscribes to up to 4 process event types + 1 agent status,\n // plus 1 bridge fan-out per agent. Two browser tabs = 10+ listeners easily.\n this.setMaxListeners(50);\n }\n\n // Make an authenticated fetch call to an agent's tRPC endpoint.\n // GET calls that are already in flight with identical input are shared rather\n // than re-issued (see `inFlightGets`).\n private async agentCall<T>(agent: AgentConfig, path: string, input?: unknown, method: 'GET' | 'POST' = 'GET', timeout = AGENT_FETCH_TIMEOUT): Promise<T> {\n if (method !== 'GET') return this.performAgentCall<T>(agent, path, input, method, timeout);\n\n let serialisedInput: string;\n try {\n serialisedInput = input === undefined ? '' : JSON.stringify(input);\n } catch {\n // Non-serialisable input — can't key it, so just issue the call.\n return this.performAgentCall<T>(agent, path, input, method, timeout);\n }\n // The timeout is part of the key: a caller must never inherit a shorter\n // deadline than it asked for by piggybacking on someone else's call.\n const key = `${agent.id}\\u0000${path}\\u0000${timeout}\\u0000${serialisedInput}`;\n\n const pending = this.inFlightGets.get(key);\n if (pending) return pending as Promise<T>;\n\n const call = this.performAgentCall<T>(agent, path, input, method, timeout)\n .finally(() => { this.inFlightGets.delete(key); });\n this.inFlightGets.set(key, call);\n return call;\n }\n\n private async performAgentCall<T>(agent: AgentConfig, path: string, input?: unknown, method: 'GET' | 'POST' = 'GET', timeout = AGENT_FETCH_TIMEOUT): Promise<T> {\n const url = new URL(`/api/trpc/${path}`, agent.url);\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${agent.passphrase}`,\n 'Content-Type': 'application/json',\n };\n\n // console.log(`[agent-manager] Calling agent ${agent.id} → ${method} ${path}`);\n\n let response: Response;\n try {\n if (method === 'GET') {\n if (input !== undefined) {\n url.searchParams.set('input', JSON.stringify(input));\n }\n response = await fetch(url.toString(), { headers, signal: AbortSignal.timeout(timeout) });\n } else {\n response = await fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify(input !== undefined ? input : {}),\n signal: AbortSignal.timeout(timeout),\n });\n }\n } catch (err: unknown) {\n console.error('[agent-manager] Agent call failed (' + method + ' ' + path + '):', err instanceof Error ? err.message : String(err)); // nosemgrep: unsafe-formatstring\n throw err;\n }\n\n if (!response.ok) {\n let agentMessage = '';\n try {\n const errBody = await response.json() as { error?: { message?: string }; message?: string };\n agentMessage = errBody?.error?.message ?? errBody?.message ?? '';\n } catch { /* ignore parse errors */ }\n const detail = agentMessage ? `: ${agentMessage}` : '';\n console.error('[agent-manager] Agent call failed: HTTP', response.status, 'for', method, path, detail);\n throw new Error(`Agent call failed: HTTP ${response.status}${detail}`);\n }\n\n const json = await response.json() as { error?: { message?: string }; result?: { data?: T } };\n if (json.error) {\n console.error('[agent-manager] Agent error on', path + ':', json.error);\n throw new Error(`Agent error on ${path}: ${json.error.message ?? 'unknown'}`);\n }\n return json.result?.data as T;\n }\n\n // Query wrapper\n async agentQuery<T>(agentId: string, path: string, input?: unknown): Promise<T> {\n const agent = this.getAgent(agentId);\n if (!agent) throw new Error(`Agent ${agentId} not found`);\n return this.agentCall<T>(agent.config, path, input, 'GET');\n }\n\n // Mutation wrapper\n async agentMutation<T>(agentId: string, path: string, input?: unknown, timeout?: number): Promise<T> {\n const agent = this.getAgent(agentId);\n if (!agent) throw new Error(`Agent ${agentId} not found`);\n return this.agentCall<T>(agent.config, path, input, 'POST', timeout);\n }\n\n /**\n * Open a dynamic tRPC subscription on the agent's WS bridge.\n * Returns an unsubscribe function.\n * Used for per-session streaming (e.g. terminal.onData).\n */\n agentSubscribe(\n agentId: string,\n path: string,\n input: unknown,\n onData: (data: unknown) => void,\n onStop?: () => void,\n ): () => void {\n const conn = this.connections.get(agentId);\n if (!conn?.bridge) throw new Error(`Agent ${agentId} not connected via WebSocket`);\n return conn.bridge.subscribeWithInput(path, input, onData, onStop);\n }\n\n async init(): Promise<void> {\n const agents = await agentStore.list();\n for (const agent of agents) {\n this.connectAgent(agent);\n }\n }\n\n /**\n * `owner` dit à quelle organisation l'agent appartient, ou `null` pour la\n * machine — l'agent local, qui n'exécute des processus que sur ce poste.\n */\n async register(\n name: string,\n url: string,\n passphrase: string,\n owner: AgentOwner = null,\n ): Promise<AgentConfig> {\n const config = await agentStore.register(name, url, passphrase, owner);\n const isReRegistration = this.connections.has(config.id);\n\n // If already connected (e.g. loaded by init() then re-registered by spawner),\n // close the stale WS before opening a fresh one with the new passphrase.\n if (isReRegistration) {\n console.log(`[agent-manager] Re-registering agent ${config.name} (${config.id})`);\n this.disconnectAgent(config.id);\n // Small delay to ensure WS are fully closed before reconnecting\n await new Promise(resolve => setTimeout(resolve, 200));\n }\n\n this.connectAgent(config);\n console.log(`[agent-manager] Agent ${config.name} (${config.id}) connection established`);\n return config;\n }\n\n /**\n * Register a local agent in-memory only (no persistence to agentStore).\n * The local agent is always auto-spawned with deterministic config,\n * so it doesn't need disk persistence.\n * Also reassigns orphaned services (from old UUID-based agent IDs) to this agent.\n */\n async registerLocal(config: AgentConfig): Promise<void> {\n const isReRegistration = this.connections.has(config.id);\n\n if (isReRegistration) {\n console.log(`[agent-manager] Re-registering local agent ${config.name} (${config.id})`);\n this.disconnectAgent(config.id);\n await new Promise(resolve => setTimeout(resolve, 200));\n }\n\n // Load persisted passthrough env for the local agent\n const storedPassthrough = agentStore.getPassthroughEnv(config.id);\n const configWithPassthrough = { ...config, passthroughEnv: storedPassthrough };\n\n // Reassign orphaned services: any service whose agentId doesn't match\n // a known agent gets reassigned to this local agent (migration from UUID → stable ID).\n await this.reassignOrphanedServices(config.id);\n\n this.connectAgent(configWithPassthrough);\n console.log(`[agent-manager] Local agent ${config.name} (${config.id}) connected (in-memory only)`);\n }\n\n /**\n * Find services assigned to unknown agent IDs and reassign them to the given agent.\n * This handles the migration from random UUID-based agent IDs to the stable 'local-agent' ID.\n */\n private async reassignOrphanedServices(localAgentId: string): Promise<void> {\n const allServices = await serviceStore.list();\n const persistedAgents = await agentStore.list();\n const knownIds = new Set([localAgentId, ...persistedAgents.map(a => a.id)]);\n\n for (const service of allServices) {\n if (service.agentId && !knownIds.has(service.agentId)) {\n console.log(`[agent-manager] Reassigning orphaned service ${service.name} (${service.id}) from ${service.agentId} → ${localAgentId}`);\n await serviceStore.update({ id: service.id, agentId: localAgentId });\n }\n }\n }\n\n async update(agentId: string, fields: { name?: string; url?: string; passphrase?: string; passthroughEnv?: string[] }): Promise<AgentConfig> {\n const isLocal = agentSpawner.getLocalAgentId() === agentId;\n\n // Local agent: only passthroughEnv is allowed\n if (isLocal) {\n const hasOtherFields = fields.name !== undefined || fields.url !== undefined || fields.passphrase !== undefined;\n if (hasOtherFields) {\n throw new Error('Cannot modify the local agent');\n }\n }\n\n // Handle passthroughEnv separately (persisted in agentStore, pushed to agent)\n if (fields.passthroughEnv !== undefined) {\n await agentStore.savePassthroughEnv(agentId, fields.passthroughEnv);\n\n const conn = this.connections.get(agentId);\n if (conn) {\n conn.config = { ...conn.config, passthroughEnv: fields.passthroughEnv };\n // Push to the agent\n try {\n await this.agentMutation(agentId, 'config.setPassthroughEnv', {\n names: fields.passthroughEnv,\n values: resolvePassthroughValues(fields.passthroughEnv),\n });\n } catch (err) {\n console.error('[agent-manager] Failed to push passthroughEnv to agent', agentId + ':', err instanceof Error ? err.message : String(err));\n }\n }\n }\n\n // For local agent with only passthroughEnv, return config from connection\n if (isLocal) {\n const conn = this.connections.get(agentId);\n if (!conn) throw new Error(`Agent ${agentId} not found`);\n return conn.config;\n }\n\n // Remote agent: update name/url/passphrase in the store\n const { passthroughEnv: _pt, ...storeFields } = fields;\n const hasStoreFields = storeFields.name !== undefined || storeFields.url !== undefined || storeFields.passphrase !== undefined;\n\n if (hasStoreFields) {\n const updated = await agentStore.update(agentId, storeFields);\n if (!updated) {\n throw new Error(`Agent ${agentId} not found`);\n }\n const conn = this.connections.get(agentId);\n if (conn) {\n conn.config = updated;\n if (storeFields.url !== undefined || storeFields.passphrase !== undefined) {\n this.disconnectAgent(agentId);\n this.connectAgent(updated);\n }\n }\n return updated;\n }\n\n // passthroughEnv-only update for remote agent\n const conn = this.connections.get(agentId);\n if (!conn) throw new Error(`Agent ${agentId} not found`);\n return conn.config;\n }\n\n async remove(agentId: string): Promise<boolean> {\n // Prevent removal of the local spawned agent\n if (agentSpawner.getLocalAgentId() === agentId) {\n throw new Error('Cannot remove the local agent');\n }\n this.disconnectAgent(agentId);\n return agentStore.remove(agentId);\n }\n\n private connectAgent(config: AgentConfig): void {\n const connection: AgentConnection = {\n config,\n status: {\n connected: false,\n lastSeen: null,\n uptime: 0,\n runnersAvailable: [],\n },\n reconnectDelay: MIN_RECONNECT_DELAY,\n };\n\n this.connections.set(config.id, connection);\n this.openHealthWs(config.id);\n this.openBridge(config.id);\n }\n\n private openBridge(agentId: string): void {\n const conn = this.connections.get(agentId);\n if (!conn) return;\n\n console.log(`[agent-manager] Opening tRPC bridge for agent ${conn.config.name} (${agentId})`);\n\n const bridge = new AgentWSBridge(agentId, conn.config.url, conn.config.passphrase);\n conn.bridge = bridge;\n\n // Fan-out bridge events to the AgentManager\n const eventTypes = ['process:status', 'process:log', 'process:health', 'process:metrics'] as const;\n for (const eventType of eventTypes) {\n bridge.on(eventType, (data: unknown) => {\n this.emit(eventType, data);\n });\n }\n\n bridge.connect();\n }\n\n private disconnectAgent(agentId: string): void {\n const conn = this.connections.get(agentId);\n if (!conn) return;\n\n console.log(`[agent-manager] Disconnecting agent ${conn.config.name} (${agentId})`);\n\n if (conn.healthWs) {\n conn.healthWs.removeAllListeners();\n conn.healthWs.close();\n conn.healthWs = undefined;\n }\n if (conn.bridge) {\n conn.bridge.disconnect();\n conn.bridge = undefined;\n }\n if (conn.reconnectTimer) {\n clearTimeout(conn.reconnectTimer);\n conn.reconnectTimer = undefined;\n }\n if (conn.aliveTimer) {\n clearTimeout(conn.aliveTimer);\n conn.aliveTimer = undefined;\n }\n\n this.connections.delete(agentId);\n this.emit('status', { agentId, status: { connected: false, lastSeen: null, uptime: 0, runnersAvailable: [] } satisfies AgentStatus });\n }\n\n private openHealthWs(agentId: string): void {\n const conn = this.connections.get(agentId);\n if (!conn) return;\n\n // Build WS URL from agent HTTP URL\n const httpUrl = new URL(conn.config.url);\n const wsProtocol = httpUrl.protocol === 'https:' ? 'wss:' : 'ws:';\n const wsUrl = `${wsProtocol}//${httpUrl.host}/ws/health`;\n\n const ws = new WebSocket(wsUrl, {\n headers: { 'Authorization': `Bearer ${conn.config.passphrase}` },\n });\n conn.healthWs = ws;\n\n const resetAliveTimer = () => {\n if (conn.aliveTimer) clearTimeout(conn.aliveTimer);\n conn.aliveTimer = setTimeout(() => {\n console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) alive timeout — closing WS`);\n ws.close();\n }, ALIVE_TIMEOUT);\n };\n\n ws.on('open', () => {\n console.debug(`[agent-manager] WS open to agent ${conn.config.name} (${agentId})`);\n resetAliveTimer();\n });\n\n ws.on('message', (raw: Buffer | string) => {\n try {\n const health = HealthMessageSchema.parse(\n JSON.parse(typeof raw === 'string' ? raw : raw.toString()),\n );\n\n // Only reset alive timer after successful validation\n resetAliveTimer();\n\n const wasConnected = conn.status.connected;\n conn.status = {\n connected: true,\n lastSeen: new Date().toISOString(),\n uptime: health.uptime,\n runnersAvailable: health.runnersAvailable,\n lanIp: health.lanIp,\n os: health.os,\n home: health.home,\n cwd: health.cwd,\n };\n\n // Reset reconnect delay on successful message\n conn.reconnectDelay = MIN_RECONNECT_DELAY;\n\n this.emit('status', { agentId, status: { ...conn.status } satisfies AgentStatus });\n\n if (!wasConnected) {\n console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) connected`);\n void this.pushPassthroughEnv(agentId);\n void this.reconcileAiSpawns(agentId);\n const rawTraefikConfig = agentStore.getTraefikConfig(agentId);\n if (rawTraefikConfig) {\n const parsed = TraefikConfigSchema.safeParse(rawTraefikConfig);\n if (parsed.success) {\n // Decrypt DNS credentials before pushing to agent — the stored config\n // holds encrypted ciphertext which is not valid as env var values.\n const configToPush = {\n ...parsed.data,\n dnsCredentials: traefikManager.decryptDnsCredentials(parsed.data.dnsCredentials ?? {}),\n };\n void this.pushTraefikConfig(agentId, configToPush);\n } else {\n console.error('[agent-manager] Invalid Traefik config in store for agent', agentId);\n }\n }\n }\n } catch (err) {\n console.debug('[agent-manager] Failed to parse health message for agent', agentId + ':', err);\n }\n });\n\n ws.on('close', () => {\n if (conn.aliveTimer) {\n clearTimeout(conn.aliveTimer);\n conn.aliveTimer = undefined;\n }\n conn.healthWs = undefined;\n\n if (conn.status.connected) {\n console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) disconnected`);\n conn.status.connected = false;\n this.emit('status', { agentId, status: { ...conn.status } satisfies AgentStatus });\n }\n\n // Schedule reconnect only if still tracked\n if (this.connections.has(agentId)) {\n this.scheduleReconnect(agentId);\n }\n });\n\n ws.on('error', (err) => {\n // Error is always followed by close event, no need to handle reconnect here\n if (conn.status.connected) {\n console.debug('[agent-manager] WS error for agent', conn.config.name, '(' + agentId + '):', err.message);\n }\n });\n }\n\n private scheduleReconnect(agentId: string): void {\n const conn = this.connections.get(agentId);\n if (!conn) return;\n\n // Clear any existing reconnect timer\n if (conn.reconnectTimer) {\n clearTimeout(conn.reconnectTimer);\n }\n\n conn.reconnectTimer = setTimeout(() => {\n conn.reconnectTimer = undefined;\n if (this.connections.has(agentId)) {\n this.openHealthWs(agentId);\n }\n }, conn.reconnectDelay);\n\n // Exponential backoff: 1s → 2s → 4s → 8s → 15s (capped)\n conn.reconnectDelay = Math.min(conn.reconnectDelay * 2, MAX_RECONNECT_DELAY);\n }\n\n /**\n * On agent (re)connect, discover any AI spawns still alive on the agent.\n * Log them for now — full rehydration of ActiveSessionRegistry is a follow-up.\n */\n private async reconcileAiSpawns(agentId: string): Promise<void> {\n try {\n const spawns = await this.agentQuery<Array<{ spawnId: string; cli: string; status: string; bufferLength: number }>>(agentId, 'ai.list');\n if (spawns.length > 0) {\n const summary = spawns.map(s => `${s.spawnId} (${s.cli}, ${s.status}, buf:${s.bufferLength})`).join(', ');\n console.log('[agent-manager] Agent %s has %d AI spawn(s): %s', agentId, spawns.length, summary);\n // TODO: rehydrate ActiveSessionRegistry for running spawns that match known conversations\n // For now, ack done spawns without a matching session to allow cleanup\n for (const spawn of spawns) {\n if (spawn.status !== 'running') {\n this.agentMutation(agentId, 'ai.ack', { spawnId: spawn.spawnId }).catch(() => {});\n }\n }\n }\n } catch (err) {\n console.debug('[agent-manager] Failed to reconcile AI spawns for agent', agentId + ':', err instanceof Error ? err.message : String(err));\n }\n }\n\n // Push passthrough env variables to agent on (re)connection\n private async pushPassthroughEnv(agentId: string): Promise<void> {\n const conn = this.connections.get(agentId);\n if (conn && conn.config.passthroughEnv && conn.config.passthroughEnv.length > 0) {\n try {\n await this.agentMutation(agentId, 'config.setPassthroughEnv', {\n names: conn.config.passthroughEnv,\n values: resolvePassthroughValues(conn.config.passthroughEnv),\n });\n console.log('[agent-manager] Pushed passthroughEnv to agent', agentId + ':', conn.config.passthroughEnv.join(', '));\n } catch (err) {\n console.error('[agent-manager] Failed to push passthroughEnv to agent', agentId + ':', err instanceof Error ? err.message : String(err));\n }\n }\n }\n\n // Push Traefik config to agent on (re)connection or after update\n async pushTraefikConfig(agentId: string, config: unknown): Promise<void> {\n const conn = this.connections.get(agentId);\n if (!conn) return;\n try {\n await this.agentMutation(agentId, 'config.setTraefikConfig', config);\n } catch (err) {\n console.error('[agent-manager] Failed to push Traefik config to agent', agentId + ':', err instanceof Error ? err.message : String(err));\n }\n }\n\n /** Returns the local agent ID, or first connected agent, or null. */\n getDefaultAgentId(): string | null {\n const localId = agentSpawner.getLocalAgentId();\n if (localId && this.connections.get(localId)?.status.connected) return localId;\n for (const [id, conn] of this.connections) {\n if (conn.status.connected) return id;\n }\n return null;\n }\n\n getAgent(agentId: string): AgentConnection | undefined {\n return this.connections.get(agentId);\n }\n\n getAgentConfig(agentId: string): AgentConfig | undefined {\n return this.connections.get(agentId)?.config;\n }\n\n listAgents(): Array<{ config: AgentConfig; status: AgentStatus; isLocal: boolean }> {\n const localId = agentSpawner.getLocalAgentId();\n return Array.from(this.connections.values()).map(conn => ({\n config: { ...conn.config, passphrase: '***' }, // Don't expose passphrase\n status: conn.status,\n isLocal: conn.config.id === localId,\n }));\n }\n\n getStatus(agentId: string): AgentStatus | null {\n return this.connections.get(agentId)?.status ?? null;\n }\n\n async shutdown(): Promise<void> {\n const ids = [...this.connections.keys()];\n for (const id of ids) {\n this.disconnectAgent(id);\n }\n }\n}\n\nexport const agentManager = new AgentManager();\n","import { randomUUID, randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';\nimport { renameWithRetry } from './run-store-utils.js';\nimport { dirname, join } from 'node:path';\nimport { keyedFilePath, legacyMachineFilePath, orgVaultRoot, listOrgVaults } from './vault-resolver.js';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport type { AgentConfig } from '@runeya/packages-shared';\n\nconst FILENAME = 'agents.json';\nconst ALGORITHM = 'aes-256-gcm';\n\ninterface StoredAgent {\n id: string;\n name: string;\n url: string;\n encrypted: string; // hex\n iv: string; // hex\n authTag: string; // hex\n}\n\nfunction encrypt(plaintext: string, vault?: string): { encrypted: string; iv: string; authTag: string } {\n const maybeKey = getEncryptionKey(vault);\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey; // Type narrowing\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n let enc = cipher.update(plaintext, 'utf-8', 'hex');\n enc += cipher.final('hex');\n const authTag = cipher.getAuthTag().toString('hex');\n return { encrypted: enc, iv: iv.toString('hex'), authTag };\n}\n\nfunction decrypt(encrypted: string, ivHex: string, authTagHex: string, vault?: string): string {\n // Validate all hex inputs before Buffer.from()\n if (!/^[0-9a-f]+$/i.test(encrypted) || !/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex)) {\n throw new Error('Invalid hex string in encrypted agent data');\n }\n const maybeKey = getEncryptionKey(vault);\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey; // Type narrowing\n const iv = Buffer.from(ivHex, 'hex');\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: 16 });\n decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));\n let dec = decipher.update(encrypted, 'hex', 'utf-8');\n dec += decipher.final('utf-8');\n return dec;\n}\n\ninterface AgentsFileData {\n agents: StoredAgent[];\n passthroughEnv: Record<string, string[]>;\n traefikConfigs: Record<string, unknown>;\n}\n\n/**\n * Où vit un agent : dans le coffre d'une organisation, ou sur la machine.\n *\n * Un agent décrivait jusqu'ici la machine, pour une seule raison — la clé qui\n * le chiffre y est rangée. Mais un service porte `agentId`, et ce champ part\n * dans le document synchronisé : la référence circule à l'échelle de\n * l'organisation pendant que sa cible reste enfermée ici. C'est ce décalage que\n * la portée corrige. Un agent d'organisation est rangé sous\n * `~/.runeya/orgs/<org>/`, et disparaît avec elle.\n *\n * `null` désigne la machine — le cas de l'agent local, qui n'est effectivement\n * l'affaire de personne d'autre : il exécute des processus sur CE poste.\n */\nexport type AgentOwner = string | null;\n\nclass AgentStore {\n private agents: Map<string, StoredAgent> = new Map();\n private passthroughEnv: Record<string, string[]> = {};\n private traefikConfigs: Record<string, unknown> = {};\n /** Le propriétaire de chaque agent, d'après le coffre où il a été lu. */\n private owners: Map<string, AgentOwner> = new Map();\n /**\n * Tous les coffres rencontrés, y compris ceux devenus vides.\n *\n * Déduire cette liste des agents encore présents laisserait le dernier agent\n * d'une organisation dans son fichier après suppression : plus personne pour\n * désigner ce coffre, donc plus personne pour le réécrire.\n */\n private knownOwners: Set<AgentOwner> = new Set([null]);\n private loaded = false;\n\n /**\n * Le fichier d'un propriétaire donné.\n *\n * Pour la machine, il reste à côté de la clé qui l'ouvre (`keyedFilePath`) :\n * lu sous une autre clé il se déchiffrerait en blancs, puis serait réécrit\n * blanc. Pour une organisation, c'est son coffre — la clé, elle, répond\n * toujours pour toute la machine.\n */\n /** L'identifiant de clé d'un agent : son organisation, ou la machine. */\n private vaultOf(id: string): string | undefined {\n return this.owners.get(id) ?? undefined;\n }\n\n private filePathFor(owner: AgentOwner): Promise<string> {\n if (owner === null) return keyedFilePath(FILENAME);\n return Promise.resolve(join(orgVaultRoot(owner), FILENAME));\n }\n\n /** Where older versions kept it — read only, so an older Runeya keeps working. */\n private getLegacyFilePath(): string {\n return legacyMachineFilePath(FILENAME);\n }\n\n /** Ce qu'un fichier de coffre contient, ou null s'il n'y en a pas. */\n private async readOwnerFile(owner: AgentOwner): Promise<AgentsFileData | null> {\n const paths = owner === null\n ? [await this.filePathFor(null), this.getLegacyFilePath()]\n : [await this.filePathFor(owner)];\n for (const path of paths) {\n let raw: string;\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n continue;\n }\n try {\n const data: unknown = JSON.parse(raw);\n // Legacy format: plain StoredAgent[]\n if (Array.isArray(data)) {\n return { agents: data as StoredAgent[], passthroughEnv: {}, traefikConfigs: {} };\n }\n const fileData = data as AgentsFileData;\n return {\n agents: fileData.agents ?? [],\n passthroughEnv: fileData.passthroughEnv ?? {},\n traefikConfigs: fileData.traefikConfigs ?? {},\n };\n } catch {\n // Fichier illisible : le suivant, plutôt que de perdre tout le reste.\n }\n }\n return null;\n }\n\n /**\n * Lire la machine puis chaque organisation présente sur ce disque.\n *\n * Les organisations sont relues du disque et non d'une liste tenue à part :\n * un coffre qui existe est une organisation dont les agents comptent, et\n * c'est déjà comme ça que la synchronisation décide de sa portée.\n */\n private async load(): Promise<void> {\n if (this.loaded) return;\n const owners: AgentOwner[] = [null, ...await listOrgVaults()];\n for (const owner of owners) {\n const data = await this.readOwnerFile(owner);\n if (!data) continue;\n this.knownOwners.add(owner);\n for (const a of data.agents) {\n this.agents.set(a.id, a);\n this.owners.set(a.id, owner);\n }\n Object.assign(this.passthroughEnv, data.passthroughEnv);\n Object.assign(this.traefikConfigs, data.traefikConfigs);\n }\n this.loaded = true;\n }\n\n /**\n * Réécrire chaque coffre concerné.\n *\n * Un agent emporte son `passthroughEnv` et sa configuration Traefik dans le\n * fichier de son propriétaire : les laisser côté machine rendrait la portée\n * mensongère — l'organisation partirait en laissant ses miettes derrière.\n * Tous les coffres sont réécrits, y compris ceux devenus vides, sinon un\n * agent déplacé subsisterait dans les deux.\n */\n private async save(): Promise<void> {\n const byOwner = new Map<AgentOwner, StoredAgent[]>();\n // Un coffre vidé doit être réécrit vide, donc on part des propriétaires\n // connus et pas seulement de ceux qui ont encore un agent.\n for (const owner of this.knownOwners) byOwner.set(owner, []);\n for (const agent of this.agents.values()) {\n const owner = this.owners.get(agent.id) ?? null;\n byOwner.set(owner, [...(byOwner.get(owner) ?? []), agent]);\n }\n\n for (const [owner, agents] of byOwner) {\n const ids = new Set(agents.map((a) => a.id));\n const fileData: AgentsFileData = {\n agents,\n passthroughEnv: Object.fromEntries(\n Object.entries(this.passthroughEnv).filter(([id]) => ids.has(id)),\n ),\n traefikConfigs: Object.fromEntries(\n Object.entries(this.traefikConfigs).filter(([id]) => ids.has(id)),\n ),\n };\n const filePath = await this.filePathFor(owner);\n await mkdir(dirname(filePath), { recursive: true });\n const tmpPath = filePath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(fileData, null, 2), 'utf-8');\n await renameWithRetry(tmpPath, filePath);\n await chmod(filePath, 0o600);\n }\n }\n\n private toAgentConfig(stored: StoredAgent): AgentConfig {\n return {\n id: stored.id,\n name: stored.name,\n url: stored.url,\n // La passphrase est chiffrée par la clé du coffre où l'agent est rangé :\n // celle de son organisation, ou celle de la machine.\n passphrase: decrypt(stored.encrypted, stored.iv, stored.authTag, this.vaultOf(stored.id)),\n passthroughEnv: this.passthroughEnv[stored.id] ?? [],\n };\n }\n\n async list(): Promise<AgentConfig[]> {\n await this.load();\n // If no encryption key, return empty list (setup mode)\n if (!getEncryptionKey()) {\n return [];\n }\n return Array.from(this.agents.values()).map(a => this.toAgentConfig(a));\n }\n\n async get(id: string): Promise<AgentConfig | null> {\n await this.load();\n // If no encryption key, return null (setup mode)\n if (!getEncryptionKey()) {\n return null;\n }\n const stored = this.agents.get(id);\n if (!stored) return null;\n return this.toAgentConfig(stored);\n }\n\n async register(name: string, url: string, passphrase: string, owner: AgentOwner = null): Promise<AgentConfig> {\n await this.load();\n // Check if an agent with the same URL already exists\n const existing = Array.from(this.agents.values()).find(a => a.url === url);\n // Un ré-enregistrement garde le coffre où l'agent vit déjà : chiffrer sous\n // une autre clé que celle de son fichier le rendrait illisible sur place.\n const vault = existing ? this.vaultOf(existing.id) : (owner ?? undefined);\n const { encrypted, iv, authTag } = encrypt(passphrase, vault);\n if (existing) {\n // Update existing agent instead of creating duplicate\n existing.name = name;\n existing.encrypted = encrypted;\n existing.iv = iv;\n existing.authTag = authTag;\n await this.save();\n return { id: existing.id, name, url, passphrase, passthroughEnv: this.passthroughEnv[existing.id] ?? [] };\n }\n const id = randomUUID();\n const stored: StoredAgent = { id, name, url, encrypted, iv, authTag };\n this.agents.set(id, stored);\n this.owners.set(id, owner);\n this.knownOwners.add(owner);\n await this.save();\n return { id, name, url, passphrase, passthroughEnv: [] };\n }\n\n async remove(id: string): Promise<boolean> {\n await this.load();\n if (!this.agents.has(id)) return false;\n this.agents.delete(id);\n this.owners.delete(id);\n delete this.passthroughEnv[id];\n delete this.traefikConfigs[id];\n await this.save();\n return true;\n }\n\n getPassthroughEnv(agentId: string): string[] {\n return this.passthroughEnv[agentId] ?? [];\n }\n\n async savePassthroughEnv(agentId: string, names: string[]): Promise<void> {\n await this.load();\n this.passthroughEnv[agentId] = names;\n await this.save();\n }\n\n getTraefikConfig(agentId: string): unknown {\n return this.traefikConfigs[agentId] ?? null;\n }\n\n async saveTraefikConfig(agentId: string, config: unknown): Promise<void> {\n await this.load();\n this.traefikConfigs[agentId] = config;\n await this.save();\n }\n\n async update(id: string, fields: { name?: string; url?: string; passphrase?: string }): Promise<AgentConfig | null> {\n await this.load();\n const stored = this.agents.get(id);\n if (!stored) return null;\n if (fields.name !== undefined) stored.name = fields.name;\n if (fields.url !== undefined) stored.url = fields.url;\n if (fields.passphrase !== undefined) {\n const { encrypted, iv, authTag } = encrypt(fields.passphrase, this.vaultOf(id));\n stored.encrypted = encrypted;\n stored.iv = iv;\n stored.authTag = authTag;\n }\n await this.save();\n return this.toAgentConfig(stored);\n }\n\n async updateUrl(id: string, url: string): Promise<AgentConfig | null> {\n await this.load();\n const stored = this.agents.get(id);\n if (!stored) return null;\n stored.url = url;\n this.agents.set(id, stored);\n await this.save();\n return this.toAgentConfig(stored);\n }\n}\n\nexport const agentStore = new AgentStore();\n","import { createHash } from 'node:crypto';\n\nconst KEY_SIZE_BYTES = 32;\n\n/**\n * Keys in memory, one per vault.\n *\n * A machine can hold several vaults at once — the local `.runeya` and one per\n * organization it follows — and they do not share a key. The keyring is\n * therefore indexed by vault root, with `DEFAULT_VAULT` standing for \"the vault\n * this process was launched in\".\n *\n * Every accessor keeps its no-argument form, which addresses that default\n * vault: the whole server currently runs in a single vault, and only the code\n * that knowingly juggles several has to name one.\n */\nconst DEFAULT_VAULT = '\\u0000default';\nconst keyring = new Map<string, Buffer>();\n/**\n * Coffres dont la clé existe mais n'a pas pu être ouverte.\n *\n * Un coffre inconnu du trousseau retombe sur la clé machine — c'est ce qui\n * permet à une organisation matérialisée après le déverrouillage de\n * fonctionner, et c'est sans danger : elle n'a pas encore de clé à elle, donc\n * ses données sont déjà sous celle-ci. Un coffre *verrouillé*, lui, a une clé\n * propre qu'on n'a pas su ouvrir : y écrire sous la clé machine passerait par\n * dessus des données qu'on ne sait pas lire. Le repli s'arrête là.\n */\nconst lockedVaults = new Set<string>();\nlet _ciMode = false;\n\n// Pending legacy key holder — kept in memory between migration and setup\nlet _pendingLegacyKey: Buffer | null = null;\n\n/** True only when the key was loaded from RUNEYA_ENCRYPTION_KEY (CI/CD — no JWT required). */\nexport function isCiMode(): boolean {\n return _ciMode;\n}\n\n/** Load encryption key from env var (CI mode) */\nexport async function loadFromEnv(): Promise<boolean> {\n const keyHex = process.env['RUNEYA_ENCRYPTION_KEY'];\n if (!keyHex) return false;\n if (!/^[0-9a-f]{64}$/i.test(keyHex)) {\n throw new Error('RUNEYA_ENCRYPTION_KEY invalide : attendu 64 caractères hex');\n }\n console.log('');\n console.log('⚠ ATTENTION : RUNEYA_ENCRYPTION_KEY est défini — l\\'authentification par master password est désactivée.');\n console.log(' Cette variable d\\'environnement est réservée aux environnements CI/CD.');\n console.log(' Ne la définissez jamais sur une machine de développement.');\n console.log('');\n setEncryptionKeyInMemory(Buffer.from(keyHex, 'hex'));\n _ciMode = true;\n return true;\n}\n\n// ── Pending legacy key holder ──────────────────────────────────────────────\n// After legacy migration, the libsodium key is stored here (NOT in the main\n// singleton) so that hasEncryptionKey() returns false → server state = setup.\n// The key is consumed by /api/setup to write .encryption.key.enc.\n\nexport function setPendingLegacyKey(key: Buffer): void {\n if (key.length !== KEY_SIZE_BYTES) throw new Error('Taille de clé invalide');\n _pendingLegacyKey = key;\n}\n\nexport function getPendingLegacyKey(): Buffer | null {\n return _pendingLegacyKey;\n}\n\nexport function clearPendingLegacyKey(): void {\n _pendingLegacyKey = null;\n}\n\nexport function hasPendingLegacyKey(): boolean {\n return _pendingLegacyKey !== null;\n}\n\n/** Set the encryption key in memory (called after successful unlock/setup) */\nexport function setEncryptionKeyInMemory(key: Buffer, vault: string = DEFAULT_VAULT): void {\n if (key.length !== KEY_SIZE_BYTES) throw new Error('Taille de clé invalide');\n keyring.set(vault, key);\n lockedVaults.delete(vault);\n announceUnlock();\n}\n\n/**\n * Prévenus quand un coffre devient lisible.\n *\n * Tant qu'aucune clé n'est là, les magasins rendent des listes vides — sans\n * erreur, par conception. Ce qui a lu avant le déverrouillage tient donc une\n * vue vide qu'aucune écriture ne viendra corriger : l'interface se rattrape en\n * refaisant ses requêtes, mais l'icône de barre système, elle, ne redemande\n * rien de son propre chef. D'où ce signal, posé au seul endroit par lequel une\n * clé entre en mémoire.\n */\nconst unlockListeners = new Set<() => void>();\n\nexport function onVaultUnlocked(listener: () => void): () => void {\n unlockListeners.add(listener);\n return () => unlockListeners.delete(listener);\n}\n\nfunction announceUnlock(): void {\n for (const listener of unlockListeners) {\n try {\n listener();\n } catch {\n // Un abonné qui échoue ne doit pas empêcher le déverrouillage.\n }\n }\n}\n\n/** Clear the cached key (lock). Without a vault, clears the whole keyring. */\nexport function clearEncryptionKey(vault?: string): void {\n if (vault === undefined) {\n keyring.clear();\n lockedVaults.clear();\n } else {\n keyring.delete(vault);\n }\n}\n\n/**\n * Ce coffre a-t-il une clé À LUI ?\n *\n * Volontairement plus strict que `getEncryptionKey`, qui replie sur la clé\n * machine : la question « puis-je chiffrer ici ? » et la question « ce coffre\n * a-t-il sa propre clé ? » sont deux questions différentes, et les confondre\n * ferait passer une organisation qui n'a pas encore la sienne pour une\n * organisation déjà dotée.\n */\nexport function hasEncryptionKey(vault: string = DEFAULT_VAULT): boolean {\n return keyring.has(vault);\n}\n\nexport function getEncryptionKey(vault: string = DEFAULT_VAULT): Buffer | null {\n const own = keyring.get(vault);\n if (own) return own;\n if (vault === DEFAULT_VAULT || lockedVaults.has(vault)) return null;\n return keyring.get(DEFAULT_VAULT) ?? null;\n}\n\n/** Ce coffre a une clé propre que ce master password n'ouvre pas. */\nexport function markVaultLocked(vault: string): void {\n lockedVaults.add(vault);\n keyring.delete(vault);\n}\n\n/** Ce coffre a une clé propre qu'on n'a pas su ouvrir — rien n'en est lisible. */\nexport function isVaultLocked(vault: string): boolean {\n return lockedVaults.has(vault);\n}\n\n/** Ce coffre s'ouvre à nouveau (clé chargée, ou adoptée). */\nexport function clearVaultLock(vault: string): void {\n lockedVaults.delete(vault);\n}\n\nexport function deriveStableSecret(purpose: string, vault: string = DEFAULT_VAULT): string | null {\n const key = getEncryptionKey(vault);\n if (!key) return null;\n return createHash('sha256').update(key).update(purpose).digest('hex');\n}\n\n/** Vault roots holding a loaded key — for callers that unlock several at once. */\nexport function unlockedVaults(): string[] {\n return Array.from(keyring.keys()).filter((v) => v !== DEFAULT_VAULT);\n}\n\n// Legacy compat: ensureEncryptionKey for code that still calls it\n// Returns null if key not loaded yet (LOCKED/SETUP state)\nexport async function ensureEncryptionKey(): Promise<Buffer | null> {\n return getEncryptionKey();\n}\n\n// Legacy compat: setEncryptionKey (used by old setup router)\nexport async function setEncryptionKey(keyHex: string): Promise<void> {\n if (!/^[0-9a-f]{64}$/i.test(keyHex)) throw new Error('Invalid key format');\n setEncryptionKeyInMemory(Buffer.from(keyHex, 'hex'));\n}\n","import { randomUUID } from 'node:crypto';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport { allVaultRoots, localVaultRoot , vaultIdOfRoot, projectVaultRoot } from './vault-resolver.js';\nimport { rootOfService, rootOfProjectRef, ensureProjectsLoaded } from './vault-registry.js';\nimport type { ProjectRef } from './project-key.js';\nimport { recordLocalRemoval } from './local-removals.js';\nimport { markVaultConflict, isVaultConflicted, isVaultClash } from './vault-conflicts.js';\nimport { fingerprintFiles } from './disk-freshness.js';\nimport { atomicWriteSecure } from './run-store-utils.js';\nimport { StoreDiskCoordinator } from './store-disk-coordinator.js';\nimport { environmentStore } from './environment-store.js';\nimport { stableEncryptValue, decryptValue, isEncryptedValue } from './crypto-helpers.js';\nimport { ServiceConfigSchema } from '@runeya/packages-shared';\nimport type { ServiceConfig, CreateService, UpdateService, LogSourceConfig } from '@runeya/packages-shared';\n\nconst FILENAME = 'services.json';\n\n/**\n * Encrypt sensitive log source credentials before writing to disk.\n *\n * `previous` is the last ciphertext written for the same sources: reusing it\n * when the plaintext is unchanged keeps the output byte-stable. Without it,\n * every save re-encrypts with a fresh IV, so an untouched service still looks\n * modified — which shows up as phantom git diffs, and as an endless sync loop\n * once the file is mirrored into a CRDT (each side keeps \"changing\" it).\n */\nfunction encryptLogSources(\n logSources: LogSourceConfig[],\n previous?: LogSourceConfig[],\n vault?: string,\n): LogSourceConfig[] {\n return logSources.map((src, i) => {\n const prev = previous?.[i];\n // Une valeur encore chiffrée est une valeur que le déchiffrement n'a pas su\n // ouvrir — clé absente, ou coffre lu sous une autre. La re-chiffrer\n // produirait un chiffré de chiffré, et la seule copie lisible serait\n // perdue : on la laisse telle quelle sur le disque.\n if (src.type === 'mongo') {\n if (isEncryptedValue(src.connectionString)) return src;\n const was = prev?.type === 'mongo' ? prev.connectionString : undefined;\n return { ...src, connectionString: stableEncryptValue(src.connectionString, was, vault) };\n }\n if (src.type === 'mysql' && src.password) {\n if (isEncryptedValue(src.password)) return src;\n const was = prev?.type === 'mysql' ? prev.password : undefined;\n return { ...src, password: stableEncryptValue(src.password, was, vault) };\n }\n return src;\n });\n}\n\n/** Decrypt sensitive log source credentials after reading from disk. */\nfunction decryptLogSources(logSources: LogSourceConfig[], vault?: string): LogSourceConfig[] {\n return logSources.map((src) => {\n if (src.type === 'mongo' && isEncryptedValue(src.connectionString)) {\n try {\n return { ...src, connectionString: decryptValue(src.connectionString, vault) };\n } catch {\n console.error('[service-store] Failed to decrypt mongo connectionString');\n return src;\n }\n }\n if (src.type === 'mysql' && src.password && isEncryptedValue(src.password)) {\n try {\n return { ...src, password: decryptValue(src.password, vault) };\n } catch {\n console.error('[service-store] Failed to decrypt mysql password');\n return src;\n }\n }\n return src;\n });\n}\n\n/**\n * Prepare service for disk: strip transient env/envSecrets/resolvedLogParsers\n * fields and encrypt log source credentials. `previous` is the service's last\n * on-disk form, so unchanged credentials keep their existing ciphertext.\n */\nfunction prepareForDisk(service: ServiceConfig, previous?: ServiceConfig, vault?: string): ServiceConfig {\n return {\n ...service,\n env: {},\n envSecrets: [],\n resolvedLogParsers: undefined,\n resolvedCwd: undefined,\n logSources: encryptLogSources(service.logSources ?? [], previous?.logSources, vault),\n };\n}\n\ntype ChangeListener = () => void;\n\nclass ServiceStore {\n private services: Map<string, ServiceConfig> = new Map();\n private loaded = false;\n /**\n * L'état du disque au dernier chargement — voir `ProjectStore.fingerprint` :\n * plusieurs Runeya partagent la racine machine, et un store chargé une fois\n * pour toutes ne verrait jamais les services créés par l'autre.\n */\n private fingerprint = '';\n private readonly disk = new StoreDiskCoordinator();\n private listeners: Set<ChangeListener> = new Set();\n /**\n * Last on-disk (encrypted) form per service id, so `prepareForDisk` can reuse\n * existing ciphertext instead of re-encrypting unchanged credentials.\n */\n private diskForm: Map<string, ServiceConfig> = new Map();\n\n onChange(fn: ChangeListener): () => void {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n\n private notify(): void {\n for (const fn of this.listeners) fn();\n }\n\n /** Which vault each service was read from, used until its project is known. */\n private roots: Map<string, string> = new Map();\n /**\n * L'organisation d'où vient un service reçu du cloud.\n *\n * Le coffre ne peut pas encore répondre : les contenus arrivent avant leur\n * projet, donc `rootFor` désignerait le répertoire de lancement et\n * chiffrerait les secrets de l'organisation avec la clé de la machine. Cette\n * provenance tient le temps que le projet devienne local — après quoi elle\n * dit la même chose que le coffre.\n */\n private remoteOrgs: Map<string, string> = new Map();\n\n /**\n * Oublier ce qui a été lu, pour tout relire sous la clé courante.\n *\n * Appelé après l'adoption d'une clé d'organisation : le disque vient d'être\n * re-chiffré sous les pieds du store, si bien que sa mémoire décrit un état\n * qui n'existe plus. La laisser en place n'affichait pas seulement des\n * comptes périmés — la sauvegarde suivante réécrivait l'ancien chiffré\n * par-dessus le nouveau, et défaisait l'adoption.\n */\n resetCache(): void {\n this.services.clear();\n this.diskForm.clear();\n this.roots.clear();\n this.remoteOrgs.clear();\n this.loaded = false;\n // Les clients abonnés tiennent l'ancienne liste : sans ce signal, l'écran\n // ne se corrigerait qu'au prochain appel qu'il ferait de lui-même.\n this.notify();\n }\n\n private getFilePath(root: string): string {\n return join(root, FILENAME);\n }\n\n /**\n * The vault a service must be written to: the one of the project owning it.\n *\n * A service carries no project id — ownership lives in `project.serviceIds` —\n * so the registry answers, falling back to where the service was read and\n * finally to the local vault.\n */\n /** La clé sous laquelle chiffrer ce service : son organisation, ou son coffre. */\n private vaultOf(serviceId: string): string | undefined {\n return this.remoteOrgs.get(serviceId) ?? vaultIdOfRoot(this.rootFor(serviceId));\n }\n\n private rootFor(serviceId: string): string {\n // La provenance sert d'arbitre quand deux projets revendiquent le service.\n return rootOfService(serviceId, this.roots.get(serviceId) ?? null) ?? localVaultRoot();\n }\n\n /** Ce que le disque porte : un fichier de services par coffre. */\n private async diskFingerprint(): Promise<string> {\n return fingerprintFiles((await allVaultRoots()).map((root) => this.getFilePath(root)));\n }\n\n /**\n * Read every vault: the launch directory and each materialized cloud project.\n *\n * Roots come from the disk, not from the project store, so services load\n * without waiting on projects.\n */\n private async load(): Promise<void> {\n if (!getEncryptionKey()) return;\n await this.disk.load(async () => {\n const fingerprint = await this.diskFingerprint();\n if (this.loaded && fingerprint === this.fingerprint) return null;\n // Les projets d'abord : tant qu'ils sont inconnus, tout se résoudrait vers\n // le coffre de lancement et y serait réécrit à la première sauvegarde —\n // hors de l'organisation propriétaire.\n await ensureProjectsLoaded();\n const next = await this.readVaults();\n return () => {\n this.services = next.services;\n this.diskForm = next.diskForm;\n this.roots = next.roots;\n this.fingerprint = fingerprint;\n this.loaded = true;\n if (this.services.size > 0) this.notify();\n };\n });\n }\n\n private async readVaults(): Promise<{\n services: Map<string, ServiceConfig>;\n diskForm: Map<string, ServiceConfig>;\n roots: Map<string, string>;\n }> {\n const services = new Map<string, ServiceConfig>();\n const diskForm = new Map<string, ServiceConfig>();\n const roots = new Map<string, string>();\n for (const root of await allVaultRoots()) {\n try {\n const raw = await readFile(this.getFilePath(root), 'utf-8');\n const data: ServiceConfig[] = JSON.parse(raw);\n // Un coffre qui rapporte un service déjà tenu par un autre est une\n // copie du même projet dans une seconde organisation : on garde le\n // premier et on laisse le second intact plutôt que de l'écraser.\n const clash = data.find((s) => isVaultClash(root, roots.get(s.id)));\n if (clash) {\n markVaultConflict(root, `service ${clash.id}`);\n continue;\n }\n for (const s of data) {\n // Ensure shell defaults to true for legacy commands stored without it\n if (s.commands) {\n for (const cmd of s.commands) {\n if (cmd.shell === undefined) cmd.shell = true;\n }\n }\n diskForm.set(s.id, s);\n services.set(s.id, {\n ...s,\n logSources: decryptLogSources(s.logSources ?? [], vaultIdOfRoot(root)),\n });\n roots.set(s.id, root);\n }\n } catch {\n // No services in this vault yet.\n }\n }\n return { services, diskForm, roots };\n }\n\n /**\n * Write each vault that holds — or held — a service.\n *\n * A vault emptied by this save loses its file: a service that moved to\n * another vault must not survive in the one it left, and a `[]` left behind\n * would keep (or recreate) a `.runeya` in a directory that holds nothing.\n */\n private save(): Promise<void> {\n return this.disk.save(() => this.writeVaults());\n }\n\n private async writeVaults(): Promise<void> {\n const byRoot = new Map<string, ServiceConfig[]>();\n for (const root of this.roots.values()) {\n if (!isVaultConflicted(root)) byRoot.set(root, []);\n }\n byRoot.set(localVaultRoot(), byRoot.get(localVaultRoot()) ?? []);\n\n for (const service of this.services.values()) {\n const root = this.rootFor(service.id);\n // Le coffre décide de la clé : un service d'organisation est chiffré par\n // la sienne, pas par celle de la machine.\n const prepared = prepareForDisk(service, this.diskForm.get(service.id), this.vaultOf(service.id));\n this.diskForm.set(prepared.id, prepared);\n this.roots.set(service.id, root);\n if (isVaultConflicted(root)) continue;\n const bucket = byRoot.get(root);\n if (bucket) bucket.push(prepared);\n else byRoot.set(root, [prepared]);\n }\n\n for (const [root, services] of byRoot) {\n const filePath = this.getFilePath(root);\n if (services.length === 0) {\n // Un coffre vidé perd son fichier plutôt que d'en garder une liste\n // vide. Écrire `[]` recréait un `.runeya` dans le dépôt de lancement\n // alors que tous les projets vivent désormais dans le cloud — un\n // fichier fantôme qui apparaissait dans le git status de l'utilisateur.\n await rm(filePath, { force: true }).catch(() => {});\n continue;\n }\n await atomicWriteSecure(filePath, JSON.stringify(services, null, 2));\n }\n\n // Nos propres écritures deviennent la référence : sans ça, le chargement\n // suivant les prendrait pour celles d'une autre instance et relirait tout.\n this.fingerprint = await this.diskFingerprint();\n }\n\n /**\n * Drop services from memory, without touching the disk.\n *\n * Used when their project stops being followed: its vault goes away whole,\n * and services left in memory would be written back on the next save — into\n * the local vault, since their owner is no longer known.\n */\n async forgetLocally(serviceIds: string[]): Promise<void> {\n await this.load();\n for (const id of serviceIds) {\n this.services.delete(id);\n this.diskForm.delete(id);\n this.roots.delete(id);\n this.remoteOrgs.delete(id);\n }\n this.notify();\n }\n\n /**\n * Rewrite every vault, moving entities that now belong elsewhere.\n *\n * Called once the project store knows where projects live: entities read from\n * the launch directory whose project turns out to be a cloud one are written\n * into that project's vault, and disappear from the directory they came from.\n */\n async relocateAll(): Promise<void> {\n await this.load();\n await this.save();\n }\n\n /**\n * Réattribuer l'organisation de services dont le projet vient de changer de\n * coffre.\n *\n * Un service porte son `orgId` en propre : laissé à `null` après le passage\n * du projet dans une organisation, il partirait dans le doc en se disant\n * local. La sauvegarde qui suit les écrit dans leur nouveau coffre et les\n * retire de l'ancien, le registre ayant déjà été mis à jour par le projet.\n */\n async reassignOrg(serviceIds: string[], orgId: string | null): Promise<void> {\n await this.load();\n for (const id of serviceIds) {\n const service = this.services.get(id);\n if (service) this.services.set(id, { ...service, orgId });\n }\n await this.save();\n this.notify();\n }\n\n async list(): Promise<ServiceConfig[]> {\n await this.load();\n return Array.from(this.services.values());\n }\n\n async get(id: string): Promise<ServiceConfig | null> {\n await this.load();\n return this.services.get(id) ?? null;\n }\n\n /**\n * Service in its on-disk (sanitized) form: transient runtime env and plaintext\n * secrets stripped. Used when exporting a project to the cloud so that no\n * secret ever leaves the machine.\n */\n async getSanitized(id: string): Promise<ServiceConfig | null> {\n await this.load();\n const service = this.services.get(id);\n return service ? prepareForDisk(service, this.diskForm.get(id), this.vaultOf(id)) : null;\n }\n\n /**\n * `project` : le projet qui va l'adopter, quand on le connaît. Le service est\n * alors écrit d'emblée dans son coffre — sans lui, il naissait dans le coffre\n * de lancement et y laissait une copie une fois rattaché.\n */\n async create(input: CreateService, project?: ProjectRef): Promise<ServiceConfig> {\n await this.load();\n const service: ServiceConfig = {\n id: randomUUID(),\n orgId: input.orgId ?? null,\n agentId: input.agentId,\n name: input.name,\n commands: input.commands ?? [],\n cwd: input.cwd,\n ports: input.ports ?? [],\n groups: input.groups ?? [],\n description: input.description,\n docsPath: input.docsPath,\n url: input.url,\n localUrls: input.localUrls ?? [],\n openapiUrl: input.openapiUrl,\n git: input.git,\n meta: input.meta,\n env: {},\n envSecrets: [],\n runner: input.runner ?? 'native',\n dockerConfig: input.dockerConfig,\n healthCheck: input.healthCheck,\n parserIds: input.parserIds ?? [],\n shortcuts: input.shortcuts ?? [],\n logSources: input.logSources ?? [],\n autoRestart: input.autoRestart ?? false,\n restartBackoffMs: input.restartBackoffMs ?? 1000,\n ...(input.restartStrategy !== undefined && { restartStrategy: input.restartStrategy }),\n ...(input.maxRestarts !== undefined && { maxRestarts: input.maxRestarts }),\n clearLogsOnStart: input.clearLogsOnStart ?? false,\n };\n this.services.set(service.id, service);\n // Provenance donnée d'avance : `rootFor` s'y range tant qu'aucun projet ne\n // revendique encore le service.\n if (project) this.roots.set(service.id, rootOfProjectRef(project));\n await this.save();\n this.notify();\n return service;\n }\n\n async update(input: UpdateService): Promise<ServiceConfig | null> {\n await this.load();\n const existing = this.services.get(input.id);\n if (!existing) return null;\n const updated: ServiceConfig = {\n ...existing,\n ...(input.agentId !== undefined && { agentId: input.agentId ?? undefined }),\n ...(input.name !== undefined && { name: input.name }),\n ...(input.commands !== undefined && { commands: input.commands }),\n ...(input.cwd !== undefined && { cwd: input.cwd ?? undefined }),\n ...(input.ports !== undefined && { ports: input.ports }),\n ...(input.groups !== undefined && { groups: input.groups }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.docsPath !== undefined && { docsPath: input.docsPath }),\n ...(input.url !== undefined && { url: input.url }),\n ...(input.localUrls !== undefined && { localUrls: input.localUrls }),\n ...(input.openapiUrl !== undefined && { openapiUrl: input.openapiUrl }),\n ...(input.git !== undefined && { git: input.git ?? undefined }),\n ...(input.meta !== undefined && { meta: input.meta ?? undefined }),\n ...(input.runner !== undefined && { runner: input.runner }),\n ...(input.dockerConfig !== undefined && { dockerConfig: input.dockerConfig }),\n ...(input.healthCheck !== undefined && { healthCheck: input.healthCheck ?? undefined }),\n ...(input.autoRestart !== undefined && { autoRestart: input.autoRestart }),\n ...(input.clearLogsOnStart !== undefined && { clearLogsOnStart: input.clearLogsOnStart }),\n ...(input.restartBackoffMs !== undefined && { restartBackoffMs: input.restartBackoffMs }),\n ...(input.restartStrategy !== undefined && { restartStrategy: input.restartStrategy }),\n ...(input.maxRestarts !== undefined && { maxRestarts: input.maxRestarts }),\n ...(input.orgId !== undefined && { orgId: input.orgId }),\n ...(input.shortcuts !== undefined && { shortcuts: input.shortcuts }),\n ...(input.parserIds !== undefined && { parserIds: input.parserIds }),\n ...(input.logSources !== undefined && { logSources: input.logSources }),\n };\n this.services.set(input.id, updated);\n await this.save();\n this.notify();\n return updated;\n }\n\n async delete(id: string): Promise<boolean> {\n await this.load();\n if (!this.services.has(id)) return false;\n this.services.delete(id);\n recordLocalRemoval('services', id);\n await this.save();\n this.notify();\n // Clean up service-scoped environments\n await environmentStore.deleteByReference(id);\n return true;\n }\n\n // ---- Cloud sync ------------------------------------------------------\n\n /** The services of a project, in their on-disk form (what the cloud mirrors). */\n async listSanitizedByIds(ids: string[]): Promise<ServiceConfig[]> {\n await this.load();\n const out: ServiceConfig[] = [];\n for (const id of ids) {\n const service = this.services.get(id);\n if (service) out.push(prepareForDisk(service, this.diskForm.get(id), this.vaultOf(id)));\n }\n return out;\n }\n\n /**\n * Apply a service received from the cloud, in its on-disk form.\n *\n * Validated rather than trusted: a peer running a different version — or a\n * tampered doc — must not be able to write arbitrary shapes into\n * `services.json`. Diff-guarded against the on-disk form so re-applying an\n * identical service neither saves nor notifies (loop-safe).\n */\n async applyRemoteUpsert(\n id: string, fields: unknown, vault?: string, projectId?: string,\n ): Promise<{ created: boolean }> {\n await this.load();\n // Retenue avant tout : la sauvegarde qui suit doit chiffrer sous la même\n // clé que celle qui vient d'ouvrir, pas sous celle du coffre de repli.\n if (vault) this.remoteOrgs.set(id, vault);\n // Le projet que le doc lui donne, quand il arrive avant ce projet : sans\n // cette provenance, le service atterrissait dans le coffre de lancement et\n // y restait en double une fois le projet arrivé.\n if (vault && projectId && !this.roots.has(id)) this.roots.set(id, projectVaultRoot(vault, projectId));\n const parsed = ServiceConfigSchema.safeParse({ ...(fields as object), id });\n if (!parsed.success) {\n console.error('[service-store] Rejected remote service %s:', id, parsed.error.message);\n return { created: false };\n }\n const incoming = parsed.data;\n const existing = this.services.get(id);\n if (existing) {\n const currentDisk = prepareForDisk(existing, this.diskForm.get(id), vault ?? this.vaultOf(id));\n if (JSON.stringify(currentDisk) === JSON.stringify(incoming)) return { created: false };\n }\n this.diskForm.set(id, incoming);\n this.services.set(id, {\n ...incoming,\n // The doc carries the on-disk form, where credentials are ciphertext;\n // in memory they are expected in the clear, as `load()` keeps them.\n logSources: decryptLogSources(incoming.logSources ?? [], vault ?? this.vaultOf(id)),\n });\n await this.save();\n this.notify();\n return { created: !existing };\n }\n\n /** Delete a service that was removed on another peer. */\n async applyRemoteDelete(id: string): Promise<boolean> {\n await this.load();\n if (!this.services.has(id)) return false;\n this.services.delete(id);\n this.diskForm.delete(id);\n await this.save();\n this.notify();\n await environmentStore.deleteByReference(id);\n return true;\n }\n}\n\nexport const serviceStore = new ServiceStore();\n","/**\n * L'identité d'un projet, telle que le disque et le cloud la connaissent.\n *\n * Un `projectId` seul ne désigne rien : le même id peut exister dans plusieurs\n * organisations — c'est ce qui arrive dès qu'un projet est suivi par une org\n * puis par une autre, l'entrée précédente restant dans le doc de la première.\n * Ce qui place un projet sur disque est déjà le couple (`vaultFor`), donc c'est\n * le couple qui doit l'identifier partout au-dessus.\n */\n\nexport interface ProjectRef {\n id: string;\n orgId?: string | null;\n}\n\n/** Deux projets ont la même clé exactement quand ils sont le même projet. */\nexport function projectKey(ref: ProjectRef): string {\n return `${ref.orgId ?? 'local'}:${ref.id}`;\n}\n\n/**\n * Un `projectId` a désigné un projet à lui seul, et le désigne encore dans tout\n * ce qu'un projet contient : un environnement, une conversation, un override ne\n * nomment que leur projet. Tant que ces formats n'ont pas migré, la résolution\n * par id seul doit rester possible — mais elle doit échouer bruyamment quand\n * plusieurs projets répondent, plutôt que d'en élire un au hasard.\n */\nexport class AmbiguousProjectError extends Error {\n readonly code = 'AMBIGUOUS_PROJECT_ID';\n\n constructor(readonly projectId: string, readonly orgIds: (string | null)[]) {\n const where = orgIds.map((o) => o ?? 'local').join(', ');\n super(\n `Project id ${projectId} exists in several organizations (${where}). ` +\n 'Name the organization alongside the project id.',\n );\n this.name = 'AmbiguousProjectError';\n }\n}\n","import { localVaultRoot } from './vault-resolver.js';\nimport { projectKey, AmbiguousProjectError, type ProjectRef } from './project-key.js';\n\n/**\n * Which vault each entity belongs to.\n *\n * Projects know where they live — `vaultFor` derives it from their `orgId` —\n * but their contents do not: a service carries no project id (ownership lives\n * in `project.serviceIds`), and an environment only names its project. Rather\n * than have every store reach into the project store — and import its way into\n * a cycle — the project store publishes what it knows here, and the content\n * stores read it.\n *\n * Everything is keyed by the (organization, project) couple, because that is\n * what identifies a project: the same project id can exist in two organizations\n * and point at two different vaults. Contained entities still name their project\n * by id alone, so `rootOfProject` resolves that id — and refuses to guess when\n * more than one project answers to it.\n *\n * Anything unknown resolves to the local vault. That is the honest default: an\n * entity nobody claims belongs to the directory the server was launched in,\n * which is exactly where it used to live before vaults existed.\n */\n\n/** Set by the project store, so content stores can make sure it has loaded. */\nlet loadProjects: (() => Promise<void>) | null = null;\n\n/** key = `${orgId ?? 'local'}:${projectId}` */\nconst projectRoots = new Map<string, string>();\n/** The refs behind each bare project id, to spot an id that answers twice. */\nconst projectRefs = new Map<string, ProjectRef>();\n/**\n * Quels projets revendiquent chaque service.\n *\n * Un ensemble, pas un propriétaire unique : deux organisations portant le même\n * projet déclarent le même service, et retenir le dernier faisait écrire ce\n * service dans le coffre de l'autre — vidant celui d'où il venait.\n */\nconst serviceOwners = new Map<string, Set<string>>();\n\nexport function registerProjectLoader(load: () => Promise<void>): void {\n loadProjects = load;\n}\n\n/**\n * Make sure the project store has read the disk.\n *\n * Content stores call this before their own load: until projects are known,\n * every id would resolve to the local vault and cloud files would be missed.\n */\nexport async function ensureProjectsLoaded(): Promise<void> {\n await loadProjects?.();\n}\n\nexport function setProjectVault(ref: ProjectRef, root: string): void {\n const key = projectKey(ref);\n projectRoots.set(key, root);\n projectRefs.set(key, { id: ref.id, orgId: ref.orgId ?? null });\n}\n\nexport function forgetProject(ref: ProjectRef): void {\n const key = projectKey(ref);\n projectRoots.delete(key);\n projectRefs.delete(key);\n for (const [serviceId, owners] of serviceOwners) {\n owners.delete(key);\n if (owners.size === 0) serviceOwners.delete(serviceId);\n }\n}\n\n/** Record which services a project owns, replacing what it owned before. */\nexport function setProjectServices(ref: ProjectRef, serviceIds: string[]): void {\n const key = projectKey(ref);\n for (const [serviceId, owners] of serviceOwners) {\n if (!serviceIds.includes(serviceId)) {\n owners.delete(key);\n if (owners.size === 0) serviceOwners.delete(serviceId);\n }\n }\n for (const serviceId of serviceIds) {\n const owners = serviceOwners.get(serviceId) ?? new Set<string>();\n owners.add(key);\n serviceOwners.set(serviceId, owners);\n }\n}\n\n/** Where a project lives, when its organization is known. */\nexport function rootOfProjectRef(ref: ProjectRef): string {\n return projectRoots.get(projectKey(ref)) ?? localVaultRoot();\n}\n\n/** Every project registered under a bare id — one, usually. */\nexport function refsOfProjectId(projectId: string): ProjectRef[] {\n const matches: ProjectRef[] = [];\n for (const ref of projectRefs.values()) {\n if (ref.id === projectId) matches.push(ref);\n }\n return matches;\n}\n\n/**\n * Where a project lives, from its id alone.\n *\n * Throws when the id answers for two projects: picking one would write a\n * project's services into another organization's vault, which is precisely the\n * silent corruption the composite key exists to prevent.\n */\nexport function rootOfProject(projectId: string): string {\n return rootOfProjectAmong(projectId, null);\n}\n\n/**\n * Where a project lives, from its id and the vault an entity came from.\n *\n * Everything a project contains — an environment, an override, a conversation —\n * names its project by id alone, but sits inside that project's vault. When the\n * id answers for two projects, that directory says which one: it is the couple,\n * written down by the layout. Without such a hint, an ambiguous id still throws\n * rather than electing one of the two.\n */\nexport function rootOfProjectAmong(projectId: string, hint: string | null): string {\n const matches = refsOfProjectId(projectId);\n if (matches.length > 1) {\n const known = matches.map((ref) => rootOfProjectRef(ref));\n if (hint && known.includes(hint)) return hint;\n throw new AmbiguousProjectError(projectId, matches.map((r) => r.orgId ?? null));\n }\n // Aucun projet connu sous cet id : l'entité reste là où elle a été lue. La\n // renvoyer au coffre de lancement la sortirait de son organisation — ce qui\n // arrive précisément quand les projets ne sont pas encore chargés.\n const [ref] = matches;\n return ref ? rootOfProjectRef(ref) : (hint ?? localVaultRoot());\n}\n\n/** The couple behind a project id, disambiguated by the vault an entity came from. */\nexport function refOfProjectAmong(projectId: string, hint: string | null): ProjectRef {\n const matches = refsOfProjectId(projectId);\n if (matches.length > 1) {\n const found = hint ? matches.find((ref) => rootOfProjectRef(ref) === hint) : undefined;\n if (found) return found;\n throw new AmbiguousProjectError(projectId, matches.map((r) => r.orgId ?? null));\n }\n return matches[0] ?? { id: projectId, orgId: null };\n}\n\nexport function rootOfService(serviceId: string, hint: string | null = null): string {\n const owners = serviceOwners.get(serviceId);\n if (!owners || owners.size === 0) return hint ?? localVaultRoot();\n const roots = Array.from(owners, (key) => projectRoots.get(key)).filter((r): r is string => !!r);\n // Revendiqué par plusieurs projets — le même projet dans deux organisations :\n // le coffre d'où le service a été lu tranche, sinon le premier connu.\n if (hint && roots.includes(hint)) return hint;\n return roots[0] ?? hint ?? localVaultRoot();\n}\n\n/**\n * Every vault holding data, the local one first.\n *\n * Stores read all of them and write back each one they touched, so an entity\n * that moves between vaults never lingers in the one it came from.\n */\nexport function knownRoots(): string[] {\n const roots = new Set<string>([localVaultRoot()]);\n for (const root of projectRoots.values()) roots.add(root);\n return Array.from(roots);\n}\n\n/** Test seam: drop everything the registry learned. */\nexport function resetVaultRegistry(): void {\n projectRoots.clear();\n projectRefs.clear();\n serviceOwners.clear();\n loadProjects = null;\n}\n","/**\n * Ce que cette machine a retiré, par un geste explicite.\n *\n * La synchronisation ne déduit jamais une suppression d'une absence : une\n * entité absente d'ici peut n'avoir pas été lue (coffre écarté, scellé, copie\n * pas encore descendue), et la retirer du document l'effaçait pour toute\n * l'organisation. Seuls les retraits inscrits ici partent au cloud.\n *\n * Deux registres :\n * - les entités supprimées (services, environnements, parseurs) ;\n * - les liens défaits entre un projet et ses services ou environnements — un\n * projet repoussé garde sinon les ids que le document connaît et pas nous.\n *\n * En mémoire : un redémarrage avant l'envoi fait réapparaître l'entité, ce qui\n * se rattrape ; l'inverse — perdre une donnée — ne se rattrape pas.\n */\n\nexport type RemovableKind = 'services' | 'environments' | 'logParsers';\nexport type ProjectLinkField = 'serviceIds' | 'environmentIds';\n\nconst removed = new Map<RemovableKind, Set<string>>();\nconst detached = new Map<string, Set<string>>();\n\nconst linkKey = (projectId: string, field: ProjectLinkField): string => `${projectId}:${field}`;\n\nexport function recordLocalRemoval(kind: RemovableKind, id: string): void {\n const ids = removed.get(kind) ?? new Set<string>();\n ids.add(id);\n removed.set(kind, ids);\n}\n\nexport function isLocallyRemoved(kind: RemovableKind, id: string): boolean {\n return removed.get(kind)?.has(id) ?? false;\n}\n\nexport function clearLocalRemoval(kind: RemovableKind, id: string): void {\n removed.get(kind)?.delete(id);\n}\n\n/** Inscrire les ids qu'une nouvelle liste retire de l'ancienne. */\nexport function recordDetached(\n projectId: string, field: ProjectLinkField, before: readonly string[], after: readonly string[],\n): void {\n const kept = new Set(after);\n const gone = before.filter((id) => !kept.has(id));\n if (gone.length === 0) return;\n const key = linkKey(projectId, field);\n const ids = detached.get(key) ?? new Set<string>();\n for (const id of gone) ids.add(id);\n detached.set(key, ids);\n}\n\nexport function isDetached(projectId: string, field: ProjectLinkField, id: string): boolean {\n return detached.get(linkKey(projectId, field))?.has(id) ?? false;\n}\n\nexport function clearDetached(projectId: string, field: ProjectLinkField): void {\n detached.delete(linkKey(projectId, field));\n}\n\n/** Test seam. */\nexport function resetLocalRemovals(): void {\n removed.clear();\n detached.clear();\n}\n","/**\n * Les coffres dont le contenu ne peut pas être tenu en mémoire sans écraser\n * celui d'un autre.\n *\n * Deux organisations peuvent porter le même projet — même id de projet, et donc\n * les mêmes ids de services, d'environnements, de conversations, puisque le\n * second coffre est une copie du premier. Les stores de contenu, eux, indexent\n * par id d'entité : le dernier coffre lu écraserait le précédent, et la\n * sauvegarde suivante réécrirait le coffre perdant à partir de données qui ne\n * sont plus les siennes.\n *\n * Plutôt que d'élire un gagnant en silence, le coffre en second est déclaré en\n * conflit : son contenu est ignoré en mémoire, et surtout **plus jamais écrit**.\n * Les fichiers restent intacts sur le disque, prêts à servir dès que le conflit\n * est levé — en cessant de suivre l'un des deux projets.\n */\n\nimport { localVaultRoot } from './vault-resolver.js';\n\nconst conflicted = new Map<string, string>();\n\n/**\n * Le coffre `root` rapporte-t-il une entité déjà lue ailleurs (`heldIn`) au\n * point d'être une copie d'un autre projet ?\n *\n * Pas quand la première copie vient du coffre de lancement : il n'est la copie\n * d'aucun projet d'organisation. Une entité y naît tant qu'aucun projet ne la\n * revendique, puis passe dans le coffre du projet qui l'adopte — la copie de\n * départ qui traîne est un reste, et la sauvegarde suivante l'efface. En faire\n * un conflit écartait tout le coffre du projet, et ses autres entités avec.\n */\nexport function isVaultClash(root: string, heldIn: string | undefined): boolean {\n return heldIn !== undefined && heldIn !== root && heldIn !== localVaultRoot();\n}\n\nexport function markVaultConflict(root: string, detail: string): void {\n if (conflicted.has(root)) return;\n conflicted.set(root, detail);\n console.error(\n '[vaults] %s partage des identifiants avec un autre coffre (%s) — ' +\n 'son contenu est ignoré et laissé intact. Cessez de suivre l\\'un des deux projets.',\n root, detail,\n );\n}\n\nexport function isVaultConflicted(root: string): boolean {\n return conflicted.has(root);\n}\n\n/** Les coffres écartés, pour un diagnostic exposable à l'utilisateur. */\nexport function conflictedVaults(): { root: string; detail: string }[] {\n return Array.from(conflicted, ([root, detail]) => ({ root, detail }));\n}\n\n/** Test seam, et relecture après qu'un projet a cessé d'être suivi. */\nexport function resetVaultConflicts(): void {\n conflicted.clear();\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Recharger un store depuis le disque sans jamais perdre ce qu'il contient.\n *\n * Plusieurs Runeya partagent la racine machine : un store relit ses fichiers\n * quand ils ont bougé. Relire en vidant la mémoire d'abord laissait une fenêtre\n * où une sauvegarde concurrente écrivait un état à moitié rempli, et effaçait\n * du disque ce qui n'était pas encore relu — c'est ce qui a vidé des coffres le\n * 12 septembre. Trois règles tiennent ici, pour tous les stores :\n *\n * - un rechargement lit dans des structures neuves et ne les substitue qu'une\n * fois complètes ;\n * - un seul rechargement à la fois, et jamais pendant une écriture en file ;\n * - une sauvegarde partie pendant la lecture la rend caduque : on relit.\n *\n * Les écritures passent en file, chacune écrivant la mémoire au moment où vient\n * son tour : lancées ensemble, la plus ancienne pouvait finir la dernière et\n * recouvrir la plus récente.\n */\nconst insideReload = new AsyncLocalStorage<Set<StoreDiskCoordinator>>();\n\nexport class StoreDiskCoordinator {\n private reloading: Promise<void> | null = null;\n private saves = 0;\n private queue: Promise<void> = Promise.resolve();\n\n /**\n * `read` rend de quoi substituer le nouvel état, ou `null` s'il n'y a rien à\n * relire. La substitution doit être synchrone : c'est ce qui la rend atomique.\n */\n load(read: () => Promise<(() => void) | null>): Promise<void> {\n // Appelé depuis une lecture en cours — la sienne, ou celle d'un store qui\n // en interroge un autre : attendre un rechargement déjà lancé peut boucler.\n // Deux rechargements partis par deux portes s'attendaient l'un l'autre\n // (environnements → projets → environnements). L'appel imbriqué travaille\n // sur la mémoire actuelle.\n const inside = insideReload.getStore();\n if (inside && (inside.has(this) || this.reloading)) return Promise.resolve();\n this.reloading ??= this.reload(read).finally(() => { this.reloading = null; });\n return this.reloading;\n }\n\n private async reload(read: () => Promise<(() => void) | null>): Promise<void> {\n for (;;) {\n await this.queue;\n const savesBefore = this.saves;\n const outer = insideReload.getStore() ?? new Set<StoreDiskCoordinator>();\n const commit = await insideReload.run(new Set([...outer, this]), read);\n if (!commit) return;\n if (this.saves !== savesBefore) continue;\n commit();\n return;\n }\n }\n\n save(write: () => Promise<void>): Promise<void> {\n this.saves += 1;\n const run = this.queue.then(write);\n this.queue = run.catch(() => {});\n return run;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport { allVaultRoots, localVaultRoot, vaultFor, vaultIdOfRoot } from './vault-resolver.js';\nimport { rememberFile, hasChangedSinceRead, wasRead, fingerprintFiles } from './disk-freshness.js';\nimport { rootOfProjectAmong, refOfProjectAmong, ensureProjectsLoaded } from './vault-registry.js';\nimport { markVaultConflict, isVaultConflicted, isVaultClash } from './vault-conflicts.js';\nimport { recordLocalRemoval } from './local-removals.js';\nimport { atomicWriteSecure } from './run-store-utils.js';\nimport { StoreDiskCoordinator } from './store-disk-coordinator.js';\nimport type { ProjectRef } from './project-key.js';\nimport {\n encryptVariableSlots,\n decryptVariableSlots,\n extractEncryptedVariableSlots,\n} from './crypto-helpers.js';\nimport type { Environment, CreateEnvironment, UpdateEnvironment, VariableSlot } from '@runeya/packages-shared';\nimport { CreateEnvironmentSchema, EnvironmentSchema } from '@runeya/packages-shared';\n\nconst FILENAME = 'environments.json';\n\ninterface DiskEncryptedCache {\n envSlots: Record<string, string>;\n}\n\ntype ChangeListener = () => void;\n\nclass EnvironmentStore {\n private environments: Map<string, Environment> = new Map();\n private loaded = false;\n /** Cache of encrypted secret values as last written to disk */\n private diskEncryptedSlots: Map<string, DiskEncryptedCache> = new Map();\n /**\n * Les secrets qu'aucune clé chargée n'a ouverts, par environnement.\n *\n * Vides en mémoire, ils doivent repartir sur le disque sous leur forme\n * chiffrée : c'est ce que retient cette liste. Voir `keepUnreadableSecrets`.\n */\n private unreadableSecrets: Map<string, Set<string>> = new Map();\n /**\n * Coffres dont le fichier a bougé depuis la lecture.\n *\n * On ne les réécrit pas. Ni avec notre copie — elle décrirait l'état d'avant\n * la réparation — ni sans elle, ce qui effacerait les secrets qu'elle\n * protège. Entre les deux, la seule issue sûre est de ne pas toucher au\n * fichier et de laisser le prochain chargement faire foi.\n */\n private staleRoots: Set<string> = new Set();\n /**\n * L'état du disque au dernier chargement — voir `ProjectStore.fingerprint`.\n *\n * `staleRoots` dit « ne réécris pas ce fichier, il a bougé » ; celui-ci dit\n * « relis-le ». Les deux vont ensemble : refuser d'écrire protège le travail\n * de l'autre instance, mais tant que la mémoire n'est pas reprise, l'écriture\n * refusée l'est pour toujours et la modification de l'utilisateur se perd.\n */\n private fingerprint = '';\n private readonly disk = new StoreDiskCoordinator();\n private listeners: Set<ChangeListener> = new Set();\n\n /** Notified after every persisted mutation, for the cloud sync bridge. */\n onChange(fn: ChangeListener): () => void {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n\n private notify(): void {\n for (const fn of this.listeners) fn();\n }\n\n /**\n * Which vault each environment was read from, so a save puts it back where it\n * belongs even before its project has been loaded.\n */\n private roots: Map<string, string> = new Map();\n /** L'organisation d'où vient un environnement reçu du cloud — voir `vaultOf`. */\n private remoteOrgs: Map<string, string> = new Map();\n\n /**\n * Oublier ce qui a été lu, pour tout relire sous la clé courante.\n *\n * Appelé après l'adoption d'une clé d'organisation : le disque vient d'être\n * re-chiffré sous les pieds du store, si bien que sa mémoire décrit un état\n * qui n'existe plus. La laisser en place n'affichait pas seulement des\n * comptes périmés — la sauvegarde suivante réécrivait l'ancien chiffré\n * par-dessus le nouveau, et défaisait l'adoption.\n */\n resetCache(): void {\n this.environments.clear();\n this.diskEncryptedSlots.clear();\n this.unreadableSecrets.clear();\n this.roots.clear();\n this.remoteOrgs.clear();\n this.loaded = false;\n // Les clients abonnés tiennent l'ancienne liste : sans ce signal, l'écran\n // ne se corrigerait qu'au prochain appel qu'il ferait de lui-même.\n this.notify();\n }\n\n /**\n * Combien de secrets ce coffre garde chiffrés sans savoir les ouvrir, par\n * organisation (`null` pour la machine).\n *\n * Ils sont déjà préservés à l'écriture (`keepUnreadableSecrets`) ; ce qui\n * manquait, c'est de le dire. Un secret qui s'affiche vide et se sauvegarde\n * intact est rassurant sur le disque et déroutant à l'écran.\n */\n async unreadableByVault(): Promise<Map<string | null, number>> {\n await this.load();\n const out = new Map<string | null, number>();\n for (const [environmentId, failed] of this.unreadableSecrets) {\n if (!failed.size) continue;\n const environment = this.environments.get(environmentId);\n const vault = (environment ? this.vaultOf(environment) : undefined) ?? null;\n out.set(vault, (out.get(vault) ?? 0) + failed.size);\n }\n return out;\n }\n\n private getFilePath(root: string): string {\n return join(root, FILENAME);\n }\n\n /**\n * The vault an environment must be written to: its project's.\n *\n * An environment names its project by id alone, and that id can answer for a\n * project in each organization. The vault it was read from names the couple —\n * so it is handed over as the tie-breaker rather than left as a mere fallback.\n *\n * Falls back to where it was last read, then to the local vault — an\n * environment whose project is unknown belongs to the launch directory, which\n * is where it lived before vaults existed.\n */\n /**\n * La clé sous laquelle chiffrer cet environnement.\n *\n * La provenance l'emporte sur le coffre tant que le projet n'est pas local :\n * un environnement reçu du cloud arrive avant lui, et `rootFor` désignerait\n * alors le répertoire de lancement — donc la clé de la machine plutôt que\n * celle de l'organisation qui a chiffré ces secrets.\n */\n private vaultOf(environment: Environment): string | undefined {\n return this.remoteOrgs.get(environment.id) ?? vaultIdOfRoot(this.rootFor(environment));\n }\n\n private rootFor(environment: Environment): string {\n const known = this.roots.get(environment.id) ?? null;\n const projectRoot = environment.projectId\n ? rootOfProjectAmong(environment.projectId, known)\n : null;\n return projectRoot ?? known ?? localVaultRoot();\n }\n\n /**\n * Le projet d'un environnement, organisation comprise.\n *\n * L'environnement ne nomme que l'id ; le coffre d'où il vient fournit\n * l'organisation, et c'est ce couple qu'il faut pour agir sur son projet.\n */\n projectRefOf(environment: Environment): ProjectRef | null {\n if (!environment.projectId) return null;\n return refOfProjectAmong(environment.projectId, this.roots.get(environment.id) ?? null);\n }\n\n /**\n * The vault holding the environment that defines a variable.\n *\n * Overrides answer a variable, not a project: this is what lets them find the\n * same vault without re-resolving an id that may be ambiguous.\n */\n async rootOfVariable(variableId: string): Promise<string | null> {\n await this.load();\n for (const environment of this.environments.values()) {\n if (environment.variables && variableId in environment.variables) {\n return this.rootFor(environment);\n }\n }\n return null;\n }\n\n private extractEncryptedCache(environment: Environment): DiskEncryptedCache {\n const envSlots = extractEncryptedVariableSlots(\n environment.variables as Record<string, { value: VariableSlot }>,\n );\n return { envSlots };\n }\n\n /**\n * `vault` est fourni quand l'appelant sait à qui appartient l'environnement\n * mieux que le disque : à l'arrivée d'une entité distante, son projet n'est\n * pas encore local, et le coffre déduit serait celui du répertoire de\n * lancement — donc la mauvaise clé.\n */\n private encryptEnvironment(environment: Environment, vault?: string): Environment {\n const cache = this.diskEncryptedSlots.get(environment.id);\n\n // Encrypt env variables\n let encryptedVars = environment.variables;\n if (environment.variables && Object.keys(environment.variables).length > 0) {\n encryptedVars = encryptVariableSlots(\n environment.variables as Record<string, { value: VariableSlot }>,\n cache?.envSlots,\n // La clé est celle du coffre où l'environnement sera écrit : les\n // secrets d'une organisation sont chiffrés par la sienne.\n vault ?? this.vaultOf(environment),\n ) as Environment['variables'];\n }\n\n return { ...environment, variables: this.keepUnreadableSecrets(environment.id, encryptedVars) };\n }\n\n /**\n * Réécrire un secret illisible tel qu'il est sur le disque, jamais à blanc.\n *\n * Un secret qui ne s'ouvre pas est vide en mémoire (voir\n * `decryptVariableSlots`) : le chiffrer donnerait un chiffré de la chaîne\n * vide, et la sauvegarde effacerait pour de bon une valeur dont il ne\n * manquait que la clé. Son chiffré d'origine est encore là — c'est celui que\n * `diskEncryptedSlots` a mis de côté avant le déchiffrement — et c'est lui\n * qui repart sur le disque.\n *\n * La condition tient à la valeur en mémoire : tant qu'elle est vide, la\n * variable n'a pas été retouchée et son chiffré fait foi. Dès que\n * l'utilisateur en saisit une nouvelle, elle l'emporte — et vider un secret\n * volontairement reste possible, puisque cela ne concerne que les variables\n * dont le déchiffrement a échoué.\n */\n private keepUnreadableSecrets(\n environmentId: string,\n variables: Environment['variables'],\n ): Environment['variables'] {\n const failed = this.unreadableSecrets.get(environmentId);\n if (!failed?.size || !variables) return variables;\n const onDisk = this.diskEncryptedSlots.get(environmentId)?.envSlots ?? {};\n const result = { ...variables };\n for (const variableId of failed) {\n const ciphertext = onDisk[variableId];\n const variable = result[variableId];\n if (!ciphertext || !variable || variable.value.value) continue;\n result[variableId] = { ...variable, value: { ...variable.value, value: ciphertext } };\n }\n return result;\n }\n\n private decryptEnvironment(\n environment: Environment,\n vault?: string,\n unreadable: Map<string, Set<string>> = this.unreadableSecrets,\n ): Environment {\n // Decrypt env variables\n let decryptedVars = environment.variables;\n if (environment.variables && Object.keys(environment.variables).length > 0) {\n const failed = new Set<string>();\n decryptedVars = decryptVariableSlots(\n environment.variables as Record<string, { value: VariableSlot }>,\n `environment ${environment.id}`,\n (variableId) => failed.add(variableId),\n vault ?? this.vaultOf(environment),\n ) as Environment['variables'];\n if (failed.size > 0) unreadable.set(environment.id, failed);\n else unreadable.delete(environment.id);\n }\n\n return { ...environment, variables: decryptedVars };\n }\n\n /** Ce que le disque porte : un fichier d'environnements par coffre. */\n private async diskFingerprint(): Promise<string> {\n return fingerprintFiles((await allVaultRoots()).map((root) => this.getFilePath(root)));\n }\n\n /**\n * Read every vault — the launch directory and each materialized cloud project.\n *\n * The roots come from the disk rather than from the project store, so loading\n * environments never has to wait on projects being loaded first (the project\n * store reads environments while absorbing a project, which would deadlock).\n */\n private async load(): Promise<void> {\n await this.disk.load(async () => {\n const fingerprint = await this.diskFingerprint();\n if (this.loaded && fingerprint === this.fingerprint) return null;\n // Les projets d'abord : tant qu'ils sont inconnus, chaque environnement\n // résoudrait vers le coffre de lancement et y serait réécrit à la première\n // sauvegarde — hors de son organisation.\n await ensureProjectsLoaded();\n // Locked: nothing can be decrypted, and marking the store loaded with\n // half the data would hide the rest until a restart.\n if (!getEncryptionKey()) return null;\n const next = await this.readVaults();\n return () => {\n this.environments = next.environments;\n this.diskEncryptedSlots = next.diskEncryptedSlots;\n this.roots = next.roots;\n this.unreadableSecrets = next.unreadableSecrets;\n this.fingerprint = fingerprint;\n this.loaded = true;\n };\n });\n }\n\n /** Lire tous les coffres dans des tables neuves, que `load` substitue d'un coup. */\n private async readVaults(): Promise<{\n environments: Map<string, Environment>;\n diskEncryptedSlots: Map<string, DiskEncryptedCache>;\n roots: Map<string, string>;\n unreadableSecrets: Map<string, Set<string>>;\n }> {\n const environments = new Map<string, Environment>();\n const diskEncryptedSlots = new Map<string, DiskEncryptedCache>();\n const roots = new Map<string, string>();\n const unreadableSecrets = new Map<string, Set<string>>();\n for (const root of await allVaultRoots()) {\n try {\n const raw = await readFile(this.getFilePath(root), 'utf-8');\n // L'empreinte du fichier lu : les chiffrés mis de côté ne valent que\n // tant qu'il n'a pas changé sous nos pieds.\n await rememberFile(this.getFilePath(root));\n const data: Environment[] = JSON.parse(raw);\n const clash = data.find((e) => isVaultClash(root, roots.get(e.id)));\n if (clash) {\n markVaultConflict(root, `environnement ${clash.id}`);\n continue;\n }\n for (const e of data) {\n // Cache encrypted slots before decrypting\n diskEncryptedSlots.set(e.id, this.extractEncryptedCache(e));\n // Le coffre d'abord, le déchiffrement ensuite, et par la provenance :\n // le même id de projet peut nommer un projet dans deux organisations,\n // et résoudre par lui désignait l'autre clé — tous les secrets du\n // coffre échouaient, alors qu'ils étaient intacts.\n roots.set(e.id, root);\n environments.set(e.id, this.decryptEnvironment(e, vaultIdOfRoot(root), unreadableSecrets));\n }\n } catch {\n // No environments in this vault yet.\n }\n }\n return { environments, diskEncryptedSlots, roots, unreadableSecrets };\n }\n\n /**\n * Write each vault that holds — or held — an environment.\n *\n * A vault emptied by this save loses its file: an environment that moved to\n * another vault must not survive in the old one, and a `[]` left behind would\n * keep (or recreate) a `.runeya` in a directory that holds nothing.\n */\n /**\n * Repérer les coffres réparés sous nos pieds, avant d'écrire.\n *\n * Le test est fait ici, une fois par sauvegarde, plutôt que par\n * environnement : c'est le fichier du coffre qui change, pas la variable.\n */\n private async markStaleRoots(): Promise<void> {\n this.staleRoots.clear();\n for (const root of new Set(this.roots.values())) {\n const path = this.getFilePath(root);\n if (wasRead(path) && await hasChangedSinceRead(path)) this.staleRoots.add(root);\n }\n }\n\n private save(): Promise<void> {\n return this.disk.save(() => this.writeVaults());\n }\n\n private async writeVaults(): Promise<void> {\n await this.markStaleRoots();\n const byRoot = new Map<string, Environment[]>();\n // Every vault seen so far starts empty, so one that lost its last\n // environment is rewritten rather than left behind — sauf un coffre en\n // conflit, dont le contenu n'est pas à nous.\n for (const root of this.roots.values()) {\n if (!isVaultConflicted(root)) byRoot.set(root, []);\n }\n byRoot.set(localVaultRoot(), byRoot.get(localVaultRoot()) ?? []);\n\n for (const environment of this.environments.values()) {\n const root = this.rootFor(environment);\n const prepared = this.encryptEnvironment(environment);\n // Update disk cache with newly written encrypted values\n this.diskEncryptedSlots.set(prepared.id, this.extractEncryptedCache(prepared));\n this.roots.set(environment.id, root);\n if (isVaultConflicted(root)) continue;\n const bucket = byRoot.get(root);\n if (bucket) bucket.push(prepared);\n else byRoot.set(root, [prepared]);\n }\n\n for (const [root, environments] of byRoot) {\n if (this.staleRoots.has(root)) {\n console.warn(\n `[environment-store] ${this.getFilePath(root)} a changé depuis sa lecture —`\n + ' écriture ignorée pour ne pas écraser une réparation faite en dehors du serveur.',\n );\n continue;\n }\n const filePath = this.getFilePath(root);\n if (environments.length === 0) {\n // Un coffre vidé perd son fichier plutôt que d'en garder une liste\n // vide. Écrire `[]` recréait un `.runeya` dans le dépôt de lancement\n // alors que tous les projets vivent désormais dans le cloud — un\n // fichier fantôme qui apparaissait dans le git status de l'utilisateur.\n await rm(filePath, { force: true }).catch(() => {});\n continue;\n }\n await atomicWriteSecure(filePath, JSON.stringify(environments, null, 2));\n // Notre propre écriture devient la nouvelle référence : sans ça, la\n // sauvegarde suivante prendrait ce fichier pour une réparation externe.\n await rememberFile(filePath);\n }\n\n // Nos propres écritures deviennent la référence : sans ça, le chargement\n // suivant les prendrait pour celles d'une autre instance et relirait tout.\n this.fingerprint = await this.diskFingerprint();\n\n this.notify();\n }\n\n /**\n * Rewrite every vault, moving entities that now belong elsewhere.\n *\n * Called once the project store knows where projects live: entities read from\n * the launch directory whose project turns out to be a cloud one are written\n * into that project's vault, and disappear from the directory they came from.\n */\n async relocateAll(): Promise<void> {\n await this.load();\n await this.save();\n }\n\n /**\n * Which project a variable belongs to, by walking the environments that\n * define it.\n *\n * Overrides are keyed by variable id alone — nothing in an override says\n * which project it belongs to — so this is what lets them be stored with\n * their project rather than in one machine-wide pile.\n */\n /**\n * Drop a project's environments from memory, without touching the disk.\n *\n * Used when a project stops being followed: its vault is removed as a whole,\n * and environments left in memory would be written back on the next save —\n * into the local vault, since their project is no longer known.\n */\n async forgetByProject(projectId: string): Promise<void> {\n await this.load();\n for (const [id, environment] of this.environments) {\n if (environment.projectId !== projectId) continue;\n this.environments.delete(id);\n this.diskEncryptedSlots.delete(id);\n this.unreadableSecrets.delete(id);\n this.roots.delete(id);\n this.remoteOrgs.delete(id);\n }\n this.notify();\n }\n\n /**\n * Les environnements d'un projet.\n *\n * `orgId` restreint au projet de cette organisation : deux projets homonymes\n * ont chacun les siens, et filtrer sur le seul `projectId` mélangeait les deux\n * listes à l'écran. Omis, le filtre reste sur l'id — ce que fait un appelant\n * qui n'a pas d'organisation à donner.\n */\n async list(projectId: string, orgId?: string | null): Promise<Environment[]> {\n await this.load();\n return this.ofProject(projectId, orgId);\n }\n\n /** Le filtre commun : l'id, puis le coffre quand l'organisation est connue. */\n private ofProject(projectId: string, orgId?: string | null): Environment[] {\n const byId = Array.from(this.environments.values()).filter(e => e.projectId === projectId);\n if (orgId === undefined) return byId;\n const root = vaultFor({ id: projectId, orgId }).root;\n return byId.filter(e => this.rootFor(e) === root);\n }\n\n async get(id: string): Promise<Environment | null> {\n await this.load();\n return this.environments.get(id) ?? null;\n }\n\n /**\n * `orgId` ne fait pas partie de l'environnement : il dit dans quel coffre le\n * poser. Un environnement neuf n'a pas encore de provenance, et son\n * `projectId` seul peut répondre pour un projet de chaque organisation — sans\n * cette précision, la création échouerait sur l'ambiguïté au lieu de viser le\n * bon coffre.\n */\n async create(input: CreateEnvironment, orgId?: string | null): Promise<Environment> {\n await this.load();\n const parsed = CreateEnvironmentSchema.parse(input);\n const environment: Environment = {\n id: randomUUID(),\n projectId: parsed.projectId,\n name: parsed.name,\n description: parsed.description,\n parentIds: parsed.parentIds,\n variables: parsed.variables,\n scope: parsed.scope,\n reference: parsed.reference,\n activateOn: parsed.activateOn,\n immutable: parsed.immutable,\n default: parsed.default,\n color: parsed.color,\n bgColor: parsed.bgColor,\n agentId: parsed.agentId,\n };\n // Exclusivity: if this env is default, unset default on all others in the same project\n if (environment.default) {\n for (const [id, e] of this.environments) {\n if (e.projectId === environment.projectId && e.default) {\n this.environments.set(id, { ...e, default: false });\n }\n }\n }\n this.environments.set(environment.id, environment);\n if (environment.projectId && orgId !== undefined) {\n this.roots.set(environment.id, vaultFor({ id: environment.projectId, orgId }).root);\n }\n await this.save();\n return environment;\n }\n\n async update(input: UpdateEnvironment): Promise<Environment | null> {\n await this.load();\n const existing = this.environments.get(input.id);\n if (!existing) return null;\n const updated: Environment = {\n ...existing,\n // Immutable environments: allow changing variables, color, description — but not name\n ...(!existing.immutable && input.name !== undefined && { name: input.name }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.parentIds !== undefined && { parentIds: input.parentIds }),\n ...(input.variables !== undefined && { variables: input.variables }),\n ...(input.scope !== undefined && { scope: input.scope }),\n ...(input.reference !== undefined && { reference: input.reference }),\n ...(input.activateOn !== undefined && { activateOn: input.activateOn }),\n ...(input.color !== undefined && { color: input.color }),\n ...(input.bgColor !== undefined && { bgColor: input.bgColor }),\n ...(input.default !== undefined && { default: input.default }),\n ...(input.agentId !== undefined && { agentId: input.agentId ?? undefined }),\n };\n // Exclusivity: if setting default: true, unset default on all others in the same project\n if (updated.default) {\n for (const [id, e] of this.environments) {\n if (id !== input.id && e.projectId === updated.projectId && e.default) {\n this.environments.set(id, { ...e, default: false });\n }\n }\n }\n this.environments.set(input.id, updated);\n await this.save();\n return updated;\n }\n\n async upsertVariable(environmentId: string, variableId: string, variable: Environment['variables'][string]): Promise<Environment | null> {\n const existing = this.environments.get(environmentId);\n if (!existing) return null;\n const variables = { ...existing.variables, [variableId]: variable };\n return this.update({ id: environmentId, variables });\n }\n\n async removeVariable(environmentId: string, variableId: string): Promise<Environment | null> {\n const existing = this.environments.get(environmentId);\n if (!existing) return null;\n const variables = { ...existing.variables };\n delete variables[variableId];\n return this.update({ id: environmentId, variables });\n }\n\n async delete(id: string): Promise<boolean> {\n await this.load();\n const env = this.environments.get(id);\n if (!env) return false;\n if (env.immutable) return false; // Can't delete immutable environments\n this.environments.delete(id);\n recordLocalRemoval('environments', id);\n await this.save();\n return true;\n }\n\n async getByProjectId(projectId: string, orgId?: string | null): Promise<Environment[]> {\n await this.load();\n return this.ofProject(projectId, orgId);\n }\n\n async listAllGlobal(): Promise<Environment[]> {\n await this.load();\n return Array.from(this.environments.values()).filter(e => e.scope === 'global');\n }\n\n // --- Scope-based Query Methods ---\n\n async listByScope(\n projectId: string, scope: 'global' | 'service', orgId?: string | null,\n ): Promise<Environment[]> {\n await this.load();\n return this.ofProject(projectId, orgId).filter(e => e.scope === scope);\n }\n\n /** Les environnements de service qui s'activent avec un environnement global. */\n async listByActivateOn(globalEnvId: string): Promise<Environment[]> {\n await this.load();\n return Array.from(this.environments.values())\n .filter(e => e.scope === 'service' && e.activateOn === globalEnvId);\n }\n\n async findServiceEnvironment(serviceId: string, globalEnvId: string): Promise<Environment | null> {\n await this.load();\n for (const e of this.environments.values()) {\n if (e.scope === 'service' && e.reference === serviceId && e.activateOn === globalEnvId) return e;\n }\n return null;\n }\n\n async findServiceEnvironments(serviceId: string): Promise<Environment[]> {\n await this.load();\n return Array.from(this.environments.values()).filter(e => e.scope === 'service' && e.reference === serviceId);\n }\n\n async deleteByReference(serviceId: string): Promise<void> {\n await this.load();\n let changed = false;\n for (const [id, e] of this.environments) {\n if (e.scope === 'service' && e.reference === serviceId) {\n this.environments.delete(id);\n recordLocalRemoval('environments', id);\n changed = true;\n }\n }\n if (changed) await this.save();\n }\n\n async deleteByActivateOn(globalEnvId: string): Promise<void> {\n await this.load();\n let changed = false;\n for (const [id, e] of this.environments) {\n if (e.scope === 'service' && e.activateOn === globalEnvId) {\n this.environments.delete(id);\n recordLocalRemoval('environments', id);\n changed = true;\n }\n }\n if (changed) await this.save();\n }\n\n /**\n * When a service is renamed, update the name of all its service-scoped environments\n * to keep the `${serviceName} (${globalEnvName})` pattern in sync.\n */\n async renameByReference(serviceId: string, newServiceName: string): Promise<void> {\n await this.load();\n let changed = false;\n for (const [id, e] of this.environments) {\n if (e.scope === 'service' && e.reference === serviceId && e.activateOn) {\n const globalEnv = this.environments.get(e.activateOn);\n if (!globalEnv) continue;\n this.environments.set(id, { ...e, name: `${newServiceName} (${globalEnv.name})` });\n changed = true;\n }\n }\n if (changed) await this.save();\n }\n\n /**\n * When a global environment is renamed, update the name of all service-scoped\n * environments that activateOn it to keep the naming pattern in sync.\n * Caller provides a serviceId→name map since environmentStore can't access serviceStore.\n */\n async renameByActivateOn(globalEnvId: string, newGlobalName: string, serviceNames: Map<string, string>): Promise<void> {\n await this.load();\n let changed = false;\n for (const [id, e] of this.environments) {\n if (e.scope === 'service' && e.activateOn === globalEnvId && e.reference) {\n const serviceName = serviceNames.get(e.reference);\n if (!serviceName) continue;\n this.environments.set(id, { ...e, name: `${serviceName} (${newGlobalName})` });\n changed = true;\n }\n }\n if (changed) await this.save();\n }\n\n // ---- Cloud sync ------------------------------------------------------\n\n /** Environments of the given projects, in their on-disk (encrypted) form. */\n async listEncryptedByProjectIds(projectIds: string[]): Promise<Environment[]> {\n await this.load();\n const wanted = new Set(projectIds);\n return Array.from(this.environments.values())\n .filter((e) => wanted.has(e.projectId))\n .map((e) => this.encryptEnvironment(e));\n }\n\n /**\n * Apply an environment received from the cloud, in its on-disk form.\n *\n * Validated rather than trusted, and diff-guarded against the on-disk form so\n * re-applying an identical environment neither saves nor notifies (loop-safe).\n */\n async applyRemoteUpsert(id: string, fields: unknown, vault?: string): Promise<{ created: boolean }> {\n await this.load();\n // Voir `vaultOf` : la sauvegarde qui suit doit chiffrer sous la clé qui\n // vient d'ouvrir, pas sous celle du coffre de repli.\n if (vault) this.remoteOrgs.set(id, vault);\n const parsed = EnvironmentSchema.safeParse({ ...(fields as object), id });\n if (!parsed.success) {\n console.error('[environment-store] Rejected remote environment %s:', id, parsed.error.message);\n return { created: false };\n }\n const incoming = parsed.data;\n const existing = this.environments.get(id);\n if (existing && JSON.stringify(this.encryptEnvironment(existing, vault)) === JSON.stringify(incoming)) {\n return { created: false };\n }\n this.diskEncryptedSlots.set(id, this.extractEncryptedCache(incoming));\n // The doc carries ciphertext for secret slots; in memory they are expected\n // in the clear, as `load()` keeps them.\n this.environments.set(id, this.decryptEnvironment(incoming, vault));\n await this.save();\n return { created: !existing };\n }\n\n /** Delete an environment that was removed on another peer. */\n async applyRemoteDelete(id: string): Promise<boolean> {\n await this.load();\n if (!this.environments.has(id)) return false;\n this.environments.delete(id);\n this.diskEncryptedSlots.delete(id);\n this.unreadableSecrets.delete(id);\n this.remoteOrgs.delete(id);\n await this.save();\n return true;\n }\n}\n\nexport const environmentStore = new EnvironmentStore();\n","import { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport type { VariableSlot } from '@runeya/packages-shared';\n\nconst ALGORITHM = 'aes-256-gcm';\n\n/**\n * Encrypt under a caller-supplied key.\n *\n * The no-key forms below read the process keyring, which is the right default\n * everywhere except migration: converting a vault means holding two keys at\n * once, and neither of them is \"the\" current one.\n */\nexport function encryptValueWith(key: Buffer, plaintext: string): string {\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n let enc = cipher.update(plaintext, 'utf-8', 'hex');\n enc += cipher.final('hex');\n const authTag = cipher.getAuthTag().toString('hex');\n return `${iv.toString('hex')}:${authTag}:${enc}`;\n}\n\n/** Decrypt under a caller-supplied key. Throws when the key does not open it. */\nexport function decryptValueWith(key: Buffer, encrypted: string): string {\n const parts = encrypted.split(':');\n if (parts.length !== 3) throw new Error('Invalid encrypted value format — expected iv:authTag:ciphertext');\n const [ivHex, authTagHex, ciphertext] = parts;\n if (!/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex) || (ciphertext !== '' && !/^[0-9a-f]+$/i.test(ciphertext))) {\n throw new Error('Invalid hex in encrypted value — possible data corruption');\n }\n const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(ivHex, 'hex'), { authTagLength: 16 });\n decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));\n let dec = decipher.update(ciphertext, 'hex', 'utf-8');\n dec += decipher.final('utf-8');\n return dec;\n}\n\n/**\n * `vault` désigne la clé à employer : l'identifiant d'une organisation, ou\n * l'absence pour la clé machine.\n *\n * Une donnée est chiffrée par la clé du coffre où elle est écrite, jamais par\n * « la » clé du processus : c'est ce qui permet à une organisation d'avoir la\n * sienne sans que le poste entier doive la partager. Les appelants qui ne\n * connaissent qu'un coffre gardent la forme sans argument.\n */\nexport function encryptValue(plaintext: string, vault?: string): string {\n const maybeKey = getEncryptionKey(vault);\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey;\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n let enc = cipher.update(plaintext, 'utf-8', 'hex');\n enc += cipher.final('hex');\n const authTag = cipher.getAuthTag().toString('hex');\n return `${iv.toString('hex')}:${authTag}:${enc}`;\n}\n\nexport function decryptValue(encrypted: string, vault?: string): string {\n const parts = encrypted.split(':');\n if (parts.length !== 3) throw new Error('Invalid encrypted value format — expected iv:authTag:ciphertext');\n const [ivHex, authTagHex, ciphertext] = parts;\n if (!/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex) || (ciphertext !== '' && !/^[0-9a-f]+$/i.test(ciphertext))) {\n throw new Error('Invalid hex in encrypted value — possible data corruption');\n }\n const maybeKey = getEncryptionKey(vault);\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey;\n const iv = Buffer.from(ivHex, 'hex');\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: 16 });\n decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));\n let dec = decipher.update(ciphertext, 'hex', 'utf-8');\n dec += decipher.final('utf-8');\n return dec;\n}\n\n/** Reuse existing ciphertext if plaintext is unchanged — keeps diffs stable */\nexport function stableEncryptValue(plaintext: string, existingEncrypted?: string, vault?: string): string {\n if (existingEncrypted) {\n try {\n if (decryptValue(existingEncrypted, vault) === plaintext) return existingEncrypted;\n } catch {\n // Existing value is corrupt — fall through to re-encrypt\n }\n }\n return encryptValue(plaintext, vault);\n}\n\ntype HasValueSlot = { value: VariableSlot; [k: string]: unknown };\n\n/** Encrypt secret Variable.value slots (environment variables — {value: VariableSlot}) */\nexport function encryptVariableSlots(\n variables: Record<string, HasValueSlot>,\n existingEncrypted?: Record<string, string>,\n vault?: string,\n): Record<string, HasValueSlot> {\n const result: Record<string, HasValueSlot> = {};\n for (const [key, variable] of Object.entries(variables)) {\n const slot = variable.value;\n if (slot.isSecret && slot.value) {\n result[key] = { ...variable, value: { ...slot, value: stableEncryptValue(slot.value, existingEncrypted?.[key], vault) } };\n } else {\n result[key] = { ...variable, value: { ...slot } };\n }\n }\n return result;\n}\n\n/**\n * Decrypt secret Variable.value slots (environment variables).\n *\n * A value that does not open becomes a blank *in memory* — nothing downstream\n * can be handed a ciphertext as if it were a secret. But a blank in memory is\n * one save away from a blank on disk, which destroys the only copy of a secret\n * whose key is merely absent (a vault read under another key, a key not loaded\n * yet). `onFailure` is how the caller learns which slots must keep their\n * on-disk form no matter what memory says.\n */\nexport function decryptVariableSlots(\n variables: Record<string, HasValueSlot>,\n contextLabel: string,\n onFailure?: (key: string) => void,\n vault?: string,\n): Record<string, HasValueSlot> {\n const result: Record<string, HasValueSlot> = {};\n for (const [key, variable] of Object.entries(variables)) {\n const slot = variable.value;\n if (slot.isSecret && slot.value) {\n try {\n result[key] = { ...variable, value: { ...slot, value: decryptValue(slot.value, vault) } };\n } catch (err) {\n console.error(`[crypto-helpers] Failed to decrypt variable \"${key}\" for ${contextLabel}: ${(err as Error).message} — la valeur chiffrée est conservée telle quelle sur le disque`);\n onFailure?.(key);\n result[key] = { ...variable, value: { ...slot, value: '' } };\n }\n } else {\n result[key] = { ...variable, value: { ...slot } };\n }\n }\n return result;\n}\n\n/** Extract encrypted slot values from Variable records (for stable-encrypt cache) */\nexport function extractEncryptedVariableSlots(\n variables: Record<string, HasValueSlot>,\n): Record<string, string> {\n const slots: Record<string, string> = {};\n for (const [key, variable] of Object.entries(variables)) {\n if (variable.value?.isSecret && variable.value?.value) {\n slots[key] = variable.value.value;\n }\n }\n return slots;\n}\n\n/**\n * Returns true if `s` looks like a value encrypted by `encryptValue()`.\n * Format: `<32hex>:<32hex>:<Nhex>` (iv:authTag:ciphertext).\n */\nexport function isEncryptedValue(s: string): boolean {\n const parts = s.split(':');\n return parts.length === 3 && /^[0-9a-f]{32}$/i.test(parts[0]) && /^[0-9a-f]{32}$/i.test(parts[1]);\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { watch } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { env } from '../config/env.js';\nimport { PASSPHRASE_TIMEOUT } from '@runeya/packages-shared';\nimport { agentManager } from './agent-manager.js';\nimport { setInstanceAgentPid } from './instance-registry.js';\nimport { deriveStableSecret } from '../utils/encryption-key.js';\n\n/**\n * Environment variables inherited by the spawned agent, per platform.\n * Only pass system-essential and explicitly needed variables —\n * never spread all of process.env to avoid leaking secrets.\n */\nconst POSIX_SAFE_KEYS = [\n 'PATH', 'HOME', 'USER', 'SHELL', 'LANG', 'TERM', 'NODE_ENV', 'TMPDIR', 'TZ',\n 'DBUS_SESSION_BUS_ADDRESS', 'XDG_RUNTIME_DIR',\n // Needed so the agent can talk to the user's clipboard for the chat image-paste flow\n // (xclip needs DISPLAY/XAUTHORITY, wl-copy needs WAYLAND_DISPLAY).\n 'DISPLAY', 'XAUTHORITY', 'WAYLAND_DISPLAY', 'XDG_SESSION_TYPE',\n];\n\n/**\n * Windows needs its whole native environment, not a POSIX subset. Without\n * PATHEXT a shell cannot resolve `git` to `git.exe`; without SystemRoot/windir\n * half of the OS fails to load; without APPDATA/USERPROFILE git finds neither\n * .gitconfig nor its credential helper. Keep this list broad — these are all\n * OS-provided variables, none of them carry user secrets.\n */\nconst WINDOWS_SAFE_KEYS = [\n 'PATH', 'PATHEXT', 'NODE_ENV', 'TZ', 'TERM',\n // Interpreter + OS roots\n 'ComSpec', 'SystemRoot', 'SystemDrive', 'windir', 'OS', 'PSModulePath',\n // User profile & app data (git config, credential helpers, npm/scoop shims)\n 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH', 'USERNAME', 'USERDOMAIN',\n 'USERDOMAIN_ROAMINGPROFILE', 'LOGONSERVER', 'APPDATA', 'LOCALAPPDATA',\n // Machine-wide install roots\n 'ProgramData', 'ProgramFiles', 'ProgramFiles(x86)', 'ProgramW6432',\n 'CommonProgramFiles', 'CommonProgramFiles(x86)', 'CommonProgramW6432',\n 'ALLUSERSPROFILE', 'PUBLIC', 'DriverData',\n // Temp\n 'TEMP', 'TMP',\n // Architecture / CPU (used by installers and native module loaders)\n 'PROCESSOR_ARCHITECTURE', 'PROCESSOR_ARCHITEW6432', 'PROCESSOR_IDENTIFIER',\n 'PROCESSOR_LEVEL', 'PROCESSOR_REVISION', 'NUMBER_OF_PROCESSORS',\n // Session / terminal\n 'SESSIONNAME', 'COMPUTERNAME', 'FPS_BROWSER_APP_PROFILE_STRING',\n 'FPS_BROWSER_USER_PROFILE_STRING', 'WT_SESSION', 'WT_PROFILE_ID',\n];\n\n/**\n * Build the environment for the spawned agent process.\n *\n * Keys are copied with the casing they have in `process.env`: Windows exposes\n * `Path`, not `PATH`, and Node's case-insensitive lookup would otherwise let us\n * write a second `PATH` key alongside it — the child would then carry both and\n * the OS would honour only one of them.\n *\n * Returns null if encryption key is not available (setup mode).\n */\nfunction buildAgentEnv(): Record<string, string> | null {\n const safeKeys = process.platform === 'win32' ? WINDOWS_SAFE_KEYS : POSIX_SAFE_KEYS;\n const agentEnv: Record<string, string> = {\n RUNEYA_AGENT_PORT: String(env.RUNEYA_AGENT_PORT),\n RUNEYA_AGENT_HOST: env.RUNEYA_AGENT_HOST,\n };\n\n // In local-simple mode, use a stable passphrase derived from encryption key\n // (allows committing agents.json without constant changes on restart)\n if (env.MODE === 'local-simple') {\n const passphrase = deriveStableSecret('local-agent-passphrase');\n if (!passphrase) {\n // Encryption key not available — cannot spawn agent\n return null;\n }\n agentEnv.PASSPHRASE_OVERRIDE = passphrase;\n }\n\n const wanted = new Set(safeKeys.map(k => k.toLowerCase()));\n for (const [key, value] of Object.entries(process.env)) {\n if (value && wanted.has(key.toLowerCase())) {\n agentEnv[key] = value;\n }\n }\n\n return agentEnv;\n}\n\n/**\n * La ligne par laquelle l'agent transmet sa passphrase.\n *\n * Tolère ce qui la précède : un agent d'une autre version peut horodater sa\n * sortie, et un motif ancré en début de ligne ne la reconnaîtrait plus — le\n * serveur attendrait alors son délai entier avant de renoncer, agent pourtant\n * parfaitement démarré. Ce qui suit reste strict : 64 caractères hexadécimaux,\n * puis la fin de ligne.\n */\n/**\n * Relaie une ligne de l'agent sans la dater une seconde fois.\n *\n * L'agent horodate déjà sa sortie, et c'est son temps à lui qui compte pour\n * lire son démarrage. Passer par `console` y ajouterait celui du serveur, et\n * chaque ligne porterait deux compteurs.\n */\nfunction forwardAgentLine(line: string): void {\n process.stdout.write(`[agent] ${line}\\n`);\n}\n\nconst PASSPHRASE_LINE = /(?:^|\\s)RUNEYA_PASSPHRASE=([a-f0-9]{64})$/;\n\nconst LOCAL_AGENT_ID = 'local-agent';\nconst LOCAL_AGENT_NAME = 'local';\n\nclass AgentSpawner {\n private agentProcess: ChildProcess | null = null;\n private exitHandlerRegistered = false;\n private fileWatcher: ReturnType<typeof watch> | null = null;\n private isRestarting = false;\n private agentDistPath: string | null = null;\n\n async spawnLocalAgent(): Promise<void> {\n // Échappatoire pour les bancs d'essai : un serveur qui ne pilote aucun\n // process n'a pas besoin de payer le démarrage d'un second process Node.\n // Sans agent local, seuls les agents distants déclarés répondent.\n if (process.env.RUNEYA_DISABLE_LOCAL_AGENT === '1') {\n console.log('[agent-spawner] RUNEYA_DISABLE_LOCAL_AGENT=1 — aucun agent local ne sera démarré');\n return;\n }\n\n const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;\n\n // Check if encryption key is available\n const agentEnv = buildAgentEnv();\n if (!agentEnv) {\n console.log('[agent-spawner] ⚠️ Cannot spawn local agent: encryption key not available');\n console.log('[agent-spawner] Please provide the encryption key via the setup UI');\n return;\n }\n\n console.log(`[agent-spawner] Spawning local agent on ${agentUrl}...`);\n\n const isDev = process.env.NODE_ENV === 'development';\n // Use AGENT_BINARY_PATH if provided (e.g., from CLI), otherwise resolve\n // relative to this file's location — process.cwd() is unreliable because\n // the server chdir's into the user's stack root when DATA_DIR is set.\n this.agentDistPath = env.AGENT_BINARY_PATH\n || join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'agent', 'dist', 'index.js');\n\n // Always spawn from dist/index.js (built by tsup watch in dev mode)\n this.agentProcess = spawn('node', [this.agentDistPath], {\n env: agentEnv,\n stdio: ['ignore', 'pipe', 'pipe'],\n // Sans ceci, une fenêtre de commande s'ouvre sous Windows dès que ce\n // serveur n'a pas de console à prêter — ce qui est le cas après un\n // redémarrage détaché (diagnostics, mise à jour) : Windows alloue alors\n // une console AU FILS, et une console allouée est une fenêtre visible.\n windowsHide: true,\n });\n\n // Publié au registre pour qu'une autre instance sache quoi arrêter : tuer\n // ce serveur ne réclame pas l'agent, qui n'est pas dans son groupe de\n // processus — et sur Windows il n'y a pas de groupe du tout.\n void setInstanceAgentPid(this.agentProcess.pid ?? null);\n\n if (isDev) {\n // In dev mode, watch dist/index.js and restart agent when it changes\n // (tsup --watch rebuilds this file when src/ changes)\n this.setupFileWatcher();\n }\n\n // Safety net: kill agent synchronously when server process exits for any reason\n // (e.g. Ctrl+C via Turbo/tsup where async shutdown may not complete)\n if (!this.exitHandlerRegistered) {\n this.exitHandlerRegistered = true;\n process.on('exit', () => {\n if (this.agentProcess && !this.agentProcess.killed) {\n this.agentProcess.kill('SIGKILL');\n }\n });\n }\n\n // Parse passphrase from stdout\n const passphrase = await this.waitForPassphrase();\n if (!passphrase) {\n console.error('[agent-spawner] Failed to get passphrase from local agent');\n return;\n }\n\n console.log('[agent-spawner] Local agent started, passphrase received');\n\n // Register the local agent (in-memory only — no persistence to agents.json)\n await agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase, passthroughEnv: [] });\n console.log(`[agent-spawner] Local agent registered as ${LOCAL_AGENT_ID}`);\n }\n\n private waitForPassphrase(): Promise<string | null> {\n return new Promise((resolve) => {\n if (!this.agentProcess?.stdout) {\n resolve(null);\n return;\n }\n\n const rl = createInterface({ input: this.agentProcess.stdout });\n const timeout = setTimeout(() => {\n resolve(null);\n }, PASSPHRASE_TIMEOUT);\n\n let initialPassphraseFound = false;\n\n rl.on('line', (line) => {\n const match = PASSPHRASE_LINE.exec(line);\n if (match) {\n const newPassphrase = match[1];\n\n if (!initialPassphraseFound) {\n // First passphrase - resolve the promise to complete initial spawn\n initialPassphraseFound = true;\n clearTimeout(timeout);\n resolve(newPassphrase);\n } else {\n // Agent reloaded (tsx watch) - update the passphrase\n console.log('[agent-spawner] 🔄 Agent reloaded, updating passphrase...');\n this.updateAgentPassphrase(newPassphrase).catch((err: Error) => {\n console.error('[agent-spawner] Failed to update passphrase after reload:', err);\n });\n }\n // Don't print the passphrase line\n return;\n }\n // Print all non-passphrase agent stdout\n forwardAgentLine(line);\n });\n\n // Forward stderr permanently\n if (this.agentProcess?.stderr) {\n const stderrRl = createInterface({ input: this.agentProcess.stderr });\n stderrRl.on('line', (line) => {\n console.error(`[agent:err] ${line}`);\n });\n }\n\n this.agentProcess?.on('error', (err) => {\n console.error('[agent-spawner] Failed to spawn agent:', err);\n clearTimeout(timeout);\n if (!initialPassphraseFound) resolve(null);\n });\n\n this.agentProcess?.on('exit', (code) => {\n if (code !== null && code !== 0) {\n console.error(`[agent-spawner] Agent exited with code ${code}`);\n }\n clearTimeout(timeout);\n if (!initialPassphraseFound) resolve(null);\n });\n });\n }\n\n async shutdown(): Promise<void> {\n if (this.fileWatcher) {\n this.fileWatcher.close();\n this.fileWatcher = null;\n }\n\n if (this.agentProcess) {\n console.log('[agent-spawner] Stopping local agent...');\n // SIGKILL for immediate port release — the local agent is stateless\n // so graceful shutdown is unnecessary.\n this.agentProcess.kill('SIGKILL');\n\n await new Promise<void>((resolve) => {\n this.agentProcess?.on('exit', () => resolve());\n // Safety timeout in case 'exit' never fires\n setTimeout(resolve, 2_000);\n });\n\n this.agentProcess = null;\n void setInstanceAgentPid(null);\n }\n }\n\n getLocalAgentId(): string | null {\n return LOCAL_AGENT_ID;\n }\n\n private async updateAgentPassphrase(newPassphrase: string): Promise<void> {\n const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;\n await agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase: newPassphrase, passthroughEnv: [] });\n console.log(`[agent-spawner] ✅ Agent passphrase updated, re-registered as ${LOCAL_AGENT_ID}`);\n }\n\n private setupFileWatcher(): void {\n if (!this.agentDistPath) return;\n\n // Close previous watcher if it exists (important for hot reload to keep working)\n if (this.fileWatcher) {\n this.fileWatcher.close();\n }\n\n console.log('[agent-spawner] 🔍 Watching agent dist for changes (hot reload enabled)');\n\n // Watch the dist/ directory instead of the file itself — tsup \"cleans\" the folder\n // (delete + recreate) on every rebuild, which causes fs.watch on a file to lose\n // its inode and stop firing. Watching the directory captures rename/add events too.\n const distDir = join(this.agentDistPath, '..');\n this.fileWatcher = watch(distDir, { persistent: false }, (eventType, filename) => {\n if ((eventType === 'change' || eventType === 'rename') && filename === 'index.js' && !this.isRestarting) {\n this.isRestarting = true;\n console.log('[agent-spawner] 🔄 Agent code changed, restarting...');\n\n // In dev mode, kill immediately for fast hot reload (no graceful shutdown needed)\n if (this.agentProcess && !this.agentProcess.killed) {\n const oldProcess = this.agentProcess;\n\n // Wait for process to fully exit before restarting\n oldProcess.once('exit', () => {\n // Give OS a moment to release the port\n setTimeout(() => {\n this.restartAgent().catch((err: Error) => {\n console.error('[agent-spawner] Failed to restart agent:', err);\n this.isRestarting = false;\n });\n }, 100);\n });\n\n // SIGKILL immediately for instant hot reload\n oldProcess.kill('SIGKILL');\n }\n }\n });\n this.fileWatcher.on('error', (err: NodeJS.ErrnoException) => {\n console.error('[agent-spawner] File watcher error:', err.message);\n });\n }\n\n private async restartAgent(): Promise<void> {\n if (!this.agentDistPath) return;\n\n const agentEnv = buildAgentEnv();\n if (!agentEnv) {\n console.log('[agent-spawner] Cannot restart agent: encryption key not available');\n this.isRestarting = false;\n return;\n }\n\n const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;\n const isDev = process.env.NODE_ENV === 'development';\n\n // Spawn new agent process\n this.agentProcess = spawn('node', [this.agentDistPath], {\n env: agentEnv,\n stdio: ['ignore', 'pipe', 'pipe'],\n // Sans ceci, une fenêtre de commande s'ouvre sous Windows dès que ce\n // serveur n'a pas de console à prêter — ce qui est le cas après un\n // redémarrage détaché (diagnostics, mise à jour) : Windows alloue alors\n // une console AU FILS, et une console allouée est une fenêtre visible.\n windowsHide: true,\n });\n\n // Setup stdout/stderr forwarding and passphrase detection\n if (this.agentProcess?.stdout) {\n const rl = createInterface({ input: this.agentProcess.stdout });\n rl.on('line', (line) => {\n const match = PASSPHRASE_LINE.exec(line);\n if (match) {\n const newPassphrase = match[1];\n // Update agent registration with new passphrase (in-memory only)\n agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase: newPassphrase, passthroughEnv: [] })\n .then(() => {\n console.log(`[agent-spawner] ✅ Agent restarted and re-registered as ${LOCAL_AGENT_ID}`);\n this.isRestarting = false;\n\n // Recreate file watcher for next hot reload (fs.watch can lose tracking after file replace)\n if (isDev) {\n this.setupFileWatcher();\n }\n })\n .catch((err: Error) => {\n console.error('[agent-spawner] Failed to register agent after restart:', err);\n this.isRestarting = false;\n });\n return;\n }\n forwardAgentLine(line);\n });\n }\n\n if (this.agentProcess?.stderr) {\n const stderrRl = createInterface({ input: this.agentProcess.stderr });\n stderrRl.on('line', (line) => {\n console.error(`[agent:err] ${line}`);\n });\n }\n }\n}\n\nexport const agentSpawner = new AgentSpawner();\n","import { mkdir, readdir, readFile, writeFile, rm } from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport dotenv from 'dotenv';\nimport { spawn } from 'node:child_process';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport { env } from '../config/env.js';\nimport { machineRoot } from './vault-resolver.js';\n\n/**\n * Registry of the Runeya instances running on this machine.\n *\n * Each instance drops a small file named after its pid and removes it on the\n * way out. Discovery by scanning the process table was the alternative, and it\n * is worse on every axis: `ps` output differs per platform, an instance run\n * through a wrapper is invisible, and the command line does not carry the data\n * that actually matters here — which port answers, which directory it serves.\n *\n * The file is a claim, not proof. Anything that crashed or was SIGKILLed\n * leaves its file behind, so every record is checked twice before it is shown:\n * the process must still exist, and its port must answer.\n *\n * Both checks are needed, and neither is enough alone. Asking only the port\n * confirms that *a* Runeya answers there, not that it is this one — restart an\n * instance on the same port and the dead record inherits the live one's answer,\n * uptime included. Asking only the pid misses a process that is alive but no\n * longer serving.\n */\n\nconst InstanceFileSchema = z.object({\n pid: z.number().int().positive(),\n /**\n * The agent this server spawned, when it has one.\n *\n * Optional because an instance started before this field existed never wrote\n * it, and because the agent may not be up yet — or at all, when the\n * encryption key is missing. Absent means « unknown », never « none ».\n */\n agentPid: z.number().int().positive().optional(),\n port: z.number().int().min(1).max(65535),\n agentPort: z.number().int().min(1).max(65535),\n cwd: z.string().max(4096),\n dataDir: z.string().max(4096),\n version: z.string().max(64),\n fromDesktop: z.boolean(),\n startedAt: z.number().int().nonnegative(),\n /**\n * De quoi relancer ce lancement à l'identique : l'exécutable, ses arguments,\n * et les `RUNEYA_*` de son environnement. Le `cwd` ci-dessus complète le\n * tableau.\n *\n * Optionnels, comme `agentPid` : une instance antérieure à ce champ ne les\n * écrit pas. Elle reste listée et arrêtable, simplement pas redémarrable —\n * un redémarrage approximatif serait pire qu'un bouton absent.\n */\n execPath: z.string().max(4096).optional(),\n argv: z.array(z.string().max(4096)).max(128).optional(),\n env: z.record(z.string().max(64), z.string().max(4096)).optional(),\n /**\n * L'instance tient un secret (`RUNEYA_ENCRYPTION_KEY`,\n * `RUNEYA_MASTER_PASSWORD`) que le relancement ne retrouverait pas. Le\n * drapeau, jamais la valeur : le registre est un fichier lisible, et ces\n * deux-là n'ont délibérément pas de flag CLI parce qu'`argv` se lit dans\n * `ps`.\n *\n * Un secret posé dans le `.env` de la pile ne compte pas : `dotenv` le relit\n * au démarrage depuis le même répertoire. Voir `secretsAreReproducible()`.\n */\n needsSecretEnv: z.boolean().optional(),\n});\n\nexport type InstanceFile = z.infer<typeof InstanceFileSchema>;\n\nexport type DetectedInstance = InstanceFile & {\n /** The port answered a health check just now. */\n reachable: boolean;\n /** This very process. */\n current: boolean;\n /** Uptime reported by the instance itself, in milliseconds — health.check's unit. */\n uptimeMs: number | null;\n /** Le lancement est reproductible : redémarrage possible. */\n restartable: boolean;\n /** Pourquoi il ne l'est pas, quand il ne l'est pas. */\n restartBlockedBy: RestartBlocker | null;\n};\n\n/**\n * - `legacy` : lancée avant que le registre ne publie de quoi la relancer.\n * - `secrets` : son environnement portait un secret que le registre n'a pas le\n * droit de conserver ; le lancement ne peut pas être reproduit à l'identique.\n *\n * Être l'instance courante n'en fait pas partie : elle se redémarre par un\n * autre chemin (voir `scheduleSelfRestart`), mais elle se redémarre.\n */\nexport type RestartBlocker = 'legacy' | 'secrets';\n\nfunction registryDir(): string {\n // Machine-level, not project-level: the point is to see every Runeya on the\n // box, including those serving other directories. `machineRoot()` reads the\n // environment on each call, which is what lets the test suite redirect it.\n return join(machineRoot(), 'instances');\n}\n\nfunction fileFor(pid: number): string {\n return join(registryDir(), `${pid}.json`);\n}\n\n/**\n * Ce que cette instance a publié. Gardé en mémoire pour que la mise à jour du\n * pid de l'agent n'ait pas à relire — et donc à faire confiance à — son propre\n * fichier, qu'un tiers pourrait avoir réécrit.\n */\nlet published: InstanceFile | null = null;\n\n/**\n * Les variables que le registre ne recopie jamais. Voir `needsSecretEnv`.\n */\nconst SECRET_ENV_KEYS = ['RUNEYA_ENCRYPTION_KEY', 'RUNEYA_MASTER_PASSWORD'] as const;\n\nfunction isSecretEnvKey(key: string): boolean {\n return (SECRET_ENV_KEYS as readonly string[]).includes(key);\n}\n\n/**\n * Les `RUNEYA_*` de cet environnement, secrets exclus.\n *\n * Seul ce préfixe est retenu : tout ce que Runeya lit le porte (cf. `env.ts`),\n * et recopier l'environnement entier reviendrait à figer dans un fichier le\n * `.env` de la pile gérée, jetons compris.\n */\nfunction runeyaEnv(): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith('RUNEYA_') || isSecretEnvKey(key)) continue;\n if (typeof value === 'string' && value.length <= 4096) out[key] = value;\n }\n return out;\n}\n\n/**\n * Un secret présent dans l'environnement empêche-t-il de rejouer ce lancement ?\n *\n * Pas toujours, et la nuance est celle qui décide si le bouton est cliquable.\n * `RUNEYA_MASTER_PASSWORD` posé dans le `.env` de la pile n'est pas hérité du\n * shell : `dotenv` le relit au démarrage, depuis ce même répertoire. Le\n * relancer le retrouve donc tout seul, sans que le registre ait eu à le copier.\n *\n * Ce qui n'est pas reproductible, c'est le secret venu d'ailleurs — exporté\n * dans le shell, injecté par un CI. La comparaison porte sur la valeur, et pas\n * seulement sur la présence de la clé : `dotenv` n'écrase pas une variable déjà\n * définie, donc une clé présente des deux côtés avec deux valeurs différentes\n * signifie que c'est celle du shell qui a gagné.\n */\nfunction secretsAreReproducible(): boolean {\n const present = SECRET_ENV_KEYS.filter((key) => Boolean(process.env[key]));\n if (present.length === 0) return true;\n let fromFile: Record<string, string>;\n try {\n fromFile = dotenv.parse(readFileSync(join(process.cwd(), '.env')));\n } catch {\n // Pas de `.env` lisible : le secret ne peut venir que de l'environnement.\n return false;\n }\n return present.every((key) => fromFile[key] === process.env[key]);\n}\n\nfunction selfRecord(): InstanceFile {\n return {\n pid: process.pid,\n port: env.PORT,\n agentPort: env.RUNEYA_AGENT_PORT,\n cwd: process.cwd(),\n dataDir: env.DATA_DIR,\n version: process.env['RUNEYA_VERSION'] ?? 'unknown',\n fromDesktop: env.FROM_DESKTOP,\n startedAt: Date.now(),\n execPath: process.execPath,\n // argv[0] est l'exécutable, déjà repris par `execPath` ; le reste est le\n // script suivi de ses arguments, c'est-à-dire ce qu'il faut repasser à spawn.\n argv: process.argv.slice(1),\n env: runeyaEnv(),\n needsSecretEnv: !secretsAreReproducible(),\n };\n}\n\n/**\n * Announce this instance, and arrange for the file to go away.\n *\n * `exit` handlers must be synchronous, and unlinking there is unreliable\n * anyway, so the file is treated as disposable: a stale one is detected by the\n * health probe rather than prevented.\n */\nexport async function registerInstance(): Promise<void> {\n try {\n await mkdir(registryDir(), { recursive: true });\n published = selfRecord();\n await writeFile(fileFor(process.pid), JSON.stringify(published, null, 2));\n } catch (err) {\n // A machine whose home is read-only still runs Runeya fine; it just does\n // not show up in the listing.\n console.log('[instances] Could not register this instance:', (err as Error).message);\n }\n}\n\nexport async function unregisterInstance(): Promise<void> {\n await rm(fileFor(process.pid), { force: true }).catch(() => { /* best effort */ });\n}\n\n/**\n * Publier le pid de l'agent local, ou l'effacer quand il n'y en a plus.\n *\n * Sans lui, arrêter une instance depuis une autre laisserait son agent orphelin\n * sur Windows, où tuer un parent ne réclame pas ses enfants. En développement\n * l'agent redémarre à chaque rebuild avec un pid neuf, d'où un appel à chaque\n * spawn plutôt qu'un seul au démarrage.\n */\nexport async function setInstanceAgentPid(agentPid: number | null): Promise<void> {\n if (!published) return;\n published = { ...published, agentPid: agentPid ?? undefined };\n try {\n await writeFile(fileFor(process.pid), JSON.stringify(published, null, 2));\n } catch {\n // Une instance qui ne peut pas réécrire son fichier reste utilisable ;\n // seule l'extinction à distance de son agent en pâtit.\n }\n}\n\n/**\n * Whether the process still exists. Signal 0 performs the permission and\n * existence checks without delivering anything; EPERM means it exists but\n * belongs to another user, which still counts as alive.\n */\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\n/** Ask a port whether a Runeya answers there. */\nasync function probe(port: number): Promise<{ ok: boolean; uptimeMs: number | null; version: string | null }> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/trpc/health.check`, {\n signal: AbortSignal.timeout(1500),\n });\n if (!res.ok) return { ok: false, uptimeMs: null, version: null };\n const body = await res.json() as { result?: { data?: { uptime?: number; version?: string } } };\n const data = body.result?.data;\n // health.check reports `Date.now() - startedAt`, i.e. milliseconds.\n return { ok: true, uptimeMs: data?.uptime ?? null, version: data?.version ?? null };\n } catch {\n return { ok: false, uptimeMs: null, version: null };\n }\n}\n\n/**\n * List the instances this machine knows about.\n *\n * Unreachable entries are dropped *and* their file removed: a listing that\n * keeps showing a Runeya that died last week is worse than one that shows\n * nothing, because it invites the user to go looking for it.\n */\n/**\n * Ce qu'on peut dire d'une instance à partir de son seul pid.\n *\n * Assez pour la NOMMER — c'est son port qu'on cherche, pas son dossier : c'est\n * par là qu'on l'ouvre, et c'est ce qui la distingue de ses voisines pour qui\n * la regarde. Volontairement sans sonde réseau, contrairement à `listInstances` :\n * l'appelant a déjà, par ailleurs, une raison de croire ce pid vivant.\n *\n * `null` quand le pid n'a rien publié : une instance antérieure au registre, ou\n * un tiers. Le dire vaut mieux qu'inventer.\n */\nexport async function describeInstance(pid: number): Promise<{ pid: number; port: number; cwd: string } | null> {\n try {\n const parsed = InstanceFileSchema.safeParse(JSON.parse(await readFile(fileFor(pid), 'utf8')));\n if (!parsed.success) return null;\n return { pid, port: parsed.data.port, cwd: parsed.data.cwd };\n } catch {\n return null;\n }\n}\n\nexport async function listInstances(): Promise<DetectedInstance[]> {\n let files: string[];\n try {\n files = await readdir(registryDir());\n } catch {\n return [];\n }\n\n const found = await Promise.all(\n files\n .filter((f) => f.endsWith('.json'))\n .map(async (name): Promise<DetectedInstance | null> => {\n const path = join(registryDir(), name);\n let record: InstanceFile;\n try {\n const parsed = InstanceFileSchema.safeParse(JSON.parse(await readFile(path, 'utf8')));\n if (!parsed.success) {\n await rm(path, { force: true }).catch(() => { /* best effort */ });\n return null;\n }\n record = parsed.data;\n } catch {\n return null;\n }\n\n // Cheapest check first, and the one that catches the common case: an\n // instance killed or crashed never removes its own file.\n if (!isProcessAlive(record.pid)) {\n await rm(path, { force: true }).catch(() => { /* best effort */ });\n return null;\n }\n\n const { ok, uptimeMs, version } = await probe(record.port);\n if (!ok) {\n await rm(path, { force: true }).catch(() => { /* best effort */ });\n return null;\n }\n\n const current = record.pid === process.pid;\n const blocker = restartBlockerFor(record);\n return {\n ...record,\n // Trust the live answer over the file: the file was written at boot\n // and an upgraded instance would keep claiming its old version.\n version: version ?? record.version,\n reachable: true,\n current,\n uptimeMs,\n restartable: blocker === null,\n restartBlockedBy: blocker,\n };\n }),\n );\n\n return dedupeByPort(found.filter((i): i is DetectedInstance => i !== null))\n .sort((a, b) => Number(b.current) - Number(a.current) || a.port - b.port);\n}\n\n/**\n * One port, one instance.\n *\n * Two live records can still claim the same port when a pid has been recycled\n * onto an unrelated process — the file then passes both checks while its\n * Runeya is long gone. Only one of them can actually be listening, and it is\n * the one that started last, so that is the one kept.\n */\nfunction dedupeByPort(instances: DetectedInstance[]): DetectedInstance[] {\n const byPort = new Map<number, DetectedInstance>();\n for (const instance of instances) {\n const kept = byPort.get(instance.port);\n // The current process is authoritative about its own port.\n if (!kept || instance.current || (!kept.current && instance.startedAt > kept.startedAt)) {\n byPort.set(instance.port, instance);\n }\n }\n return [...byPort.values()];\n}\n\n/**\n * Ce qui empêche de relancer ce lancement à l'identique, s'il y a lieu.\n *\n * Ne dit rien de *comment* on relance : l'instance courante passe par un\n * relanceur détaché plutôt que par un arrêt suivi d'un spawn, mais les deux\n * chemins exigent le même lancement reproductible.\n */\nfunction restartBlockerFor(record: InstanceFile): RestartBlocker | null {\n if (record.needsSecretEnv) return 'secrets';\n if (!record.execPath || !record.argv) return 'legacy';\n return null;\n}\n\n/**\n * Arrêter une instance Runeya de cette machine, son agent d'abord.\n *\n * L'ordre n'est pas cosmétique. L'agent est un processus distinct, lancé sans\n * groupe de processus : sur Windows, tuer le serveur ne réclame pas ses\n * enfants, et l'agent survivrait en tenant son port. On le termine donc\n * explicitement, puis le serveur.\n *\n * Le pid visé ne vient jamais de l'appelant : il est cherché dans la liste que\n * `listInstances()` vient de valider — le processus existe, et son port répond\n * bien à `health.check`. Un pid quelconque, recyclé ou inventé, n'y figure pas\n * et n'est donc jamais signalé.\n *\n * Tout passe par `process.kill`, que Node porte sur les trois systèmes ; aucune\n * commande externe (`kill`, `pkill`, `taskkill`) n'est appelée. Sur Windows les\n * signaux n'existent pas et la terminaison est immédiate : le SIGTERM y est\n * déjà brutal, et l'escalade qui suit devient sans objet.\n */\nexport type KillOutcome = 'terminated' | 'killed' | 'absent' | 'denied' | 'failed';\n\nexport interface KillInstanceResult {\n agent: KillOutcome;\n server: KillOutcome;\n}\n\n/** Laissé à un processus pour sortir de lui-même avant l'escalade. */\nconst GRACE_MS = 5_000;\nconst POLL_MS = 100;\n\nasync function terminate(pid: number): Promise<KillOutcome> {\n if (!isProcessAlive(pid)) return 'absent';\n try {\n process.kill(pid, 'SIGTERM');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ESRCH') return 'absent';\n if (code === 'EPERM') return 'denied';\n return 'failed';\n }\n\n const deadline = Date.now() + GRACE_MS;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n if (!isProcessAlive(pid)) return 'terminated';\n }\n\n // Le délai de grâce a passé : le processus est bloqué, ou ignore SIGTERM.\n try {\n process.kill(pid, 'SIGKILL');\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === 'ESRCH' ? 'terminated' : 'failed';\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n return isProcessAlive(pid) ? 'failed' : 'killed';\n}\n\nexport async function killInstance(pid: number): Promise<KillInstanceResult> {\n const target = (await listInstances()).find((i) => i.pid === pid);\n if (!target) throw new Error('instance-not-found');\n // Se tuer soi-même couperait la réponse en cours d'envoi : la page n'aurait\n // aucun moyen de distinguer l'extinction réussie de la requête perdue.\n if (target.current) throw new Error('instance-is-current');\n\n const agent = target.agentPid ? await terminate(target.agentPid) : 'absent';\n const server = await terminate(pid);\n\n // Le fichier du registre survit à un SIGKILL, qui ne laisse rien se\n // désinscrire. Le retirer ici évite d'attendre le prochain balayage.\n if (server === 'terminated' || server === 'killed') {\n await rm(fileFor(pid), { force: true }).catch(() => { /* best effort */ });\n }\n return { agent, server };\n}\n\n/**\n * Redémarrer une instance : l'arrêter, attendre que son port se libère, puis\n * relancer exactement le même lancement.\n *\n * « Exactement » est la seule promesse qui vaille ici. Le registre publie\n * l'exécutable, les arguments, le répertoire de lancement et les `RUNEYA_*` de\n * l'environnement ; ce qu'il ne peut pas publier — les deux secrets — bloque le\n * redémarrage plutôt que de le rendre approximatif. Une instance relancée sans\n * sa clé démarrerait sans agent, et le bouton aurait menti.\n *\n * Le processus est détaché, et ce n'est pas cosmétique : sans `detached`, la\n * ressuscitée devient fille de l'instance qui a cliqué et mourrait avec elle.\n * Ses flux sont ignorés pour la même raison — un pipe non lu finit par bloquer\n * l'écrivain, et personne n'est là pour le vider.\n *\n * L'attente sur le port n'est pas non plus une précaution de confort : relancer\n * trop tôt, c'est soit `EADDRINUSE`, soit — pire — un glissement silencieux vers\n * un autre port par `resolvePort`, et ce n'est alors plus le même lancement.\n */\n/** Un lancement prêt à être rejoué, tel que le registre l'a publié. */\ninterface Launch {\n execPath: string;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n}\n\n/**\n * Reconstituer le lancement d'une instance.\n *\n * L'environnement du fichier fait autorité, pas celui de cette instance : les\n * `RUNEYA_*` d'ici décrivent *ce* serveur, et les laisser passer relancerait la\n * voisine avec nos ports. Les secrets d'ici sont retirés pour la même raison —\n * la cible n'en avait pas, sinon elle serait bloquée. Le reste de\n * l'environnement (PATH, HOME…) est celui de la machine et se transmet tel quel.\n */\nfunction launchOf(record: InstanceFile): Launch {\n if (!record.execPath || !record.argv) throw new Error('instance-not-restartable:legacy');\n const env: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (typeof value === 'string' && !key.startsWith('RUNEYA_')) env[key] = value;\n }\n return {\n execPath: record.execPath,\n argv: record.argv,\n cwd: record.cwd,\n env: { ...env, ...record.env },\n };\n}\n\nexport interface RestartInstanceResult {\n stop: KillInstanceResult;\n /** Le pid du processus relancé. */\n pid: number;\n /** Le port a bien été rendu avant le relancement. */\n portReleased: boolean;\n}\n\n/** Au-delà, le port n'est visiblement pas près d'être rendu. */\nconst PORT_RELEASE_TIMEOUT_MS = 15_000;\n\nasync function waitForPortRelease(port: number): Promise<boolean> {\n const deadline = Date.now() + PORT_RELEASE_TIMEOUT_MS;\n while (Date.now() < deadline) {\n const { ok } = await probe(port);\n if (!ok) return true;\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n }\n return false;\n}\n\nexport async function restartInstance(pid: number): Promise<RestartInstanceResult> {\n const target = (await listInstances()).find((i) => i.pid === pid);\n if (!target) throw new Error('instance-not-found');\n if (target.restartBlockedBy) throw new Error(`instance-not-restartable:${target.restartBlockedBy}`);\n // Une instance ne s'arrête pas elle-même pour se relancer : elle passe par\n // `scheduleSelfRestart`, qui répond d'abord et pose un relanceur.\n if (target.current) throw new Error('instance-is-current');\n\n const stop = await killInstance(pid);\n if (stop.server !== 'terminated' && stop.server !== 'killed') {\n throw new Error(`instance-stop-${stop.server}`);\n }\n\n const portReleased = await waitForPortRelease(target.port);\n\n const launch = launchOf(target);\n const child = spawn(launch.execPath, launch.argv, {\n cwd: launch.cwd,\n env: launch.env,\n detached: true,\n stdio: 'ignore',\n // `detached` demande à Windows une console neuve, et une console neuve est\n // une fenêtre de commande à l'écran. Le lanceur de bureau cache la sienne :\n // sans ceci, relancer une instance la ferait réapparaître.\n windowsHide: true,\n });\n // Un `spawn` échoue de manière asynchrone (répertoire disparu, exécutable\n // déplacé depuis le lancement). Sans écouteur, l'erreur remonterait en\n // exception non capturée et tuerait *cette* instance — celle qui rendait\n // service.\n child.on('error', (err) => {\n console.log(`[instances] Relance du pid ${pid} impossible :`, err.message);\n });\n child.unref();\n\n if (typeof child.pid !== 'number') throw new Error('instance-restart-failed');\n return { stop, pid: child.pid, portReleased };\n}\n\n/**\n * Le relanceur : un processus de rien du tout, dont le seul travail est\n * d'attendre que le port soit rendu, puis de rejouer le lancement.\n *\n * Il existe parce qu'une instance ne peut pas se relancer elle-même sans\n * course. Lancer le successeur avant de mourir, c'est lui offrir un port encore\n * tenu — `resolvePort` le fait alors glisser ailleurs en silence, et le\n * lancement n'est plus le même, ce qui est précisément la promesse à tenir ici.\n * Mourir avant de lancer, c'est n'avoir plus personne pour lancer.\n *\n * Sa charge passe par une variable d'environnement, jamais par `argv` : c'est\n * la même raison qui prive les deux secrets de flag CLI — `ps` lit les\n * arguments de tout le monde.\n */\nconst RELAUNCHER = `\n const p = JSON.parse(process.env.RUNEYA_RELAUNCH_PAYLOAD);\n const deadline = Date.now() + p.timeoutMs;\n const held = async () => {\n try {\n const res = await fetch('http://127.0.0.1:' + p.port + '/api/trpc/health.check', {\n signal: AbortSignal.timeout(500),\n });\n return res.ok;\n } catch { return false; }\n };\n (async () => {\n while (Date.now() < deadline && await held()) {\n await new Promise((r) => setTimeout(r, ${POLL_MS}));\n }\n require('node:child_process')\n .spawn(p.execPath, p.argv, { cwd: p.cwd, env: p.env, detached: true, stdio: 'ignore', windowsHide: true })\n .unref();\n })();\n`;\n\n/**\n * Se redémarrer soi-même : poster un relanceur, puis s'arrêter proprement.\n *\n * L'arrêt est différé d'un battement, et c'est tout l'intérêt de séparer cette\n * fonction de `restartInstance` : la réponse tRPC doit avoir quitté la socket\n * avant que le processus ne parte, faute de quoi la page ne saurait pas\n * distinguer un redémarrage réussi d'une requête perdue.\n *\n * On se signale, on ne se tue pas : `SIGTERM` passe par l'arrêt gracieux\n * habituel — agent terminé, fichier de registre retiré — que `process.exit()`\n * court-circuiterait.\n */\nexport interface SelfRestartResult {\n /** Le relanceur, pas le futur serveur : son pid n'est pas celui qui reviendra. */\n relauncherPid: number;\n /** Port sur lequel l'instance est attendue de retour. */\n port: number;\n}\n\n/** Délai laissé à la réponse pour partir avant que le processus ne s'arrête. */\nconst SELF_RESTART_DELAY_MS = 250;\n\n/**\n * Poster le relanceur d'un lancement donné, sans rien arrêter.\n *\n * Séparé de `scheduleSelfRestart` pour être vérifiable : le relanceur est la\n * pièce délicate — il attend un port et relance — et un test ne peut pas\n * s'arrêter lui-même pour l'éprouver.\n */\nexport function spawnRelauncher(record: InstanceFile): number {\n const blocker = restartBlockerFor(record);\n if (blocker) throw new Error(`instance-not-restartable:${blocker}`);\n\n const launch = launchOf(record);\n const relauncher = spawn(process.execPath, ['-e', RELAUNCHER], {\n cwd: launch.cwd,\n // Une console neuve serait une fenêtre de commande à l'écran, le temps que\n // le relanceur attende la libération du port. Voir `restartInstance`.\n windowsHide: true,\n env: {\n ...launch.env,\n RUNEYA_RELAUNCH_PAYLOAD: JSON.stringify({ ...launch, port: record.port, timeoutMs: PORT_RELEASE_TIMEOUT_MS }),\n },\n detached: true,\n stdio: 'ignore',\n });\n relauncher.on('error', (err) => {\n console.log('[instances] Relanceur impossible à lancer :', err.message);\n });\n relauncher.unref();\n if (typeof relauncher.pid !== 'number') throw new Error('instance-restart-failed');\n return relauncher.pid;\n}\n\nexport async function scheduleSelfRestart(): Promise<SelfRestartResult> {\n const record = published ?? selfRecord();\n const relauncherPid = spawnRelauncher(record);\n\n setTimeout(() => {\n console.log('[instances] Redémarrage demandé : arrêt de cette instance.');\n process.kill(process.pid, 'SIGTERM');\n }, SELF_RESTART_DELAY_MS);\n\n return { relauncherPid, port: record.port };\n}\n","import { stableEncryptValue, decryptValue, isEncryptedValue } from './crypto-helpers.js';\n\nclass TraefikManager {\n encryptDnsCredentials(credentials: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(credentials)) {\n result[key] = isEncryptedValue(value) ? value : stableEncryptValue(value);\n }\n return result;\n }\n\n decryptDnsCredentials(credentials: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(credentials)) {\n if (isEncryptedValue(value)) {\n try {\n result[key] = decryptValue(value);\n } catch {\n console.error(`[traefik-manager] Failed to decrypt credential: ${key}`);\n result[key] = '';\n }\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n}\n\nexport const traefikManager = new TraefikManager();\n","import { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport { WS_RECONNECT_MIN, WS_RECONNECT_MAX } from '@runeya/packages-shared';\n\n/**\n * tRPC WS JSON-RPC protocol message types:\n * → { id, method: \"subscription\", params: { path, input } }\n * ← { id, result: { type: \"started\" } }\n * ← { id, result: { type: \"data\", data } }\n * ← { id, result: { type: \"stopped\" } }\n */\n\ninterface TRPCRequest {\n id: number;\n method: 'subscription';\n params: { path: string; input?: unknown };\n}\n\ninterface TRPCResponse {\n id: number;\n result?: {\n type: 'started' | 'data' | 'stopped';\n data?: unknown;\n };\n error?: unknown;\n}\n\ninterface SubscriptionInfo {\n eventType?: string; // e.g. 'process:status' — for static fan-out subscriptions\n onData?: (data: unknown) => void; // for dynamic per-session subscriptions\n onStop?: () => void;\n}\n\nconst SUBSCRIPTION_PATHS: Array<{ path: string; eventType: string }> = [\n { path: 'process.onStatus', eventType: 'process:status' },\n { path: 'process.onLog', eventType: 'process:log' },\n { path: 'process.onHealth', eventType: 'process:health' },\n { path: 'process.onMetrics', eventType: 'process:metrics' },\n];\n\n/**\n * AgentWSBridge opens a WebSocket connection to an agent's tRPC endpoint\n * and subscribes to all process-related subscriptions using the raw\n * tRPC JSON-RPC protocol. Events are emitted with the agentId attached.\n */\nconst PING_INTERVAL_MS = 30_000;\nconst PONG_TIMEOUT_MS = 10_000;\n\nexport class AgentWSBridge extends EventEmitter {\n private ws: WebSocket | null = null;\n private nextId = 1;\n private subscriptions: Map<number, SubscriptionInfo> = new Map();\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private currentReconnectDelay = WS_RECONNECT_MIN;\n private closed = false;\n private pingInterval: ReturnType<typeof setInterval> | null = null;\n private pongTimeout: ReturnType<typeof setTimeout> | null = null;\n private connectionErrorLogged = false;\n\n constructor(\n private agentId: string,\n private url: string,\n private passphrase: string,\n ) {\n super();\n }\n\n connect(): void {\n if (this.closed) return;\n this.cleanup();\n\n const httpUrl = new URL(this.url);\n const wsProtocol = httpUrl.protocol === 'https:' ? 'wss:' : 'ws:';\n // tRPC WS endpoint is at the root (default upgrade path, not /ws/health)\n const wsUrl = `${wsProtocol}//${httpUrl.host}`;\n\n try {\n this.ws = new WebSocket(wsUrl, {\n headers: { 'Authorization': `Bearer ${this.passphrase}` },\n });\n } catch {\n this.scheduleReconnect();\n return;\n }\n\n this.ws.on('open', () => {\n console.log(`[agent-ws-bridge] ✅ Connected to agent ${this.agentId}`);\n this.currentReconnectDelay = WS_RECONNECT_MIN;\n this.connectionErrorLogged = false;\n this.startHeartbeat();\n this.subscribeAll();\n console.log(`[agent-ws-bridge] Created ${SUBSCRIPTION_PATHS.length} subscriptions for agent ${this.agentId}`);\n });\n\n this.ws.on('message', (raw: Buffer | string) => {\n try {\n const msg: TRPCResponse = JSON.parse(\n typeof raw === 'string' ? raw : raw.toString(),\n );\n this.handleMessage(msg);\n } catch {\n // Ignore unparseable messages\n }\n });\n\n this.ws.on('pong', () => {\n if (this.pongTimeout) { clearTimeout(this.pongTimeout); this.pongTimeout = null; }\n });\n\n this.ws.on('close', () => {\n if (!this.connectionErrorLogged) {\n console.log(`[agent-ws-bridge] ❌ Disconnected from agent ${this.agentId}`);\n }\n this.stopHeartbeat();\n this.ws = null;\n this.subscriptions.clear();\n if (!this.closed) {\n if (!this.connectionErrorLogged) {\n console.log(`[agent-ws-bridge] Scheduling reconnect for agent ${this.agentId} in ${this.currentReconnectDelay}ms`);\n }\n this.scheduleReconnect();\n }\n });\n\n this.ws.on('error', (err) => {\n if (!this.connectionErrorLogged) {\n console.debug('[agent-ws-bridge] WS error for agent', this.agentId + ':', err.message);\n this.connectionErrorLogged = true;\n }\n // 'error' is always followed by 'close'\n });\n }\n\n /**\n * Subscribe to a dynamic tRPC path with an input payload.\n * Returns an unsubscribe function that cleans up on the agent side.\n * Used for per-session subscriptions (e.g. terminal.onData).\n */\n subscribeWithInput(\n path: string,\n input: unknown,\n onData: (data: unknown) => void,\n onStop?: () => void,\n ): () => void {\n const id = this.nextId++;\n this.subscriptions.set(id, { onData, onStop });\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n // Le pendant client de cette ligne vit dans le navigateur : les deux\n // horodatages posés côte à côte disent qui, du client ou du serveur, tarde.\n if (process.env['RUNEYA_DEBUG']) {\n console.log(`[agent-ws-bridge] → souscription ${path} demandée pour l'agent ${this.agentId}`);\n }\n const msg: TRPCRequest = { id, method: 'subscription', params: { path, input } };\n this.ws.send(JSON.stringify(msg));\n } else {\n // Enregistrée mais jamais envoyée : l'appelant attendra des données que\n // l'agent ne sait pas devoir produire. Muet jusqu'ici — c'est le genre\n // d'attente qu'on ne s'explique qu'en relisant ce fichier.\n console.warn(\n `[agent-ws-bridge] ⚠ souscription ${path} enregistrée SANS être envoyée `\n + `(WS non ouvert, état ${this.ws?.readyState ?? 'absent'}) — agent ${this.agentId}`,\n );\n }\n\n return () => {\n this.subscriptions.delete(id);\n if (this.ws?.readyState === WebSocket.OPEN) {\n // tRPC v11 stop message\n this.ws.send(JSON.stringify({ id, method: 'subscription.stop' }));\n }\n };\n }\n\n disconnect(): void {\n this.closed = true;\n this.cleanup();\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.pingInterval = setInterval(() => {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n this.pongTimeout = setTimeout(() => {\n console.log(`[agent-ws-bridge] Pong timeout for agent ${this.agentId}, closing connection`);\n this.ws?.terminate();\n }, PONG_TIMEOUT_MS);\n this.ws.ping();\n }, PING_INTERVAL_MS);\n }\n\n private stopHeartbeat(): void {\n if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; }\n if (this.pongTimeout) { clearTimeout(this.pongTimeout); this.pongTimeout = null; }\n }\n\n private cleanup(): void {\n this.stopHeartbeat();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n // Remove all user listeners, but keep a no-op error handler\n // to prevent unhandled error events when closing in CONNECTING state\n this.ws.removeAllListeners();\n this.ws.on('error', () => {});\n this.ws.close();\n this.ws = null;\n }\n this.subscriptions.clear();\n }\n\n private subscribeAll(): void {\n for (const { path, eventType } of SUBSCRIPTION_PATHS) {\n this.sendSubscription(path, eventType);\n }\n }\n\n private sendSubscription(path: string, eventType: string): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n\n const id = this.nextId++;\n this.subscriptions.set(id, { eventType });\n\n const msg: TRPCRequest = {\n id,\n method: 'subscription',\n params: { path },\n };\n\n this.ws.send(JSON.stringify(msg));\n }\n\n private handleMessage(msg: TRPCResponse): void {\n const sub = this.subscriptions.get(msg.id);\n if (!sub) return;\n\n if (msg.result?.type === 'data' && msg.result.data !== undefined) {\n const data = msg.result.data;\n\n if (sub.onData) {\n // Dynamic subscription — forward raw data to callback\n sub.onData(data);\n } else if (sub.eventType) {\n if (typeof data !== 'object' || data === null) return;\n // Log batches arrive as arrays — emit with agentId attached for filtering\n if (Array.isArray(data)) {\n this.emit(sub.eventType, { batch: data, agentId: this.agentId });\n } else {\n // Spread agent data first, then override agentId with the trusted value\n // to prevent a compromised agent from spoofing its identity\n this.emit(sub.eventType, {\n ...(data as Record<string, unknown>),\n agentId: this.agentId,\n });\n }\n }\n } else if (msg.result?.type === 'started') {\n const label = sub.eventType ?? '(dynamic)';\n console.log(`[agent-ws-bridge] ✅ Subscription ${label} started for agent ${this.agentId}`);\n } else if (msg.result?.type === 'stopped') {\n const label = sub.eventType ?? '(dynamic)';\n console.log(`[agent-ws-bridge] ⏹️ Subscription ${label} stopped for agent ${this.agentId}`);\n if (sub.onStop) sub.onStop();\n this.subscriptions.delete(msg.id);\n } else if (msg.error) {\n const label = sub.eventType ?? '(dynamic)';\n console.debug('[agent-ws-bridge] Subscription error for', label, 'on agent', this.agentId + ':', msg.error);\n if (sub.onStop) sub.onStop();\n this.subscriptions.delete(msg.id);\n }\n }\n\n private scheduleReconnect(): void {\n if (this.closed || this.reconnectTimer) return;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n if (!this.closed) {\n this.connect();\n }\n }, this.currentReconnectDelay);\n\n this.currentReconnectDelay = Math.min(this.currentReconnectDelay * 2, WS_RECONNECT_MAX);\n }\n}\n"],"mappings":"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","names":["EventEmitter","WebSocket","z","randomUUID","readFile","rm","join","ref","randomUUID","readFile","join","randomBytes","createCipheriv","createDecipheriv","ALGORITHM","randomBytes","createCipheriv","createDecipheriv","FILENAME","join","readFile","randomUUID","env","FILENAME","join","readFile","rm","randomUUID","spawn","dirname","join","mkdir","readFile","writeFile","rm","join","join","mkdir","writeFile","rm","join","readFile","rm","env","join","dirname","spawn","z","EventEmitter","conn","WebSocket","spawn"]}