@runeya/runeya 2.0.51 → 2.0.53

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 (316) hide show
  1. package/agent/index.js +58 -17
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-RGDHYM64.js +7 -0
  4. package/{chunk-NVKGIBJN.js → chunk-EL75GG7O.js} +338 -117
  5. package/chunk-EL75GG7O.js.map +1 -0
  6. package/index.js +1 -1
  7. package/package.json +1 -1
  8. package/{src-R6JOPDNL.js → src-4GK5OK3K.js} +1012 -372
  9. package/src-4GK5OK3K.js.map +1 -0
  10. package/web/assets/AgentScalarChatInterface.vue-Cz4cGFw_.js +13 -0
  11. package/web/assets/AgentTerminalPage-ChRcIGvk.js +1 -0
  12. package/web/assets/AiChatPage-jeAEz3pL.js +1 -0
  13. package/web/assets/AppLayout-C8-7N9YJ.js +1 -0
  14. package/web/assets/{AppLayout-D1bdzemo.css → AppLayout-CDh-jiJ3.css} +1 -1
  15. package/web/assets/AuditPage-DfJlEoaI.js +1 -0
  16. package/web/assets/CloudAccountPage--6fr9upU.js +1 -0
  17. package/web/assets/CloudAccountPage-CI6Vvfrv.js +1 -0
  18. package/web/assets/CloudAccountPage-CVAMkCOF.css +1 -0
  19. package/web/assets/CloudMessagingPage-CWz2XG-O.js +2 -0
  20. package/web/assets/DashboardPage-Bkla6ZI9.js +1 -0
  21. package/web/assets/{EditorIcon-dUXSIPv3.js → EditorIcon-BYTcFJnr.js} +2 -2
  22. package/web/assets/EnvironmentForm-Tody7LuY.js +108 -0
  23. package/web/assets/EnvironmentFormPage-r_Hn2SiC.js +1 -0
  24. package/web/assets/EnvironmentListPage-BPQL4Fdp.js +1 -0
  25. package/web/assets/EnvironmentSelector-CsXJXDJE.js +1 -0
  26. package/web/assets/KanbanBoardPage-C2Cj_u1W.js +4 -0
  27. package/web/assets/KanbanBoardPage-ZIYuV1kG.css +1 -0
  28. package/web/assets/KanbanListPage-DG04Ci-t.js +1 -0
  29. package/web/assets/LaunchProjectPage-ZU10tRlq.js +1 -0
  30. package/web/assets/LaunchServicesPage-rLishQca.js +1 -0
  31. package/web/assets/LogViewer-DkR3A0v0.js +3 -0
  32. package/web/assets/{ProcessControls-t9skQ_zd.js → ProcessControls-Dkf-zJF8.js} +1 -1
  33. package/web/assets/ProcessDetailPage-vDdHOtno.js +1 -0
  34. package/web/assets/{ProcessMetrics-DL00NyoX.js → ProcessMetrics-BxwLdytO.js} +2 -2
  35. package/web/assets/ProjectDetailPage-Bu2gDzDU.js +1 -0
  36. package/web/assets/ProjectListPage-zlXxla_K.js +1 -0
  37. package/web/assets/RandomJobTestPage-DkxLX-Uy.js +2 -0
  38. package/web/assets/ScenarioEditorPage-Dlgug19y.js +14 -0
  39. package/web/assets/SendToChatDialog-BbMjL5KU.js +1 -0
  40. package/web/assets/ServiceControlsBar-D9mA1wIv.js +1 -0
  41. package/web/assets/ServiceDetailPage-CPtHs7te.js +2 -0
  42. package/web/assets/ServiceFormPage-D6zoG4d2.js +1 -0
  43. package/web/assets/SettingsPage-BL1cbj_d.js +2062 -0
  44. package/web/assets/{SettingsPage-C9WtnczY.css → SettingsPage-BY3dG-wn.css} +1 -1
  45. package/web/assets/UnlockPage-tTNE4QTc.js +1 -0
  46. package/web/assets/VariablesTable-C8UT5LGj.js +1 -0
  47. package/web/assets/VaultMigrationPage-ee6mGXkE.js +1 -0
  48. package/web/assets/VersionBadge-XGdKeJYP.js +1 -0
  49. package/web/assets/WikiPage-DBxWCk1C.js +1 -0
  50. package/web/assets/WikiView-CtMG-Mpi.js +1 -0
  51. package/web/assets/{add-scalar-classes-CEf8wgbL.js → add-scalar-classes-CjP6yoH9.js} +26 -26
  52. package/web/assets/auth-errors-C7Lmr_5E.js +1 -0
  53. package/web/assets/autocomplete-DLtfuYAg.js +422 -0
  54. package/web/assets/{basic-shape-diamond.svg-BxBPwaOm.js → basic-shape-diamond.svg-Dwn3EJf7.js} +1 -1
  55. package/web/assets/{basic-shape-hexagon.svg-Du908vLS.js → basic-shape-hexagon.svg-kpOvfzAi.js} +1 -1
  56. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-CfFm5Pl7.js → basic-shape-primary-circle-ellipse-round.svg-CZpeeE8h.js} +1 -1
  57. package/web/assets/{basic-shape-primary-square-rectangle.svg-CP0kG7Ut.js → basic-shape-primary-square-rectangle.svg-lIzT-3bQ.js} +1 -1
  58. package/web/assets/{basic-shape-shield.svg-DxTsLdrS.js → basic-shape-shield.svg-DB-n6Wmn.js} +1 -1
  59. package/web/assets/{browser-DV9-oWjy.js → browser-Dm_lNzZ-.js} +1 -1
  60. package/web/assets/{browser-BvCyKum4.js → browser-wjfUiObI.js} +1 -1
  61. package/web/assets/card-C3zlSqpG.js +32 -0
  62. package/web/assets/{checkbox-D5bej0ao.js → checkbox-lVXx5U1s.js} +3 -3
  63. package/web/assets/{chip-D8vO8NID.js → chip-Cc40pDDT.js} +2 -2
  64. package/web/assets/client-CN9LxBfq.js +24 -0
  65. package/web/assets/cloud-chat.store-CKtcP8mx.js +1 -0
  66. package/web/assets/{cloud-session-tALRHewK.js → cloud-session-luM5H14C.js} +1 -1
  67. package/web/assets/column-DlQNKY_S.js +907 -0
  68. package/web/assets/{computer-device-desktop-monitor.svg-82Glaw7U.js → computer-device-desktop-monitor.svg-B8fVLOqV.js} +1 -1
  69. package/web/assets/{computer-device-desktop.svg-BZb-XFcK.js → computer-device-desktop.svg-CKeybOQk.js} +1 -1
  70. package/web/assets/{computer-device-laptop.svg-BnptbmCn.js → computer-device-laptop.svg-C5xV7_Nt.js} +1 -1
  71. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-g23ZYYHm.js → computer-device-mobile-phone-android-samsung-back.svg-5CRKvrQx.js} +1 -1
  72. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-B5fBZlIN.js → computer-device-mobile-phone-android-samsung.svg-Da4aZrXd.js} +1 -1
  73. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-BsKZAUH_.js → computer-device-mobile-phone-iphone-x-back.svg-D9phyiQZ.js} +1 -1
  74. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-GVv-OoXC.js → computer-device-mobile-phone-iphone-x.svg-V7yMTZCu.js} +1 -1
  75. package/web/assets/{computer-device-mobile-tablet-touch.svg-CpyRybCz.js → computer-device-mobile-tablet-touch.svg-CGMdXdex.js} +1 -1
  76. package/web/assets/{computer-device-mobile-tablet.svg-C_6zzbHC.js → computer-device-mobile-tablet.svg-66hlusLK.js} +1 -1
  77. package/web/assets/{computer-device-network-ethernet-cat6.svg-s_MRa0bJ.js → computer-device-network-ethernet-cat6.svg-BhuzmqfI.js} +1 -1
  78. package/web/assets/{computer-device-network-lan-www.svg-31TS3sqT.js → computer-device-network-lan-www.svg-BXLNIvQ6.js} +1 -1
  79. package/web/assets/{computer-device-network-wifi-connection.svg-BfBnT-iK.js → computer-device-network-wifi-connection.svg-CaVzq_6z.js} +1 -1
  80. package/web/assets/{computer-device-network-wifi-router.svg-BNQJ_FEI.js → computer-device-network-wifi-router.svg-DasSXsmT.js} +1 -1
  81. package/web/assets/{cssMode-IplHgA8x.js → cssMode-CokQPPhF.js} +1 -1
  82. package/web/assets/{divider-D-QC8_57.js → divider-3st3S0cz.js} +2 -2
  83. package/web/assets/{ecology-science-erlenmeyer-flask.svg-CKCEQFIq.js → ecology-science-erlenmeyer-flask.svg-0xfq6Yyi.js} +1 -1
  84. package/web/assets/{fieldset-DpQHSMKf.js → fieldset-CvZZBOcz.js} +2 -2
  85. package/web/assets/{freemarker2-DHUVsWtT.js → freemarker2-1YMoAedE.js} +1 -1
  86. package/web/assets/{handlebars-pE00Hjr_.js → handlebars-wAW8puhT.js} +1 -1
  87. package/web/assets/{html-DMtmtVNV.js → html-DdFlw76f.js} +1 -1
  88. package/web/assets/{htmlMode-DFeVLqUM.js → htmlMode-Cy1vm8Ag.js} +1 -1
  89. package/web/assets/{image-flash-lightning.svg-NAFI_e2J.js → image-flash-lightning.svg-COFAE0wR.js} +1 -1
  90. package/web/assets/{image-picture-flower.svg-B6ZUCTNS.js → image-picture-flower.svg-ezqcE4KS.js} +1 -1
  91. package/web/assets/{index-D7XqcLhE.css → index-CICxLkjp.css} +1 -1
  92. package/web/assets/{index-a1iwaPG1.js → index-Ccntdgfe.js} +290 -290
  93. package/web/assets/{interface-alert-exclamation-diamond.svg-CwHTxp3a.js → interface-alert-exclamation-diamond.svg-CHGW-cLp.js} +1 -1
  94. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-Bi-1vcWG.js → interface-alert-exclamation-triangle-warning.svg-DvPTCqs0.js} +1 -1
  95. package/web/assets/{interface-alert-information-circle.svg-CQnbLMfS.js → interface-alert-information-circle.svg-jX4BkfAJ.js} +1 -1
  96. package/web/assets/{interface-award-crown.svg-CFtMz4dE.js → interface-award-crown.svg-DtuQ-cID.js} +1 -1
  97. package/web/assets/{interface-bookmark.svg-7SV2pxyN.js → interface-bookmark-tag.svg-CAFVyAoV.js} +1 -1
  98. package/web/assets/{interface-bookmark-tag.svg-DumdjnW1.js → interface-bookmark.svg-BneowIGx.js} +1 -1
  99. package/web/assets/{interface-calendar-date-one.svg-e7-h1b3y.js → interface-calendar-date-one.svg-CfpOGCx4.js} +1 -1
  100. package/web/assets/{interface-content-book-open-pages.svg-DwLQV4lf.js → interface-content-book-open-pages.svg-Cl1dxQ4s.js} +1 -1
  101. package/web/assets/{interface-content-book-page.svg-CAKDQ2x-.js → interface-content-book-page.svg-CEhybPQh.js} +1 -1
  102. package/web/assets/{interface-content-file.svg-DZEwGNLB.js → interface-content-file.svg-HK08zzYO.js} +1 -1
  103. package/web/assets/interface-content-folder.svg-BD3Sy4hk.js +1 -0
  104. package/web/assets/{interface-content-folder.svg-BnFYmNMO.js → interface-content-folder.svg-DuncK7Kw.js} +1 -1
  105. package/web/assets/{interface-copy-clipboard.svg-9UwhLON-.js → interface-copy-clipboard.svg-CHxj1VUB.js} +1 -1
  106. package/web/assets/{interface-edit-attachment.svg-C1_u9TNx.js → interface-edit-attachment.svg-DQ-TGp2N.js} +1 -1
  107. package/web/assets/{interface-edit-binocular.svg-DoQnsqHf.js → interface-edit-binocular.svg-CIUJcI__.js} +1 -1
  108. package/web/assets/{interface-edit-magic-wand.svg-C7wTd6JH.js → interface-edit-magic-wand.svg-Bn7wsTVd.js} +1 -1
  109. package/web/assets/{interface-edit-tool-paint-roller.svg-BZ4hqe-1.js → interface-edit-tool-paint-roller.svg-BLR1HoWs.js} +1 -1
  110. package/web/assets/{interface-edit-tool-pencil.svg-D1bysygW.js → interface-edit-tool-pencil.svg-DM3xZUtj.js} +1 -1
  111. package/web/assets/{interface-favorite-award.svg-0Thu2k9M.js → interface-favorite-award.svg-Bc8_APGD.js} +1 -1
  112. package/web/assets/{interface-favorite-flag.svg-DI4_vrUQ.js → interface-favorite-flag.svg-BBV01eyw.js} +1 -1
  113. package/web/assets/{interface-favorite-heart.svg-DpwKtPuU.js → interface-favorite-heart.svg-DpFl3KSk.js} +1 -1
  114. package/web/assets/{interface-favorite-star.svg-D-udxHVN.js → interface-favorite-star.svg-CaFUQ9jK.js} +1 -1
  115. package/web/assets/{interface-favorite-stars-sparkles.svg-Dc4Z9Mjg.js → interface-favorite-stars-sparkles.svg-19cy1b4g.js} +1 -1
  116. package/web/assets/{interface-hierarchy-flowchart.svg-vfERX-oc.js → interface-hierarchy-flowchart.svg-Btiu5TuK.js} +1 -1
  117. package/web/assets/{interface-home-house.svg-BY98XY-a.js → interface-home-house.svg-Bp4aVUgs.js} +1 -1
  118. package/web/assets/{interface-hyperlink.svg-8D6WX1Q9.js → interface-hyperlink.svg-Df34T3Eu.js} +1 -1
  119. package/web/assets/{interface-lighting-brightness.svg-DUoPpiwx.js → interface-lighting-brightness.svg-CBJnLIw7.js} +1 -1
  120. package/web/assets/{interface-lock-closed.svg-CzFKU_Z4.js → interface-lock-closed.svg-C082mEtr.js} +1 -1
  121. package/web/assets/{interface-lock-open-unlock.svg-BnrVx0lo.js → interface-lock-open-unlock.svg-ktQ_lN_X.js} +1 -1
  122. package/web/assets/{interface-login-key.svg-DAfv-MMA.js → interface-login-key.svg-1JZV_q3n.js} +1 -1
  123. package/web/assets/{interface-search.svg-Q2hCozQb.js → interface-search.svg-BWOepCe2.js} +1 -1
  124. package/web/assets/{interface-setting-cog.svg-Bvq0dvZR.js → interface-setting-cog.svg-Ck7-QPf8.js} +1 -1
  125. package/web/assets/{interface-share-megaphone-bullhorn.svg-fUmta8NQ.js → interface-share-megaphone-bullhorn.svg-B4XWLcvf.js} +1 -1
  126. package/web/assets/{interface-share-rocket.svg-BKSZkx2r.js → interface-share-rocket.svg-jsXjbfvo.js} +1 -1
  127. package/web/assets/{interface-share-satellite.svg-CB_uwNHS.js → interface-share-satellite.svg-DYc-pAdI.js} +1 -1
  128. package/web/assets/{interface-share-space-ship.svg-CcUB1z0B.js → interface-share-space-ship.svg-azKKMF8L.js} +1 -1
  129. package/web/assets/{interface-share.svg-Bug50vV0.js → interface-share.svg-k0lPxn1t.js} +1 -1
  130. package/web/assets/{interface-signal-square.svg-zg41vf79.js → interface-signal-square.svg-eCO4ev15.js} +1 -1
  131. package/web/assets/{interface-time-clock-circle.svg-a4omIVlV.js → interface-time-clock-circle.svg-C0Sm0_j9.js} +1 -1
  132. package/web/assets/{interface-time-hour-glass.svg-3zmzbQg_.js → interface-time-hour-glass.svg-hfRVIC1r.js} +1 -1
  133. package/web/assets/{interface-users-multiple.svg-BdPX2m6Y.js → interface-users-multiple.svg-Dm9UimfL.js} +1 -1
  134. package/web/assets/{interface-weather-moon.svg-D9_m-QTi.js → interface-weather-moon.svg-BhxFHPbN.js} +1 -1
  135. package/web/assets/{javascript-BJlgvmgy.js → javascript-_AR6wrF0.js} +1 -1
  136. package/web/assets/{jsonMode-D0zJGMKu.js → jsonMode-CYWBTJpV.js} +2 -2
  137. package/web/assets/{liquid-B0fONmvZ.js → liquid-Doe_Qoxn.js} +1 -1
  138. package/web/assets/{lspLanguageFeatures-CrwGD4mx.js → lspLanguageFeatures-TK5k9ahW.js} +2 -2
  139. package/web/assets/{mail-chat-bubble-square.svg-DZaD3FQ3.js → mail-chat-bubble-square.svg-CitsmxJ_.js} +1 -1
  140. package/web/assets/{mail-send-email-paper-airplane.svg-DkbRfjXY.js → mail-send-email-paper-airplane.svg-CYDMogt4.js} +1 -1
  141. package/web/assets/{mail-send-envelope.svg-5c8j8mB_.js → mail-send-envelope.svg-s-A4ytfv.js} +1 -1
  142. package/web/assets/{mdx-CKW2c2uF.js → mdx-CSf3fjQb.js} +1 -1
  143. package/web/assets/{message-DZ0RuJXv.js → message-5nViABpL.js} +2 -2
  144. package/web/assets/{minus-C_n6M3oC.js → minus-mDLJQeXY.js} +2 -2
  145. package/web/assets/modal-DcxJxueq.js +5 -0
  146. package/web/assets/modal-n2wMDd7F.js +1 -0
  147. package/web/assets/{money-cashier-receipt.svg-nNTup6TP.js → money-cashier-receipt.svg-B3w4dcvy.js} +1 -1
  148. package/web/assets/{money-currency-dollar-pay.svg-D6ct80fW.js → money-currency-dollar-pay.svg-By5pgatC.js} +1 -1
  149. package/web/assets/{money-graph-arrow-increase.svg-DrQ3RzKG.js → money-graph-arrow-increase.svg-JJK_oYl6.js} +1 -1
  150. package/web/assets/{money-graph-bar-chart-increase.svg-CgI8xnRe.js → money-graph-bar-chart-increase.svg-BMvEIVpy.js} +1 -1
  151. package/web/assets/multiselect-CqGmdE3J.js +251 -0
  152. package/web/assets/{nature-ecology-leaf.svg-SIUCD6fX.js → nature-ecology-leaf.svg-CGCHa8bD.js} +1 -1
  153. package/web/assets/{password-1NqZx53t.js → password-CSX8oB5M.js} +4 -4
  154. package/web/assets/{phone-telephone.svg-DuLAx6_w.js → phone-telephone.svg-CA8_wOQH.js} +1 -1
  155. package/web/assets/{plus-CP-dlA9U.js → plus-CGTKvVZ7.js} +6 -6
  156. package/web/assets/process.store-CTLjlYjT.js +1 -0
  157. package/web/assets/{programming-bug.svg-DzOMyrsK.js → programming-bug.svg-Cizii8p1.js} +1 -1
  158. package/web/assets/{programming-cloud.svg-CB7hznLf.js → programming-cloud.svg-pnwxJHaK.js} +1 -1
  159. package/web/assets/{programming-computer-database-server.svg-Vdd0DgKt.js → programming-computer-database-server.svg-CNt3Jbql.js} +1 -1
  160. package/web/assets/{programming-computer-database.svg-CEqheAaf.js → programming-computer-database.svg-ESSkZX3t.js} +1 -1
  161. package/web/assets/{programming-module-four-layout.svg-BdPzuTtB.js → programming-module-four-layout.svg-BGbJvE0O.js} +1 -1
  162. package/web/assets/{programming-module-three.svg-PwBqCXu4.js → programming-module-three.svg-D3gOTAlw.js} +1 -1
  163. package/web/assets/{programming-module.svg-DsBi6ejo.js → programming-module.svg-CSHFfsFS.js} +1 -1
  164. package/web/assets/{programming-script-code.svg-Broms4WV.js → programming-script-code.svg-DjFoDv0c.js} +1 -1
  165. package/web/assets/{python-DfCB9qU4.js → python-MEGp0q_z.js} +1 -1
  166. package/web/assets/{razor-BBh-Aao5.js → razor-CEazjH4x.js} +1 -1
  167. package/web/assets/router-BmJL8z_u.js +1 -0
  168. package/web/assets/settings-scope-BGuX4AI9.js +1 -0
  169. package/web/assets/settings.store-a49Jijfe.js +1 -0
  170. package/web/assets/{shopping-cart.svg-CKdnb6hR.js → shopping-cart.svg-Cu7M1oni.js} +1 -1
  171. package/web/assets/{shopping-gift-present.svg-DIxbK0pG.js → shopping-gift-present.svg-Cdnu9uAQ.js} +1 -1
  172. package/web/assets/{shopping-shipping-box-parcel-package.svg-C-9ntsF7.js → shopping-shipping-box-parcel-package.svg-C6BL6pCC.js} +1 -1
  173. package/web/assets/{src-C77fweBS.js → src-BKhMPhsU.js} +13 -13
  174. package/web/assets/src-DQvVUJe0.js +1 -0
  175. package/web/assets/{tabpanel-C0aUv4Tp.js → tabpanel-B9VkBnSU.js} +3 -3
  176. package/web/assets/{tag-new-circle.svg-D9mnL-uB.js → tag-new-circle.svg-BpXDXKC4.js} +1 -1
  177. package/web/assets/{travel-map-earth-globe.svg-B1pf_UKK.js → travel-map-earth-globe.svg-B_t8kO2f.js} +1 -1
  178. package/web/assets/trpc-B-qv_zm2.js +1 -0
  179. package/web/assets/{tsMode-CBiaVW1A.js → tsMode-lBRYpyez.js} +6 -6
  180. package/web/assets/{typescript-DSa5kNRs.js → typescript-DGOxNj9C.js} +1 -1
  181. package/web/assets/useAiChatDrawer-xv2HAiHA.js +1 -0
  182. package/web/assets/useCloudOrg-_Z_zfd63.js +1 -0
  183. package/web/assets/useProjectCatalog-CNbze2zg.js +1 -0
  184. package/web/assets/{xml-BYwyGk9G.js → xml-Cqgx2kWf.js} +1 -1
  185. package/web/assets/{yaml-Dl2OSYTO.js → yaml-CIs4cY7e.js} +1 -1
  186. package/web/index.html +2 -2
  187. package/agent-manager-KX5MSTAK.js +0 -7
  188. package/chunk-NVKGIBJN.js.map +0 -1
  189. package/src-R6JOPDNL.js.map +0 -1
  190. package/web/assets/AgentScalarChatInterface.vue-D53JSFzC.js +0 -13
  191. package/web/assets/AgentTerminalPage-zuXFRGB0.js +0 -1
  192. package/web/assets/AiChatPage-BjC9iQnH.js +0 -1
  193. package/web/assets/AppLayout-fVYVDGr-.js +0 -1
  194. package/web/assets/AuditPage-DvhG8fxh.js +0 -1
  195. package/web/assets/CloudAccountPage-CE3b0q5v.js +0 -1
  196. package/web/assets/CloudAccountPage-ChVg2wnJ.css +0 -1
  197. package/web/assets/CloudAccountPage-YkGuA0nb.js +0 -1
  198. package/web/assets/CloudMessagingPage-okqqdltV.js +0 -2
  199. package/web/assets/DashboardPage-CmZITjvU.js +0 -1
  200. package/web/assets/EnvironmentForm-_y5JoRHH.js +0 -108
  201. package/web/assets/EnvironmentFormPage-DYhDZdPj.js +0 -1
  202. package/web/assets/EnvironmentListPage-soh5FJDb.js +0 -1
  203. package/web/assets/EnvironmentSelector-CQNLsssr.js +0 -1
  204. package/web/assets/KanbanBoardPage-BvMPl2FW.css +0 -1
  205. package/web/assets/KanbanBoardPage-C3cG7v4z.js +0 -4
  206. package/web/assets/KanbanListPage-xyv-oiyq.js +0 -1
  207. package/web/assets/LaunchProjectPage-DKqMjh-V.js +0 -1
  208. package/web/assets/LaunchServicesPage-IhWdB_Gw.js +0 -1
  209. package/web/assets/LogViewer-ChElXmMu.js +0 -3
  210. package/web/assets/ProcessDetailPage-DFv0ESg1.js +0 -1
  211. package/web/assets/ProjectDetailPage-DEXvg1pg.js +0 -1
  212. package/web/assets/ProjectListPage-h0AQ_RCw.js +0 -1
  213. package/web/assets/RandomJobTestPage-Dp7A7Qgx.js +0 -2
  214. package/web/assets/ScenarioEditorPage-D0_VpZUT.js +0 -14
  215. package/web/assets/SendToChatDialog-BpJetpLA.js +0 -1
  216. package/web/assets/ServiceControlsBar-qiCaxnab.js +0 -1
  217. package/web/assets/ServiceDetailPage-BD8D0CuN.js +0 -2
  218. package/web/assets/ServiceFormPage-Dax40DkX.js +0 -1
  219. package/web/assets/SettingsPage-zriZbuPv.js +0 -2062
  220. package/web/assets/UnlockPage-BYtu3w_t.js +0 -1
  221. package/web/assets/VariablesTable-IvhXwdsn.js +0 -1
  222. package/web/assets/VaultMigrationPage-6Id4Fmeo.js +0 -1
  223. package/web/assets/VersionBadge-B-PtNx51.js +0 -1
  224. package/web/assets/WikiPage-DqqlIbM6.js +0 -1
  225. package/web/assets/WikiView-BZvgaTQp.js +0 -1
  226. package/web/assets/auth-errors-B510-08J.js +0 -1
  227. package/web/assets/autocomplete-DpSrZtZQ.js +0 -422
  228. package/web/assets/card-TJ-av0Pi.js +0 -32
  229. package/web/assets/client-D5eMRPpD.js +0 -24
  230. package/web/assets/cloud-chat.store-DzSz0QMb.js +0 -1
  231. package/web/assets/column-BAToyYIb.js +0 -907
  232. package/web/assets/interface-content-folder.svg-BF9F5qf2.js +0 -1
  233. package/web/assets/modal-C5SccGq4.js +0 -1
  234. package/web/assets/modal-CvqNyAvh.js +0 -5
  235. package/web/assets/multiselect-CSd05WUd.js +0 -251
  236. package/web/assets/process.store-B_vfVoW1.js +0 -1
  237. package/web/assets/router-CEOd6ZAd.js +0 -1
  238. package/web/assets/settings.store-BfNYjk_y.js +0 -1
  239. package/web/assets/src-CXyRAwtR.js +0 -1
  240. package/web/assets/trpc-BNJN8Syq.js +0 -1
  241. package/web/assets/useAiChatDrawer-DJh6hREC.js +0 -1
  242. package/web/assets/useCloudOrg-DYG5BAW1.js +0 -1
  243. package/web/assets/useProjectCatalog-Dyg12Vcr.js +0 -1
  244. /package/{agent-manager-KX5MSTAK.js.map → agent-manager-RGDHYM64.js.map} +0 -0
  245. /package/web/assets/{abap-DMmvWZUy.js → abap-DT2sAUGb.js} +0 -0
  246. /package/web/assets/{apex-D89PiTzU.js → apex-BqR8ejP0.js} +0 -0
  247. /package/web/assets/{azcli-ChnjTTA5.js → azcli-DK_jNPxf.js} +0 -0
  248. /package/web/assets/{bat-uLzQ_FhK.js → bat-BLmcleEN.js} +0 -0
  249. /package/web/assets/{bicep-CMOK7Q1v.js → bicep-DkR6Nnyy.js} +0 -0
  250. /package/web/assets/{cameligo-D1AyojFH.js → cameligo-DfT1QA_V.js} +0 -0
  251. /package/web/assets/{clojure-D9JoUgXn.js → clojure-BbsGZ_q_.js} +0 -0
  252. /package/web/assets/{coffee-D-5sSToL.js → coffee-CZQPrkXM.js} +0 -0
  253. /package/web/assets/{cpp-BqT_t_y9.js → cpp-CZLFjmrQ.js} +0 -0
  254. /package/web/assets/{csharp-CuToYI91.js → csharp-CljM85nx.js} +0 -0
  255. /package/web/assets/{csp-CffcxvlO.js → csp-B7ItwhrE.js} +0 -0
  256. /package/web/assets/{css-CUD0biQW.js → css-CIuVviKc.js} +0 -0
  257. /package/web/assets/{cypher-ByyWL_9U.js → cypher-Lk1MiloH.js} +0 -0
  258. /package/web/assets/{dart-B__ipcUn.js → dart-B83vLjrb.js} +0 -0
  259. /package/web/assets/{dockerfile-DS-_8bWw.js → dockerfile--LImnfYW.js} +0 -0
  260. /package/web/assets/{ecl-DWsNCBUj.js → ecl-C1mPHzto.js} +0 -0
  261. /package/web/assets/{elixir-OfjEy-3g.js → elixir-F7VkySYx.js} +0 -0
  262. /package/web/assets/{flow9-BPX22hxS.js → flow9-CyDLjcIP.js} +0 -0
  263. /package/web/assets/{fsharp-CVJcfIUn.js → fsharp-BtAy52j8.js} +0 -0
  264. /package/web/assets/{go-CfRHZ3yg.js → go-nAky3Nvu.js} +0 -0
  265. /package/web/assets/{graphql-BZaO3ttK.js → graphql-BFHB5AhE.js} +0 -0
  266. /package/web/assets/{hcl-BF9-qUku.js → hcl-DiNGWUmy.js} +0 -0
  267. /package/web/assets/{ini-BUvw6xhr.js → ini-D1Fg5XrG.js} +0 -0
  268. /package/web/assets/{java-BZ5BNd2x.js → java-C-10mSK7.js} +0 -0
  269. /package/web/assets/{julia-DN38cgYT.js → julia-Bqx3lIQk.js} +0 -0
  270. /package/web/assets/{kotlin-CjEJKT8q.js → kotlin-BBx7H7X0.js} +0 -0
  271. /package/web/assets/{less-DMEH_Nwq.js → less-BFHUOKEO.js} +0 -0
  272. /package/web/assets/{lexon-CKQUX8Ov.js → lexon-CZNaGS2_.js} +0 -0
  273. /package/web/assets/{lua-CuWjKLJ0.js → lua-iuAeZ15y.js} +0 -0
  274. /package/web/assets/{m3-CEp1bVrM.js → m3-Dotjo0U8.js} +0 -0
  275. /package/web/assets/{markdown-DxtQ_puq.js → markdown-B_31SQ_4.js} +0 -0
  276. /package/web/assets/{mips-Dx7qydpO.js → mips-Dp4rVQ6A.js} +0 -0
  277. /package/web/assets/{msdax-DpNCaSiO.js → msdax-DGvgi_My.js} +0 -0
  278. /package/web/assets/{mysql-C3xsBDPP.js → mysql-jqX2Izgu.js} +0 -0
  279. /package/web/assets/{objective-c-BbaVHCX1.js → objective-c-KWePe42v.js} +0 -0
  280. /package/web/assets/{pascal-sDju2faq.js → pascal-k9Augcc6.js} +0 -0
  281. /package/web/assets/{pascaligo-ChCdOH6b.js → pascaligo-CijZuMoC.js} +0 -0
  282. /package/web/assets/{perl-BCgLFj01.js → perl-CtKbbdqv.js} +0 -0
  283. /package/web/assets/{pgsql-DtKby69f.js → pgsql-IYBGBY8p.js} +0 -0
  284. /package/web/assets/{php-NYvR17Qb.js → php-CZgzKas5.js} +0 -0
  285. /package/web/assets/{pla-gSg-RBG0.js → pla-DxEUZCU5.js} +0 -0
  286. /package/web/assets/{postiats-BQxHyDfy.js → postiats-Cf9oispl.js} +0 -0
  287. /package/web/assets/{powerquery-DzDmbVZN.js → powerquery-Cyri2j7y.js} +0 -0
  288. /package/web/assets/{powershell-yy4Q5BEQ.js → powershell-CezsOW84.js} +0 -0
  289. /package/web/assets/{protobuf-B_hIk9q0.js → protobuf-BVSVLozM.js} +0 -0
  290. /package/web/assets/{pug-CdeEb1ft.js → pug-tdDHxxGI.js} +0 -0
  291. /package/web/assets/{qsharp-CiSquEgT.js → qsharp-BbxYBU73.js} +0 -0
  292. /package/web/assets/{r-D1Ocz1x4.js → r-CMiogh24.js} +0 -0
  293. /package/web/assets/{redis-D42Y12yz.js → redis-Be7YRs2n.js} +0 -0
  294. /package/web/assets/{redshift-CS62MYfG.js → redshift-BMtedC4Y.js} +0 -0
  295. /package/web/assets/{restructuredtext-CKFSMEXw.js → restructuredtext-BeqA_Nw7.js} +0 -0
  296. /package/web/assets/{ruby-C4gTaeU3.js → ruby-DQh1qjXf.js} +0 -0
  297. /package/web/assets/{rust-C_f2AiHQ.js → rust-Dtb7SJq9.js} +0 -0
  298. /package/web/assets/{sb-CdNdpP1r.js → sb-YjmuT7ii.js} +0 -0
  299. /package/web/assets/{scala-N4EWaFTL.js → scala-3ZhdCU1r.js} +0 -0
  300. /package/web/assets/{scheme-CttIARxx.js → scheme-Ck6PwJVN.js} +0 -0
  301. /package/web/assets/{scss-v0wtbxaE.js → scss-CdSZkceb.js} +0 -0
  302. /package/web/assets/{shell-BJdEWXbc.js → shell-D951-SzL.js} +0 -0
  303. /package/web/assets/{solidity-DKDa59zZ.js → solidity-BtE4jdUZ.js} +0 -0
  304. /package/web/assets/{sophia--Kw-vDpr.js → sophia-CPgh17t9.js} +0 -0
  305. /package/web/assets/{sparql-BI8jS9Pc.js → sparql-D5V7fRaF.js} +0 -0
  306. /package/web/assets/{sql-Dz6z--6O.js → sql-CIe64i2G.js} +0 -0
  307. /package/web/assets/{st-CT8D05gv.js → st-BUZL4NkZ.js} +0 -0
  308. /package/web/assets/{swift-Ayb3KzWx.js → swift-BGpAy-yZ.js} +0 -0
  309. /package/web/assets/{systemverilog-BtWJM9O5.js → systemverilog-CYBqw_20.js} +0 -0
  310. /package/web/assets/{tcl-BJvUWpbV.js → tcl-CqmcDc3_.js} +0 -0
  311. /package/web/assets/{twig-c3YaBCU6.js → twig-BWVIPIPE.js} +0 -0
  312. /package/web/assets/{typespec-gFCWLBoZ.js → typespec-mTM8EdlQ.js} +0 -0
  313. /package/web/assets/{uuid-B3B8HIK9.js → uuid-aTVnbu2y.js} +0 -0
  314. /package/web/assets/{vb-CVtfWXV2.js → vb-xvgQ7mYU.js} +0 -0
  315. /package/web/assets/{w3c-keyname-nn0G3QC2.js → w3c-keyname-CjzYiphr.js} +0 -0
  316. /package/web/assets/{wgsl-C_huaGQy.js → wgsl-C8m-CvBD.js} +0 -0
@@ -35,6 +35,7 @@ import {
35
35
  allVaultRoots,
36
36
  clearEncryptionKey,
37
37
  clearPendingLegacyKey,
38
+ clearVaultLock,
38
39
  decryptValue,
39
40
  decryptValueWith,
40
41
  decryptVariableSlots,
@@ -60,10 +61,12 @@ import {
60
61
  legacyMachineFilePath,
61
62
  legacyVaultRoot,
62
63
  listCloudVaults,
64
+ listOrgVaults,
63
65
  loadFromEnv,
64
66
  localVaultRoot,
65
67
  machineFilePath,
66
68
  machineRoot,
69
+ markVaultLocked,
67
70
  nativeParsers,
68
71
  orgVaultRoot,
69
72
  projectKey,
@@ -71,8 +74,10 @@ import {
71
74
  readEntity,
72
75
  readOrgClouds,
73
76
  refOfProjectAmong,
77
+ refsOfProjectId,
74
78
  registerProjectLoader,
75
79
  rootOfProjectAmong,
80
+ rootOfProjectRef,
76
81
  serviceStore,
77
82
  setEncryptionKey,
78
83
  setEncryptionKeyInMemory,
@@ -83,8 +88,9 @@ import {
83
88
  stampOrgCloud,
84
89
  traefikManager,
85
90
  vaultFor,
91
+ vaultIdOfRoot,
86
92
  writeEntity
87
- } from "./chunk-NVKGIBJN.js";
93
+ } from "./chunk-EL75GG7O.js";
88
94
 
89
95
  // ../server/src/create-server.ts
90
96
  import express from "express";
@@ -127,7 +133,7 @@ async function verifyJwt(token) {
127
133
  // ../server/src/services/api-key-store.ts
128
134
  import { randomBytes as randomBytes2, randomUUID, timingSafeEqual } from "crypto";
129
135
  import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
130
- import { dirname } from "path";
136
+ import { dirname, join } from "path";
131
137
  function maskApiKey(apiKey) {
132
138
  const { key, ...rest } = apiKey;
133
139
  return { ...rest, keyPreview: key.slice(-4) };
@@ -136,47 +142,66 @@ var FILE_PUBLIC = "api-keys.json";
136
142
  var FILE_PRIVATE = "api-keys.private.json";
137
143
  var ApiKeyStore = class {
138
144
  keys = /* @__PURE__ */ new Map();
145
+ /** Le propriétaire de chaque clé, d'après le coffre où elle a été lue. */
146
+ owners = /* @__PURE__ */ new Map();
147
+ /** Tous les coffres rencontrés — voir `AgentStore.knownOwners`, même raison. */
148
+ knownOwners = /* @__PURE__ */ new Set([null]);
139
149
  loaded = false;
140
150
  /**
141
151
  * Encrypted at rest, so the file sits beside the key that opens it — read
142
152
  * under a different key it would decrypt to blanks, then be saved back blank.
143
153
  */
144
- filePath(isPublic) {
145
- return keyedFilePath(isPublic ? FILE_PUBLIC : FILE_PRIVATE);
146
- }
147
- async ensureDir() {
148
- await mkdir(dirname(await this.filePath(true)), { recursive: true });
149
- }
150
- async persistFile(isPublic) {
151
- await this.ensureDir();
152
- const path = await this.filePath(isPublic);
153
- const keys = Array.from(this.keys.values()).filter((k) => k.isPublic === isPublic).map((k) => ({ ...k, key: encryptValue(k.key) }));
154
+ /**
155
+ * Le fichier d'un propriétaire. Côté machine il reste collé à la clé qui
156
+ * l'ouvre (`keyedFilePath`) ; côté organisation, c'est son coffre.
157
+ */
158
+ filePath(isPublic, owner) {
159
+ const name = isPublic ? FILE_PUBLIC : FILE_PRIVATE;
160
+ if (owner === null) return keyedFilePath(name);
161
+ return Promise.resolve(join(orgVaultRoot(owner), name));
162
+ }
163
+ async persistFile(isPublic, owner) {
164
+ const path = await this.filePath(isPublic, owner);
165
+ await mkdir(dirname(path), { recursive: true });
166
+ const keys = Array.from(this.keys.values()).filter((k) => k.isPublic === isPublic && (this.owners.get(k.id) ?? null) === owner).map((k) => ({ ...k, key: encryptValue(k.key, owner ?? void 0) }));
154
167
  const tmp = path + ".tmp";
155
168
  await writeFile(tmp, JSON.stringify({ keys }, null, 2), "utf-8");
156
169
  await rename(tmp, path);
157
170
  await chmod(path, 384);
158
171
  }
172
+ /**
173
+ * Réécrire ce fichier pour chaque propriétaire connu.
174
+ *
175
+ * Y compris ceux qui n'ont plus de clé : sinon une clé supprimée — ou dont le
176
+ * propriétaire a changé — survivrait dans son ancien coffre.
177
+ */
159
178
  async persist(isPublic) {
160
- await this.persistFile(isPublic);
179
+ for (const owner of this.knownOwners) await this.persistFile(isPublic, owner);
161
180
  }
162
- async loadFile(path) {
181
+ async loadFile(path, owner) {
163
182
  try {
164
183
  const raw = await readFile(path, "utf-8");
165
184
  const parsed = ApiKeysFileSchema.safeParse(JSON.parse(raw));
166
185
  if (parsed.success) {
167
186
  for (const k of parsed.data.keys) {
168
- const key = isEncryptedValue(k.key) ? decryptValue(k.key) : k.key;
187
+ const key = isEncryptedValue(k.key) ? decryptValue(k.key, owner ?? void 0) : k.key;
169
188
  this.keys.set(k.id, { ...k, key });
189
+ this.owners.set(k.id, owner);
190
+ this.knownOwners.add(owner);
170
191
  }
171
192
  }
172
193
  } catch {
173
194
  }
174
195
  }
196
+ /** La machine, puis chaque organisation dont un coffre existe sur ce disque. */
175
197
  async load() {
176
198
  if (this.loaded) return;
177
199
  this.loaded = true;
178
- await this.loadFile(await this.filePath(true));
179
- await this.loadFile(await this.filePath(false));
200
+ const owners = [null, ...await listOrgVaults()];
201
+ for (const owner of owners) {
202
+ await this.loadFile(await this.filePath(true, owner), owner);
203
+ await this.loadFile(await this.filePath(false, owner), owner);
204
+ }
180
205
  }
181
206
  /**
182
207
  * Keys as exposed to callers — secrets stripped. Use `reveal` to obtain one
@@ -191,7 +216,7 @@ var ApiKeyStore = class {
191
216
  await this.load();
192
217
  return this.keys.get(id)?.key ?? null;
193
218
  }
194
- async create(name, scopes, isPublic) {
219
+ async create(name, scopes, isPublic, owner = null) {
195
220
  await this.load();
196
221
  const apiKey = {
197
222
  id: `apikey-${randomUUID()}`,
@@ -202,6 +227,8 @@ var ApiKeyStore = class {
202
227
  isPublic
203
228
  };
204
229
  this.keys.set(apiKey.id, apiKey);
230
+ this.owners.set(apiKey.id, owner);
231
+ this.knownOwners.add(owner);
205
232
  await this.persist(isPublic);
206
233
  return apiKey;
207
234
  }
@@ -212,8 +239,8 @@ var ApiKeyStore = class {
212
239
  const updated = { ...existing, name, scopes, isPublic };
213
240
  this.keys.set(id, updated);
214
241
  if (existing.isPublic !== isPublic) {
215
- await this.persistFile(existing.isPublic);
216
- await this.persistFile(isPublic);
242
+ await this.persist(existing.isPublic);
243
+ await this.persist(isPublic);
217
244
  } else {
218
245
  await this.persist(isPublic);
219
246
  }
@@ -224,6 +251,7 @@ var ApiKeyStore = class {
224
251
  const existing = this.keys.get(id);
225
252
  if (!existing) return false;
226
253
  this.keys.delete(id);
254
+ this.owners.delete(id);
227
255
  await this.persist(existing.isPublic);
228
256
  return true;
229
257
  }
@@ -450,11 +478,11 @@ import { TRPCError as TRPCError2 } from "@trpc/server";
450
478
  import { randomUUID as randomUUID2 } from "crypto";
451
479
  import { EventEmitter } from "events";
452
480
  import { readFile as readFile2, writeFile as writeFile3, rename as rename2, mkdir as mkdir2, chmod as chmod2, rm } from "fs/promises";
453
- import { join as join2 } from "path";
481
+ import { join as join3 } from "path";
454
482
 
455
483
  // ../server/src/utils/ensure-data-gitignore.ts
456
484
  import { writeFile as writeFile2 } from "fs/promises";
457
- import { join } from "path";
485
+ import { join as join2 } from "path";
458
486
  var GITIGNORE_CONTENT = `
459
487
  #################
460
488
  # Do not edit this file #
@@ -491,7 +519,7 @@ conversations.private.json
491
519
  *.log.*
492
520
  `;
493
521
  async function ensureDataGitignore(dataDir) {
494
- const gitignorePath = join(dataDir, ".gitignore");
522
+ const gitignorePath = join2(dataDir, ".gitignore");
495
523
  await writeFile2(gitignorePath, GITIGNORE_CONTENT, "utf-8");
496
524
  }
497
525
 
@@ -506,14 +534,14 @@ async function readLocalProject() {
506
534
  const root = localVaultRoot();
507
535
  let current = null;
508
536
  try {
509
- const raw = await readFile2(join2(root, FILENAME), "utf-8");
537
+ const raw = await readFile2(join3(root, FILENAME), "utf-8");
510
538
  const parsed = ProjectSchema.safeParse(JSON.parse(raw));
511
539
  if (parsed.success) current = parsed.data;
512
540
  } catch {
513
541
  }
514
542
  let legacy;
515
543
  try {
516
- legacy = JSON.parse(await readFile2(join2(root, LEGACY_FILENAME), "utf-8"));
544
+ legacy = JSON.parse(await readFile2(join3(root, LEGACY_FILENAME), "utf-8"));
517
545
  } catch {
518
546
  return { local: current, strayCloud: [] };
519
547
  }
@@ -523,7 +551,7 @@ async function readLocalProject() {
523
551
  if (locals.length > 1) {
524
552
  const names = locals.map((p) => `"${p.name}" (${p.id})`).join(", ");
525
553
  throw new Error(
526
- `${join2(root, LEGACY_FILENAME)} holds ${locals.length} local projects: ${names}. A directory now holds a single local project \u2014 move the others to their own directory, or into an organization, then start again.`
554
+ `${join3(root, LEGACY_FILENAME)} holds ${locals.length} local projects: ${names}. A directory now holds a single local project \u2014 move the others to their own directory, or into an organization, then start again.`
527
555
  );
528
556
  }
529
557
  return { local: locals[0] ?? null, strayCloud };
@@ -532,7 +560,7 @@ async function readCloudProjects() {
532
560
  const found = [];
533
561
  for (const { orgId, projectId, root } of await listCloudVaults()) {
534
562
  try {
535
- const raw = await readFile2(join2(root, FILENAME), "utf-8");
563
+ const raw = await readFile2(join3(root, FILENAME), "utf-8");
536
564
  const parsed = ProjectSchema.safeParse({ ...JSON.parse(raw), id: projectId, orgId });
537
565
  if (parsed.success) found.push({ project: parsed.data, root });
538
566
  } catch {
@@ -562,7 +590,7 @@ var ProjectStore = class {
562
590
  /** Emits `'saved'` after every persisted mutation, for the cloud sync bridge. */
563
591
  events = new EventEmitter();
564
592
  getFilePath(root = localVaultRoot()) {
565
- return join2(root, FILENAME);
593
+ return join3(root, FILENAME);
566
594
  }
567
595
  async writeAtomic(filePath, data) {
568
596
  const tmpPath = filePath + ".tmp";
@@ -643,7 +671,7 @@ var ProjectStore = class {
643
671
  await rm(this.getFilePath(), { force: true }).catch(() => {
644
672
  });
645
673
  }
646
- await rename2(join2(localVaultRoot(), LEGACY_FILENAME), join2(localVaultRoot(), LEGACY_FILENAME + ".bak")).catch(() => {
674
+ await rename2(join3(localVaultRoot(), LEGACY_FILENAME), join3(localVaultRoot(), LEGACY_FILENAME + ".bak")).catch(() => {
647
675
  });
648
676
  for (const { project, root } of cloud) {
649
677
  await mkdir2(root, { recursive: true });
@@ -732,6 +760,40 @@ var ProjectStore = class {
732
760
  await this.save();
733
761
  return updated;
734
762
  }
763
+ /**
764
+ * Faire passer le projet local de ce dossier dans une organisation.
765
+ *
766
+ * Le seul déplacement de coffre qu'on s'autorise, et il ne va que dans ce
767
+ * sens : le projet quitte `<cwd>/.runeya` pour
768
+ * `~/.runeya/orgs/<org>/projects/<id>/`, en gardant son identifiant. Il n'y a
769
+ * donc jamais deux copies à réconcilier — c'est ce qui rendait « publier »
770
+ * dangereux tant que le projet restait aussi local.
771
+ *
772
+ * Refusé si l'organisation porte déjà ce projet : la copie d'ici écraserait
773
+ * celle du cloud, et ces deux histoires ne sont pas à nous de les fusionner.
774
+ *
775
+ * Ce qui suit le projet est écrit par les stores eux-mêmes : `save()` réécrit
776
+ * chaque coffre — le fichier local disparaît, celui de l'org apparaît — puis
777
+ * les contenus sont relogés depuis le registre, qui pointe désormais ailleurs.
778
+ */
779
+ async moveToOrg(projectId, orgId) {
780
+ await this.load();
781
+ const local = this.projects.get(projectKey({ id: projectId, orgId: null }));
782
+ if (!local) throw new Error("PROJECT_NOT_LOCAL");
783
+ if (this.projects.get(projectKey({ id: projectId, orgId }))) {
784
+ throw new Error("PROJECT_ALREADY_IN_ORG");
785
+ }
786
+ const moved = { ...local, orgId };
787
+ this.projects.delete(projectKey(local));
788
+ forgetProject(local);
789
+ this.projects.set(projectKey(moved), moved);
790
+ setProjectVault(moved, vaultFor(moved).root);
791
+ setProjectServices(moved, moved.serviceIds ?? []);
792
+ await this.save();
793
+ await serviceStore.reassignOrg(moved.serviceIds ?? [], orgId);
794
+ await environmentStore.relocateAll();
795
+ return moved;
796
+ }
735
797
  async delete(target) {
736
798
  await this.load();
737
799
  const found = this.find(target);
@@ -902,7 +964,7 @@ registerProjectLoader(() => projectStore.list().then(() => void 0));
902
964
 
903
965
  // ../server/src/services/environment-overrides-store.ts
904
966
  import { readFile as readFile3, writeFile as writeFile4, rename as rename3, mkdir as mkdir3, chmod as chmod3 } from "fs/promises";
905
- import { dirname as dirname2, join as join3 } from "path";
967
+ import { dirname as dirname2, join as join4 } from "path";
906
968
  var FILENAME2 = "environment-overrides.json";
907
969
  var EnvironmentOverridesStore = class {
908
970
  data = {};
@@ -956,7 +1018,7 @@ var EnvironmentOverridesStore = class {
956
1018
  const sources = [
957
1019
  this.getLegacyFilePath(),
958
1020
  await keyedFilePath(FILENAME2),
959
- ...roots.map((root) => join3(root, FILENAME2))
1021
+ ...roots.map((root) => join4(root, FILENAME2))
960
1022
  ];
961
1023
  for (const path of sources) {
962
1024
  let parsed;
@@ -972,7 +1034,8 @@ var EnvironmentOverridesStore = class {
972
1034
  const decrypted = decryptVariableSlots(
973
1035
  parsed,
974
1036
  "environment-overrides",
975
- (variableId) => this.unreadableSecrets.add(variableId)
1037
+ (variableId) => this.unreadableSecrets.add(variableId),
1038
+ vaultIdOfRoot(dirname2(path))
976
1039
  );
977
1040
  for (const [id, override] of Object.entries(decrypted)) {
978
1041
  this.data[id] = { value: override.value };
@@ -1004,14 +1067,15 @@ var EnvironmentOverridesStore = class {
1004
1067
  for (const [root, overrides] of byRoot) {
1005
1068
  const encrypted = this.keepUnreadableSecrets(encryptVariableSlots(
1006
1069
  overrides,
1007
- this.diskEncryptedSlots
1070
+ this.diskEncryptedSlots,
1071
+ vaultIdOfRoot(root)
1008
1072
  ));
1009
1073
  Object.assign(
1010
1074
  this.diskEncryptedSlots,
1011
1075
  extractEncryptedVariableSlots(encrypted)
1012
1076
  );
1013
1077
  await mkdir3(root, { recursive: true });
1014
- const path = join3(root, FILENAME2);
1078
+ const path = join4(root, FILENAME2);
1015
1079
  const tmpPath = path + ".tmp";
1016
1080
  await writeFile4(tmpPath, JSON.stringify(encrypted, null, 2), "utf-8");
1017
1081
  await rename3(tmpPath, path);
@@ -1540,7 +1604,7 @@ var auditService = new AuditService();
1540
1604
 
1541
1605
  // ../server/src/services/project-path-store.ts
1542
1606
  import { readFile as readFile5, writeFile as writeFile5, rename as rename5, mkdir as mkdir5, chmod as chmod4, stat as stat2 } from "fs/promises";
1543
- import { isAbsolute, join as join4, resolve } from "path";
1607
+ import { isAbsolute, join as join5, resolve } from "path";
1544
1608
  var FILENAME3 = "project-paths.json";
1545
1609
  var ProjectPathStore = class {
1546
1610
  paths = null;
@@ -1610,7 +1674,7 @@ function isPathMap(value) {
1610
1674
  async function holdsRuneyaProject(path) {
1611
1675
  for (const candidate of ["project.json", "projects.json"]) {
1612
1676
  try {
1613
- await stat2(join4(path, ".runeya", candidate));
1677
+ await stat2(join5(path, ".runeya", candidate));
1614
1678
  return true;
1615
1679
  } catch {
1616
1680
  }
@@ -1896,27 +1960,61 @@ import { TRPCError as TRPCError3 } from "@trpc/server";
1896
1960
 
1897
1961
  // ../server/src/services/settings-manager.ts
1898
1962
  import { readFile as readFile6, writeFile as writeFile6, rename as rename6, mkdir as mkdir6, chmod as chmod5 } from "fs/promises";
1963
+ import { join as join6 } from "path";
1899
1964
  var SETTINGS_FILE = "settings.json";
1900
1965
  var SETTINGS_PRIVATE_FILE = "settings.private.json";
1901
1966
  var SCHEMA_VERSION_FILE = "schema-version.json";
1902
1967
  var CURRENT_SCHEMA_VERSION = 4;
1903
1968
  var SettingsManager = class {
1904
- settings = null;
1905
- settingsPrivate = null;
1969
+ settings = /* @__PURE__ */ new Map();
1970
+ settingsPrivate = /* @__PURE__ */ new Map();
1906
1971
  schemaVersion = null;
1907
- async ensureDir() {
1908
- await mkdir6(machineRoot(), { recursive: true });
1972
+ /**
1973
+ * Le coffre visé, sous forme de racine sur le disque.
1974
+ *
1975
+ * Sans organisation, la racine machine — pas le répertoire de lancement. Ce
1976
+ * n'est pas un reste de l'ancien monde : y écrire forcerait un `.runeya` à
1977
+ * exister chez quelqu'un qui ne travaille que sur des projets cloud, ce que
1978
+ * les coffres ont justement supprimé (et ce qu'un test garde).
1979
+ */
1980
+ rootOf(vault) {
1981
+ return vault ? orgVaultRoot(vault) : machineRoot();
1909
1982
  }
1910
- async atomicWrite(filename, data) {
1911
- await this.ensureDir();
1912
- const filePath = machineFilePath(filename);
1983
+ cacheKey(vault) {
1984
+ return vault ?? "";
1985
+ }
1986
+ async atomicWrite(filename, data, vault) {
1987
+ const root = this.rootOf(vault);
1988
+ await mkdir6(root, { recursive: true });
1989
+ const filePath = join6(root, filename);
1913
1990
  const tmpPath = filePath + ".tmp";
1914
1991
  await writeFile6(tmpPath, JSON.stringify(data, null, 2), "utf-8");
1915
1992
  await rename6(tmpPath, filePath);
1916
1993
  await chmod5(filePath, 384);
1917
1994
  }
1918
- /** Read the machine file, falling back to the launch directory of old. */
1919
- async readJson(filename) {
1995
+ /**
1996
+ * Lire le fichier de CE coffre, et rien d'autre.
1997
+ *
1998
+ * Pas de repli sur un autre emplacement : un réglage lu ailleurs que là où il
1999
+ * est écrit fait deux vérités pour un même fait, et l'accord entre les deux
2000
+ * devient une chose de plus à tenir. Ce que les versions précédentes ont
2001
+ * écrit ailleurs est déplacé une fois pour toutes par
2002
+ * `migrateSettingsIntoVaults`, pas relu indéfiniment.
2003
+ */
2004
+ async readJson(filename, vault) {
2005
+ try {
2006
+ return JSON.parse(await readFile6(join6(this.rootOf(vault), filename), "utf-8"));
2007
+ } catch {
2008
+ return null;
2009
+ }
2010
+ }
2011
+ /** Vide les caches — après un déplacement de fichiers sous nos pieds. */
2012
+ forget() {
2013
+ this.settings.clear();
2014
+ this.settingsPrivate.clear();
2015
+ }
2016
+ /** Un fichier qui appartient au poste, pas à un coffre. */
2017
+ async readMachineJson(filename) {
1920
2018
  for (const path of [machineFilePath(filename), legacyMachineFilePath(filename)]) {
1921
2019
  try {
1922
2020
  return JSON.parse(await readFile6(path, "utf-8"));
@@ -1925,43 +2023,53 @@ var SettingsManager = class {
1925
2023
  }
1926
2024
  return null;
1927
2025
  }
1928
- async getSettings() {
1929
- if (!this.settings) {
1930
- const raw = await this.readJson(SETTINGS_FILE);
2026
+ async getSettings(vault) {
2027
+ const key = this.cacheKey(vault);
2028
+ let current = this.settings.get(key);
2029
+ if (!current) {
2030
+ const raw = await this.readJson(SETTINGS_FILE, vault);
1931
2031
  const parsed = raw ? SettingsSchema.safeParse(raw) : null;
1932
2032
  if (parsed?.success) {
1933
- this.settings = parsed.data;
2033
+ current = parsed.data;
2034
+ this.settings.set(key, current);
1934
2035
  } else {
1935
- this.settings = SettingsSchema.parse({ mode: env.MODE, cloudUrl: env.CLOUD_URL ?? null });
1936
- await this.saveSettings(this.settings);
2036
+ current = SettingsSchema.parse({ mode: env.MODE, cloudUrl: env.CLOUD_URL ?? null });
2037
+ await this.saveSettings(current, vault);
1937
2038
  }
1938
2039
  }
1939
- return this.settings;
2040
+ return current;
1940
2041
  }
1941
- async saveSettings(settings) {
1942
- this.settings = settings;
1943
- await this.atomicWrite(SETTINGS_FILE, settings);
2042
+ async saveSettings(settings, vault) {
2043
+ this.settings.set(this.cacheKey(vault), settings);
2044
+ await this.atomicWrite(SETTINGS_FILE, settings, vault);
1944
2045
  }
1945
- async getPrivateSettings() {
1946
- if (!this.settingsPrivate) {
1947
- const raw = await this.readJson(SETTINGS_PRIVATE_FILE);
2046
+ async getPrivateSettings(vault) {
2047
+ const key = this.cacheKey(vault);
2048
+ let current = this.settingsPrivate.get(key);
2049
+ if (!current) {
2050
+ const raw = await this.readJson(SETTINGS_PRIVATE_FILE, vault);
1948
2051
  const parsed = raw ? SettingsPrivateSchema.safeParse(raw) : null;
1949
2052
  if (parsed?.success) {
1950
- this.settingsPrivate = parsed.data;
2053
+ current = parsed.data;
2054
+ this.settingsPrivate.set(key, current);
1951
2055
  } else {
1952
- this.settingsPrivate = SettingsPrivateSchema.parse({});
1953
- await this.savePrivateSettings(this.settingsPrivate);
2056
+ current = SettingsPrivateSchema.parse({});
2057
+ await this.savePrivateSettings(current, vault);
1954
2058
  }
1955
2059
  }
1956
- return this.settingsPrivate;
2060
+ return current;
1957
2061
  }
1958
- async savePrivateSettings(settings) {
1959
- this.settingsPrivate = settings;
1960
- await this.atomicWrite(SETTINGS_PRIVATE_FILE, settings);
2062
+ async savePrivateSettings(settings, vault) {
2063
+ this.settingsPrivate.set(this.cacheKey(vault), settings);
2064
+ await this.atomicWrite(SETTINGS_PRIVATE_FILE, settings, vault);
1961
2065
  }
2066
+ /**
2067
+ * La version de schéma reste au niveau machine, et ce n'est pas un oubli :
2068
+ * elle ne configure rien, elle date les migrations des données de ce poste.
2069
+ */
1962
2070
  async getSchemaVersion() {
1963
2071
  if (!this.schemaVersion) {
1964
- const raw = await this.readJson(SCHEMA_VERSION_FILE);
2072
+ const raw = await this.readMachineJson(SCHEMA_VERSION_FILE);
1965
2073
  const parsed = raw ? SchemaVersionSchema.safeParse(raw) : null;
1966
2074
  if (parsed?.success) {
1967
2075
  this.schemaVersion = parsed.data;
@@ -1974,7 +2082,12 @@ var SettingsManager = class {
1974
2082
  }
1975
2083
  async saveSchemaVersion(sv) {
1976
2084
  this.schemaVersion = sv;
1977
- await this.atomicWrite(SCHEMA_VERSION_FILE, sv);
2085
+ await mkdir6(machineRoot(), { recursive: true });
2086
+ const filePath = machineFilePath(SCHEMA_VERSION_FILE);
2087
+ const tmpPath = filePath + ".tmp";
2088
+ await writeFile6(tmpPath, JSON.stringify(sv, null, 2), "utf-8");
2089
+ await rename6(tmpPath, filePath);
2090
+ await chmod5(filePath, 384);
1978
2091
  }
1979
2092
  getCurrentSchemaVersion() {
1980
2093
  return CURRENT_SCHEMA_VERSION;
@@ -1986,7 +2099,7 @@ var settingsManager = new SettingsManager();
1986
2099
  import { isAbsolute as isAbsolute3 } from "path";
1987
2100
 
1988
2101
  // ../server/src/services/workspace-cwd.ts
1989
- import { isAbsolute as isAbsolute2, join as join5, dirname as dirname3 } from "path";
2102
+ import { isAbsolute as isAbsolute2, join as join7, dirname as dirname3 } from "path";
1990
2103
  async function projectRoot(ref) {
1991
2104
  const project = await projectStore.get(ref);
1992
2105
  if (!project?.orgId) return null;
@@ -2014,7 +2127,7 @@ async function resolveWorkspaceCwd(target) {
2014
2127
  function anchor(base, raw) {
2015
2128
  if (!raw || raw === ".") return base;
2016
2129
  if (raw.startsWith("~") || raw.startsWith("$") || isAbsolute2(raw)) return raw;
2017
- return join5(base, raw);
2130
+ return join7(base, raw);
2018
2131
  }
2019
2132
  function localVaultRootParent() {
2020
2133
  return dirname3(localVaultRoot());
@@ -2272,13 +2385,13 @@ var serviceRouter = router({
2272
2385
  remote: service.git?.remote,
2273
2386
  checkReachability: input.checkReachability
2274
2387
  });
2275
- const { exists: exists2, actualRemote, unreachable, resolvedCwd, unresolvedVars } = agentResult;
2388
+ const { exists: exists4, actualRemote, unreachable, resolvedCwd, unresolvedVars } = agentResult;
2276
2389
  if (unresolvedVars.length > 0) {
2277
2390
  return { exists: false, canClone: false, unresolvedVars, resolvedCwd, gitMismatch: null };
2278
2391
  }
2279
- const canClone = !!service.git?.remote && (!exists2 || actualRemote === null);
2392
+ const canClone = !!service.git?.remote && (!exists4 || actualRemote === null);
2280
2393
  let gitMismatch = null;
2281
- if (exists2 && service.git?.remote && !canClone) {
2394
+ if (exists4 && service.git?.remote && !canClone) {
2282
2395
  if (actualRemote === null) {
2283
2396
  gitMismatch = { actual: null, configured: service.git.remote };
2284
2397
  } else if (actualRemote !== service.git.remote) {
@@ -2287,7 +2400,7 @@ var serviceRouter = router({
2287
2400
  gitMismatch = { actual: actualRemote, configured: service.git.remote, unreachable: true };
2288
2401
  }
2289
2402
  }
2290
- return { exists: exists2, canClone, unresolvedVars: [], resolvedCwd, gitMismatch };
2403
+ return { exists: exists4, canClone, unresolvedVars: [], resolvedCwd, gitMismatch };
2291
2404
  }),
2292
2405
  cloneRepository: protectedProcedure.input(z3.object({ id: z3.string().max(128) })).mutation(async ({ input }) => {
2293
2406
  const service = await serviceStore.get(input.id);
@@ -2376,10 +2489,18 @@ var agentRouter = router({
2376
2489
  z4.object({
2377
2490
  name: z4.string().min(1).max(100),
2378
2491
  url: z4.string().url().max(2048),
2379
- passphrase: z4.string().min(1).max(1024)
2492
+ passphrase: z4.string().min(1).max(1024),
2493
+ // À quelle organisation rattacher l'agent. Absent = la machine, ce que
2494
+ // font l'agent local et tout client antérieur à ce champ.
2495
+ orgId: z4.string().min(1).max(128).nullable().optional()
2380
2496
  })
2381
2497
  ).mutation(async ({ input }) => {
2382
- const config = await agentManager.register(input.name, input.url, input.passphrase);
2498
+ const config = await agentManager.register(
2499
+ input.name,
2500
+ input.url,
2501
+ input.passphrase,
2502
+ input.orgId ?? null
2503
+ );
2383
2504
  return { ...config, passphrase: "***" };
2384
2505
  }),
2385
2506
  /** Update an existing agent */
@@ -3120,11 +3241,29 @@ var environmentRouter = router({
3120
3241
  import { promises as dns } from "dns";
3121
3242
  import { z as z9 } from "zod";
3122
3243
  import { TRPCError as TRPCError7 } from "@trpc/server";
3244
+
3245
+ // ../server/src/services/settings-scope.ts
3246
+ async function vaultOfProject(projectId) {
3247
+ if (!projectId) return void 0;
3248
+ await ensureProjectsLoaded();
3249
+ const refs = refsOfProjectId(projectId);
3250
+ if (refs.length !== 1) return void 0;
3251
+ return refs[0]?.orgId ?? void 0;
3252
+ }
3253
+
3254
+ // ../server/src/trpc/routers/settings.ts
3123
3255
  var NotificationPreferencesPatchSchema = NotificationPreferencesSchema.partial().strict();
3124
3256
  function maskApiKey2(plaintext) {
3125
3257
  if (plaintext.length <= 8) return "****";
3126
3258
  return plaintext.slice(0, 4) + "****" + plaintext.slice(-4);
3127
3259
  }
3260
+ var ScopeShape = {
3261
+ orgId: z9.string().max(128).nullable().optional(),
3262
+ projectId: z9.string().max(128).optional()
3263
+ };
3264
+ async function scopeOf(input) {
3265
+ return input?.orgId ?? await vaultOfProject(input?.projectId);
3266
+ }
3128
3267
  var settingsRouter = router({
3129
3268
  /**
3130
3269
  * Get all settings
@@ -3162,15 +3301,16 @@ var settingsRouter = router({
3162
3301
  /**
3163
3302
  * Get notification preferences
3164
3303
  */
3165
- getNotificationPreferences: protectedProcedure.query(async () => {
3166
- const priv = await settingsManager.getPrivateSettings();
3304
+ getNotificationPreferences: protectedProcedure.input(z9.object(ScopeShape).optional()).query(async ({ input }) => {
3305
+ const priv = await settingsManager.getPrivateSettings(await scopeOf(input));
3167
3306
  return NotificationPreferencesSchema.parse(priv.notifications ?? {});
3168
3307
  }),
3169
3308
  /**
3170
3309
  * Update notification preferences (granular mute)
3171
3310
  */
3172
- updateNotificationPreferences: protectedProcedure.input(NotificationPreferencesPatchSchema).mutation(async ({ input }) => {
3173
- const priv = await settingsManager.getPrivateSettings();
3311
+ updateNotificationPreferences: protectedProcedure.input(NotificationPreferencesPatchSchema.extend(ScopeShape)).mutation(async ({ input }) => {
3312
+ const vault = await scopeOf(input);
3313
+ const priv = await settingsManager.getPrivateSettings(vault);
3174
3314
  const currentPrefs = NotificationPreferencesSchema.parse(priv.notifications ?? {});
3175
3315
  const updatedPrefs = {
3176
3316
  ...currentPrefs,
@@ -3179,7 +3319,7 @@ var settingsRouter = router({
3179
3319
  await settingsManager.savePrivateSettings({
3180
3320
  ...priv,
3181
3321
  notifications: updatedPrefs
3182
- });
3322
+ }, vault);
3183
3323
  return updatedPrefs;
3184
3324
  }),
3185
3325
  /**
@@ -3290,13 +3430,14 @@ var settingsRouter = router({
3290
3430
  /**
3291
3431
  * List AI providers (API keys are masked)
3292
3432
  */
3293
- listAIProviders: protectedProcedure.query(async () => {
3294
- const priv = await settingsManager.getPrivateSettings();
3433
+ listAIProviders: protectedProcedure.input(z9.object(ScopeShape).optional()).query(async ({ input }) => {
3434
+ const vault = await scopeOf(input);
3435
+ const priv = await settingsManager.getPrivateSettings(vault);
3295
3436
  return (priv.aiProviders ?? []).map((p) => {
3296
3437
  let maskedKey = "(aucune)";
3297
3438
  if (p.encryptedApiKey) {
3298
3439
  try {
3299
- maskedKey = maskApiKey2(decryptValue(p.encryptedApiKey));
3440
+ maskedKey = maskApiKey2(decryptValue(p.encryptedApiKey, vault));
3300
3441
  } catch {
3301
3442
  maskedKey = "(cl\xE9 invalide)";
3302
3443
  }
@@ -3312,12 +3453,14 @@ var settingsRouter = router({
3312
3453
  provider: AIProviderSchema.shape.provider,
3313
3454
  apiKey: z9.string().max(500).optional(),
3314
3455
  // optional for providers like Ollama
3315
- model: z9.string().max(200).optional()
3456
+ model: z9.string().max(200).optional(),
3457
+ ...ScopeShape
3316
3458
  })).mutation(async ({ input }) => {
3317
3459
  if (input.provider !== "ollama" && !input.apiKey?.trim()) {
3318
3460
  throw new TRPCError7({ code: "BAD_REQUEST", message: "Une cl\xE9 API est requise pour ce provider." });
3319
3461
  }
3320
- const priv = await settingsManager.getPrivateSettings();
3462
+ const vault = await scopeOf(input);
3463
+ const priv = await settingsManager.getPrivateSettings(vault);
3321
3464
  const id = `aiprovider-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
3322
3465
  const now = /* @__PURE__ */ new Date();
3323
3466
  const plainKey = input.apiKey ?? "";
@@ -3325,7 +3468,9 @@ var settingsRouter = router({
3325
3468
  id,
3326
3469
  name: input.name,
3327
3470
  provider: input.provider,
3328
- encryptedApiKey: plainKey ? encryptValue(plainKey) : "",
3471
+ // Chiffrée par la clé de l'organisation : c'est ce qui permet de
3472
+ // partager le fournisseur qu'elle paie sans partager la clé du poste.
3473
+ encryptedApiKey: plainKey ? encryptValue(plainKey, vault) : "",
3329
3474
  model: input.model,
3330
3475
  active: (priv.aiProviders ?? []).length === 0,
3331
3476
  // first one is active by default
@@ -3335,7 +3480,7 @@ var settingsRouter = router({
3335
3480
  await settingsManager.savePrivateSettings({
3336
3481
  ...priv,
3337
3482
  aiProviders: [...priv.aiProviders ?? [], newProvider]
3338
- });
3483
+ }, vault);
3339
3484
  return { ...newProvider, maskedKey: plainKey ? maskApiKey2(plainKey) : "(aucune)", encryptedApiKey: void 0 };
3340
3485
  }),
3341
3486
  /**
@@ -3346,14 +3491,16 @@ var settingsRouter = router({
3346
3491
  name: z9.string().min(1).max(200).optional(),
3347
3492
  provider: AIProviderSchema.shape.provider.optional(),
3348
3493
  apiKey: z9.string().min(1).max(500).optional(),
3349
- model: z9.string().max(200).optional()
3494
+ model: z9.string().max(200).optional(),
3495
+ ...ScopeShape
3350
3496
  })).mutation(async ({ input }) => {
3351
- const priv = await settingsManager.getPrivateSettings();
3497
+ const vault = await scopeOf(input);
3498
+ const priv = await settingsManager.getPrivateSettings(vault);
3352
3499
  const providers = priv.aiProviders ?? [];
3353
3500
  const idx = providers.findIndex((p) => p.id === input.id);
3354
3501
  if (idx === -1) throw new TRPCError7({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
3355
3502
  const existing = providers[idx];
3356
- const encryptedApiKey = input.apiKey ? stableEncryptValue(input.apiKey, existing.encryptedApiKey) : existing.encryptedApiKey;
3503
+ const encryptedApiKey = input.apiKey ? stableEncryptValue(input.apiKey, existing.encryptedApiKey, vault) : existing.encryptedApiKey;
3357
3504
  const updated = {
3358
3505
  ...existing,
3359
3506
  ...input.name ? { name: input.name } : {},
@@ -3364,11 +3511,11 @@ var settingsRouter = router({
3364
3511
  };
3365
3512
  const newProviders = [...providers];
3366
3513
  newProviders[idx] = updated;
3367
- await settingsManager.savePrivateSettings({ ...priv, aiProviders: newProviders });
3514
+ await settingsManager.savePrivateSettings({ ...priv, aiProviders: newProviders }, vault);
3368
3515
  let maskedKey = "(aucune)";
3369
3516
  if (updated.encryptedApiKey) {
3370
3517
  try {
3371
- maskedKey = maskApiKey2(decryptValue(updated.encryptedApiKey));
3518
+ maskedKey = maskApiKey2(decryptValue(updated.encryptedApiKey, vault));
3372
3519
  } catch {
3373
3520
  }
3374
3521
  }
@@ -3377,26 +3524,28 @@ var settingsRouter = router({
3377
3524
  /**
3378
3525
  * Delete AI provider
3379
3526
  */
3380
- deleteAIProvider: protectedProcedure.input(z9.object({ id: z9.string().min(1).max(128) })).mutation(async ({ input }) => {
3381
- const priv = await settingsManager.getPrivateSettings();
3527
+ deleteAIProvider: protectedProcedure.input(z9.object({ id: z9.string().min(1).max(128), ...ScopeShape })).mutation(async ({ input }) => {
3528
+ const vault = await scopeOf(input);
3529
+ const priv = await settingsManager.getPrivateSettings(vault);
3382
3530
  const providers = priv.aiProviders ?? [];
3383
3531
  const filtered = providers.filter((p) => p.id !== input.id);
3384
3532
  if (filtered.length === providers.length) throw new TRPCError7({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
3385
3533
  const deletedWasActive = providers.find((p) => p.id === input.id)?.active ?? false;
3386
3534
  if (deletedWasActive && filtered.length > 0) filtered[0].active = true;
3387
- await settingsManager.savePrivateSettings({ ...priv, aiProviders: filtered });
3535
+ await settingsManager.savePrivateSettings({ ...priv, aiProviders: filtered }, vault);
3388
3536
  return { deleted: true };
3389
3537
  }),
3390
3538
  /**
3391
3539
  * Set one provider as active (clears active flag on all others)
3392
3540
  */
3393
- setActiveAIProvider: protectedProcedure.input(z9.object({ id: z9.string().min(1).max(128) })).mutation(async ({ input }) => {
3394
- const priv = await settingsManager.getPrivateSettings();
3541
+ setActiveAIProvider: protectedProcedure.input(z9.object({ id: z9.string().min(1).max(128), ...ScopeShape })).mutation(async ({ input }) => {
3542
+ const vault = await scopeOf(input);
3543
+ const priv = await settingsManager.getPrivateSettings(vault);
3395
3544
  const providers = priv.aiProviders ?? [];
3396
3545
  const idx = providers.findIndex((p) => p.id === input.id);
3397
3546
  if (idx === -1) throw new TRPCError7({ code: "NOT_FOUND", message: `AI provider not found: ${input.id}` });
3398
3547
  const updated = providers.map((p) => ({ ...p, active: p.id === input.id }));
3399
- await settingsManager.savePrivateSettings({ ...priv, aiProviders: updated });
3548
+ await settingsManager.savePrivateSettings({ ...priv, aiProviders: updated }, vault);
3400
3549
  return { id: input.id, active: true };
3401
3550
  }),
3402
3551
  // ---------------------------------------------------------------------------
@@ -3693,15 +3842,15 @@ import { z as z11 } from "zod";
3693
3842
  import { TRPCError as TRPCError8 } from "@trpc/server";
3694
3843
  import { execFile as execFile2, spawn } from "child_process";
3695
3844
  import { stat as stat3 } from "fs/promises";
3696
- import { resolve as resolve2, join as join7 } from "path";
3845
+ import { resolve as resolve2, join as join9 } from "path";
3697
3846
  import { homedir as homedir2 } from "os";
3698
3847
 
3699
3848
  // ../server/src/services/user-config-manager.ts
3700
3849
  import { readFile as readFile7, writeFile as writeFile7, rename as rename7, mkdir as mkdir7, chmod as chmod6 } from "fs/promises";
3701
- import { join as join6 } from "path";
3850
+ import { join as join8 } from "path";
3702
3851
  import { homedir } from "os";
3703
3852
  import { z as z10 } from "zod";
3704
- var CONFIG_DIR = join6(homedir(), ".runeya");
3853
+ var CONFIG_DIR = join8(homedir(), ".runeya");
3705
3854
  var CONFIG_FILE = "runeya.conf";
3706
3855
  var EditorPreferencesSchema = z10.object({
3707
3856
  default: z10.string().max(64).optional()
@@ -3725,7 +3874,7 @@ var UserConfigManager = class {
3725
3874
  }
3726
3875
  async atomicWrite(data) {
3727
3876
  await this.ensureDir();
3728
- const filePath = join6(CONFIG_DIR, CONFIG_FILE);
3877
+ const filePath = join8(CONFIG_DIR, CONFIG_FILE);
3729
3878
  const tmpPath = filePath + ".tmp";
3730
3879
  await writeFile7(tmpPath, JSON.stringify(data, null, 2), "utf-8");
3731
3880
  await rename7(tmpPath, filePath);
@@ -3734,7 +3883,7 @@ var UserConfigManager = class {
3734
3883
  async getConfig() {
3735
3884
  if (!this.config) {
3736
3885
  try {
3737
- const raw = await readFile7(join6(CONFIG_DIR, CONFIG_FILE), "utf-8");
3886
+ const raw = await readFile7(join8(CONFIG_DIR, CONFIG_FILE), "utf-8");
3738
3887
  const parsed = UserConfigSchema.safeParse(JSON.parse(raw));
3739
3888
  this.config = parsed.success ? parsed.data : {};
3740
3889
  } catch {
@@ -3887,7 +4036,7 @@ var editorRouter = router({
3887
4036
  throw new TRPCError8({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
3888
4037
  }
3889
4038
  const cwd = await getServiceCwd(input.serviceId);
3890
- const filePath = join7(cwd, input.relativePath);
4039
+ const filePath = join9(cwd, input.relativePath);
3891
4040
  if (!filePath.startsWith(cwd)) {
3892
4041
  throw new TRPCError8({ code: "BAD_REQUEST", message: "Path traversal not allowed" });
3893
4042
  }
@@ -3911,7 +4060,7 @@ var editorRouter = router({
3911
4060
  import { z as z12 } from "zod";
3912
4061
  import { TRPCError as TRPCError9 } from "@trpc/server";
3913
4062
  import { mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
3914
- import { join as join8, dirname as dirname4 } from "path";
4063
+ import { join as join10, dirname as dirname4 } from "path";
3915
4064
  var ALLOWED_SKILL_PATHS = /* @__PURE__ */ new Set([
3916
4065
  ".claude/skills/runeya/SKILL.md",
3917
4066
  ".cursor/rules/runeya.mdc",
@@ -3924,8 +4073,8 @@ var KeyInputSchema = z12.object({
3924
4073
  });
3925
4074
  var apiKeysRouter = router({
3926
4075
  list: protectedProcedure.query(() => apiKeyStore.list()),
3927
- create: protectedProcedure.input(KeyInputSchema).mutation(
3928
- ({ input }) => apiKeyStore.create(input.name, input.scopes, input.isPublic)
4076
+ create: protectedProcedure.input(KeyInputSchema.extend({ orgId: z12.string().min(1).max(128).nullable().optional() })).mutation(
4077
+ ({ input }) => apiKeyStore.create(input.name, input.scopes, input.isPublic, input.orgId ?? null)
3929
4078
  ),
3930
4079
  /**
3931
4080
  * Renvoie le secret d'une seule clé, sur demande explicite. `list` ne le
@@ -3958,7 +4107,7 @@ var apiKeysRouter = router({
3958
4107
  if (!ALLOWED_SKILL_PATHS.has(input.relativePath)) {
3959
4108
  throw new TRPCError9({ code: "BAD_REQUEST", message: "Chemin non autoris\xE9" });
3960
4109
  }
3961
- const abs = join8(await resolveWorkspaceCwd({}), input.relativePath);
4110
+ const abs = join10(await resolveWorkspaceCwd({}), input.relativePath);
3962
4111
  await mkdir8(dirname4(abs), { recursive: true });
3963
4112
  await writeFile8(abs, input.content, { encoding: "utf-8", mode: 420 });
3964
4113
  return { path: input.relativePath };
@@ -4289,12 +4438,12 @@ function buildApiDocs(apiBaseUrl, apiToken) {
4289
4438
  import { readdir as readdir2, access, readFile as readFile9 } from "fs/promises";
4290
4439
  import { watch } from "fs";
4291
4440
  import { homedir as homedir3 } from "os";
4292
- import { join as join10 } from "path";
4441
+ import { join as join12 } from "path";
4293
4442
 
4294
4443
  // ../server/src/services/conversation-store.ts
4295
4444
  import { randomUUID as randomUUID3 } from "crypto";
4296
4445
  import { readFile as readFile8, writeFile as writeFile9, rename as rename8, mkdir as mkdir9, chmod as chmod7 } from "fs/promises";
4297
- import { dirname as dirname5, join as join9 } from "path";
4446
+ import { dirname as dirname5, join as join11 } from "path";
4298
4447
  import { EventEmitter as EventEmitter2 } from "events";
4299
4448
  var FILENAME4 = "conversations.private.json";
4300
4449
  async function localProjectId() {
@@ -4338,19 +4487,19 @@ var ConversationStore = class {
4338
4487
  async ensureDir() {
4339
4488
  await mkdir9(dirname5(await this.getFilePath()), { recursive: true });
4340
4489
  }
4341
- encryptMessages(messages) {
4342
- return encryptValue(JSON.stringify(messages));
4490
+ encryptMessages(messages, vault) {
4491
+ return encryptValue(JSON.stringify(messages), vault);
4343
4492
  }
4344
- decryptMessages(encrypted) {
4345
- const json = decryptValue(encrypted);
4493
+ decryptMessages(encrypted, vault) {
4494
+ const json = decryptValue(encrypted, vault);
4346
4495
  return JSON.parse(json);
4347
4496
  }
4348
- toDisk(conv) {
4497
+ toDisk(conv, vault) {
4349
4498
  return {
4350
4499
  id: conv.id,
4351
4500
  projectId: conv.projectId,
4352
4501
  title: conv.title,
4353
- encryptedMessages: this.encryptMessages(conv.messages),
4502
+ encryptedMessages: this.encryptMessages(conv.messages, vault),
4354
4503
  isCli: conv.isCli,
4355
4504
  runner: conv.runner,
4356
4505
  providerId: conv.providerId,
@@ -4365,13 +4514,13 @@ var ConversationStore = class {
4365
4514
  updatedAt: conv.updatedAt
4366
4515
  };
4367
4516
  }
4368
- fromDisk(disk) {
4517
+ fromDisk(disk, vault) {
4369
4518
  const workflowType = (disk.workflowType === "scenario" ? "scenario" : void 0) ?? (disk.scenarioId ? "scenario" : void 0);
4370
4519
  return {
4371
4520
  id: disk.id,
4372
4521
  projectId: disk.projectId,
4373
4522
  title: disk.title,
4374
- messages: this.decryptMessages(disk.encryptedMessages),
4523
+ messages: this.decryptMessages(disk.encryptedMessages, vault),
4375
4524
  isCli: disk.isCli ?? disk.isClaudeCode ?? false,
4376
4525
  runner: disk.runner,
4377
4526
  providerId: disk.providerId,
@@ -4405,7 +4554,7 @@ var ConversationStore = class {
4405
4554
  const legacyPaths = [this.getLegacyFilePath(), await keyedFilePath(FILENAME4)];
4406
4555
  const sources = [
4407
4556
  ...legacyPaths.map((path) => ({ path, legacy: true })),
4408
- ...vaults.map((root) => ({ path: join9(root, FILENAME4), legacy: false }))
4557
+ ...vaults.map((root) => ({ path: join11(root, FILENAME4), legacy: false }))
4409
4558
  ];
4410
4559
  for (const { path, legacy } of sources) {
4411
4560
  let data;
@@ -4417,7 +4566,7 @@ var ConversationStore = class {
4417
4566
  const inherited = legacy ? await localProjectId() : null;
4418
4567
  for (const item of data) {
4419
4568
  try {
4420
- const conv = this.fromDisk(item);
4569
+ const conv = this.fromDisk(item, vaultIdOfRoot(dirname5(path)));
4421
4570
  if (!conv.projectId && inherited) conv.projectId = inherited;
4422
4571
  this.conversations.set(item.id, conv);
4423
4572
  this.roots.set(item.id, dirname5(path));
@@ -4441,12 +4590,12 @@ var ConversationStore = class {
4441
4590
  const root = await this.rootFor(conv);
4442
4591
  this.roots.set(conv.id, root);
4443
4592
  const bucket = byRoot.get(root) ?? [];
4444
- bucket.push(this.toDisk(conv));
4593
+ bucket.push(this.toDisk(conv, vaultIdOfRoot(root)));
4445
4594
  byRoot.set(root, bucket);
4446
4595
  }
4447
4596
  for (const [root, data] of byRoot) {
4448
4597
  await mkdir9(root, { recursive: true });
4449
- const filePath = join9(root, FILENAME4);
4598
+ const filePath = join11(root, FILENAME4);
4450
4599
  const tmpPath = `${filePath}.tmp`;
4451
4600
  await writeFile9(tmpPath, JSON.stringify(data, null, 2), "utf-8");
4452
4601
  await rename8(tmpPath, filePath);
@@ -4477,6 +4626,16 @@ var ConversationStore = class {
4477
4626
  const ref = refOfProjectAmong(conv.projectId, known);
4478
4627
  return { id: ref.id, orgId: ref.orgId ?? null };
4479
4628
  }
4629
+ /**
4630
+ * Réécrire chaque coffre, en déplaçant ce qui appartient désormais ailleurs.
4631
+ *
4632
+ * Appelé quand un projet a changé de coffre : ses conversations sont écrites
4633
+ * dans le nouveau, et le fichier d'où elles viennent est réécrit sans elles.
4634
+ */
4635
+ async relocateAll() {
4636
+ await this.load();
4637
+ await this.save();
4638
+ }
4480
4639
  async save() {
4481
4640
  const write = this.saveQueue.then(() => this.saveInternal());
4482
4641
  this.saveQueue = write.catch(() => {
@@ -4487,6 +4646,39 @@ var ConversationStore = class {
4487
4646
  await this.load();
4488
4647
  return Array.from(this.conversations.values()).sort((a, b) => b.updatedAt - a.updatedAt);
4489
4648
  }
4649
+ /**
4650
+ * Les conversations d'un projet.
4651
+ *
4652
+ * Celle qui ne nomme aucun projet — écrite avant le champ, ou qu'une
4653
+ * migration n'a pas su attribuer — n'est rendue que dans les projets de son
4654
+ * propre coffre : elle reste atteignable là où elle a été écrite, sans se
4655
+ * déverser dans toutes les organisations de la machine.
4656
+ */
4657
+ async listForProject(ref) {
4658
+ const belongs = await this.matcherForProject(ref);
4659
+ return (await this.list()).filter(belongs);
4660
+ }
4661
+ /**
4662
+ * Le test d'appartenance à un projet, préparé une fois.
4663
+ *
4664
+ * Le flux temps réel en a besoin autant que la liste : une conversation qui
4665
+ * arrive par événement doit passer le même filtre, sans quoi elle se
4666
+ * réinvite dans un projet qui ne l'a jamais demandée.
4667
+ */
4668
+ async matcherForProject(ref) {
4669
+ await this.load();
4670
+ let projectRoot2 = null;
4671
+ try {
4672
+ projectRoot2 = ref.orgId === void 0 ? rootOfProjectAmong(ref.id, null) : rootOfProjectRef({ id: ref.id, orgId: ref.orgId ?? null });
4673
+ } catch {
4674
+ projectRoot2 = null;
4675
+ }
4676
+ return (conv) => {
4677
+ if (conv.projectId) return conv.projectId === ref.id;
4678
+ if (!projectRoot2) return true;
4679
+ return this.roots.get(conv.id) === projectRoot2;
4680
+ };
4681
+ }
4490
4682
  async get(id) {
4491
4683
  await this.load();
4492
4684
  return this.conversations.get(id) ?? null;
@@ -4575,7 +4767,7 @@ var ConversationStore = class {
4575
4767
  var conversationStore = new ConversationStore();
4576
4768
 
4577
4769
  // ../server/src/services/claude-transcript-sync.ts
4578
- var PROJECTS_DIR = join10(homedir3(), ".claude", "projects");
4770
+ var PROJECTS_DIR = join12(homedir3(), ".claude", "projects");
4579
4771
  var DEFAULT_TITLES = /* @__PURE__ */ new Set(["", "New conversation", "Nouvelle conversation"]);
4580
4772
  async function findClaudeSessionFile(sessionId) {
4581
4773
  let entries;
@@ -4586,7 +4778,7 @@ async function findClaudeSessionFile(sessionId) {
4586
4778
  }
4587
4779
  const filename = `${sessionId}.jsonl`;
4588
4780
  for (const dir of entries) {
4589
- const candidate = join10(PROJECTS_DIR, dir, filename);
4781
+ const candidate = join12(PROJECTS_DIR, dir, filename);
4590
4782
  try {
4591
4783
  await access(candidate);
4592
4784
  return candidate;
@@ -4749,7 +4941,7 @@ async function startClaudeTranscriptWatcher() {
4749
4941
  }
4750
4942
  for (const dir of topLevel) {
4751
4943
  try {
4752
- const w = watch(join10(PROJECTS_DIR, dir), (_event, filename) => {
4944
+ const w = watch(join12(PROJECTS_DIR, dir), (_event, filename) => {
4753
4945
  handleFsEvent(filename ? filename.toString() : null);
4754
4946
  });
4755
4947
  w.on("error", () => {
@@ -4775,8 +4967,8 @@ function stopClaudeTranscriptWatcher() {
4775
4967
  // ../server/src/services/codex-sessions.ts
4776
4968
  import { readdir as readdir3 } from "fs/promises";
4777
4969
  import { homedir as homedir4 } from "os";
4778
- import { join as join11 } from "path";
4779
- var SESSIONS_DIR = join11(homedir4(), ".codex", "sessions");
4970
+ import { join as join13 } from "path";
4971
+ var SESSIONS_DIR = join13(homedir4(), ".codex", "sessions");
4780
4972
  async function findCodexSessionFile(sessionId) {
4781
4973
  const suffix = `-${sessionId}.jsonl`;
4782
4974
  async function walk(dir) {
@@ -4787,7 +4979,7 @@ async function findCodexSessionFile(sessionId) {
4787
4979
  return null;
4788
4980
  }
4789
4981
  for (const entry of entries) {
4790
- const full = join11(dir, entry.name);
4982
+ const full = join13(dir, entry.name);
4791
4983
  if (entry.isDirectory()) {
4792
4984
  const found = await walk(full);
4793
4985
  if (found) return found;
@@ -5607,34 +5799,34 @@ function normalizeNode(raw) {
5607
5799
  };
5608
5800
  }
5609
5801
  var ScenarioStore = class {
5610
- async list() {
5802
+ async list(vault) {
5611
5803
  const [settings, priv] = await Promise.all([
5612
- settingsManager.getSettings(),
5613
- settingsManager.getPrivateSettings()
5804
+ settingsManager.getSettings(vault),
5805
+ settingsManager.getPrivateSettings(vault)
5614
5806
  ]);
5615
5807
  return [...settings.scenarios ?? [], ...priv.scenarios ?? []];
5616
5808
  }
5617
- async get(id) {
5618
- const list = await this.list();
5809
+ async get(id, vault) {
5810
+ const list = await this.list(vault);
5619
5811
  return list.find((e) => e.id === id) ?? null;
5620
5812
  }
5621
- async create(scenario) {
5813
+ async create(scenario, vault) {
5622
5814
  const normalized = this.normalizeScenario(scenario);
5623
5815
  const parsed = ScenarioSchema.parse(normalized);
5624
5816
  this.validateHierarchy(parsed);
5625
5817
  if (parsed.isPublished) {
5626
- const settings = await settingsManager.getSettings();
5627
- await settingsManager.saveSettings({ ...settings, scenarios: [...settings.scenarios ?? [], parsed] });
5818
+ const settings = await settingsManager.getSettings(vault);
5819
+ await settingsManager.saveSettings({ ...settings, scenarios: [...settings.scenarios ?? [], parsed] }, vault);
5628
5820
  } else {
5629
- const priv = await settingsManager.getPrivateSettings();
5630
- await settingsManager.savePrivateSettings({ ...priv, scenarios: [...priv.scenarios ?? [], parsed] });
5821
+ const priv = await settingsManager.getPrivateSettings(vault);
5822
+ await settingsManager.savePrivateSettings({ ...priv, scenarios: [...priv.scenarios ?? [], parsed] }, vault);
5631
5823
  }
5632
5824
  return parsed;
5633
5825
  }
5634
- async update(id, patch) {
5826
+ async update(id, patch, vault) {
5635
5827
  const [settings, priv] = await Promise.all([
5636
- settingsManager.getSettings(),
5637
- settingsManager.getPrivateSettings()
5828
+ settingsManager.getSettings(vault),
5829
+ settingsManager.getPrivateSettings(vault)
5638
5830
  ]);
5639
5831
  const publicScenarios = [...settings.scenarios ?? []];
5640
5832
  const privateScenarios = [...priv.scenarios ?? []];
@@ -5650,29 +5842,29 @@ var ScenarioStore = class {
5650
5842
  const isNowPublic = parsed.isPublished;
5651
5843
  if (wasPublic && isNowPublic) {
5652
5844
  publicScenarios[publicIdx] = parsed;
5653
- await settingsManager.saveSettings({ ...settings, scenarios: publicScenarios });
5845
+ await settingsManager.saveSettings({ ...settings, scenarios: publicScenarios }, vault);
5654
5846
  } else if (!wasPublic && !isNowPublic) {
5655
5847
  privateScenarios[privateIdx] = parsed;
5656
- await settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios });
5848
+ await settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios }, vault);
5657
5849
  } else if (wasPublic && !isNowPublic) {
5658
5850
  publicScenarios.splice(publicIdx, 1);
5659
5851
  await Promise.all([
5660
- settingsManager.saveSettings({ ...settings, scenarios: publicScenarios }),
5661
- settingsManager.savePrivateSettings({ ...priv, scenarios: [...privateScenarios, parsed] })
5852
+ settingsManager.saveSettings({ ...settings, scenarios: publicScenarios }, vault),
5853
+ settingsManager.savePrivateSettings({ ...priv, scenarios: [...privateScenarios, parsed] }, vault)
5662
5854
  ]);
5663
5855
  } else {
5664
5856
  privateScenarios.splice(privateIdx, 1);
5665
5857
  await Promise.all([
5666
- settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios }),
5667
- settingsManager.saveSettings({ ...settings, scenarios: [...publicScenarios, parsed] })
5858
+ settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios }, vault),
5859
+ settingsManager.saveSettings({ ...settings, scenarios: [...publicScenarios, parsed] }, vault)
5668
5860
  ]);
5669
5861
  }
5670
5862
  return parsed;
5671
5863
  }
5672
- async delete(id) {
5864
+ async delete(id, vault) {
5673
5865
  const [settings, priv] = await Promise.all([
5674
- settingsManager.getSettings(),
5675
- settingsManager.getPrivateSettings()
5866
+ settingsManager.getSettings(vault),
5867
+ settingsManager.getPrivateSettings(vault)
5676
5868
  ]);
5677
5869
  const publicFiltered = (settings.scenarios ?? []).filter((e) => e.id !== id);
5678
5870
  const privateFiltered = (priv.scenarios ?? []).filter((e) => e.id !== id);
@@ -5680,8 +5872,8 @@ var ScenarioStore = class {
5680
5872
  const removedFromPrivate = privateFiltered.length < (priv.scenarios ?? []).length;
5681
5873
  if (!removedFromPublic && !removedFromPrivate) return false;
5682
5874
  await Promise.all([
5683
- removedFromPublic ? settingsManager.saveSettings({ ...settings, scenarios: publicFiltered }) : Promise.resolve(),
5684
- removedFromPrivate ? settingsManager.savePrivateSettings({ ...priv, scenarios: privateFiltered }) : Promise.resolve()
5875
+ removedFromPublic ? settingsManager.saveSettings({ ...settings, scenarios: publicFiltered }, vault) : Promise.resolve(),
5876
+ removedFromPrivate ? settingsManager.savePrivateSettings({ ...priv, scenarios: privateFiltered }, vault) : Promise.resolve()
5685
5877
  ]);
5686
5878
  return true;
5687
5879
  }
@@ -5748,7 +5940,7 @@ var scenarioStore = new ScenarioStore();
5748
5940
 
5749
5941
  // ../server/src/services/scenario-run-store.ts
5750
5942
  import { mkdir as mkdir11, rm as rm2, readFile as readFile11 } from "fs/promises";
5751
- import { join as join12 } from "path";
5943
+ import { join as join14 } from "path";
5752
5944
 
5753
5945
  // ../server/src/services/run-store-utils.ts
5754
5946
  import { chmod as chmod8, mkdir as mkdir10, rename as rename9, writeFile as writeFile10, readFile as readFile10 } from "fs/promises";
@@ -5825,8 +6017,8 @@ var ScenarioRunStore = class _ScenarioRunStore {
5825
6017
  "- Use read-only references from linked upstream files as context.",
5826
6018
  ""
5827
6019
  ].join("\n");
5828
- await atomicWriteSecure(join12(dir, "README.md"), overview);
5829
- await atomicWriteSecure(join12(dir, "USER_REQUEST.md"), `# User request
6020
+ await atomicWriteSecure(join14(dir, "README.md"), overview);
6021
+ await atomicWriteSecure(join14(dir, "USER_REQUEST.md"), `# User request
5830
6022
 
5831
6023
  ${userInput.trim() || "(empty)"}
5832
6024
  `);
@@ -5852,7 +6044,7 @@ ${userInput.trim() || "(empty)"}
5852
6044
  "- Waiting for assignment.",
5853
6045
  ""
5854
6046
  ].join("\n");
5855
- await atomicWriteSecure(join12(dir, fileName), content);
6047
+ await atomicWriteSecure(join14(dir, fileName), content);
5856
6048
  }
5857
6049
  }
5858
6050
  async deleteRunDir(conversationId) {
@@ -5920,18 +6112,18 @@ ${userInput.trim() || "(empty)"}
5920
6112
  ...Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {},
5921
6113
  ...Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}
5922
6114
  };
5923
- await atomicWriteJsonSecure(join12(dir, "run-state.json"), data);
6115
+ await atomicWriteJsonSecure(join14(dir, "run-state.json"), data);
5924
6116
  } catch {
5925
6117
  }
5926
6118
  }
5927
6119
  async loadPersistedRunState(conversationId) {
5928
6120
  const dir = this.getRunDir(conversationId);
5929
- return readJsonFileSafe(join12(dir, "run-state.json"));
6121
+ return readJsonFileSafe(join14(dir, "run-state.json"));
5930
6122
  }
5931
6123
  async readUserRequest(conversationId) {
5932
6124
  try {
5933
6125
  const dir = this.getRunDir(conversationId);
5934
- const content = await readFile11(join12(dir, "USER_REQUEST.md"), "utf-8");
6126
+ const content = await readFile11(join14(dir, "USER_REQUEST.md"), "utf-8");
5935
6127
  return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
5936
6128
  } catch {
5937
6129
  return null;
@@ -5940,7 +6132,7 @@ ${userInput.trim() || "(empty)"}
5940
6132
  async readReadme(conversationId) {
5941
6133
  try {
5942
6134
  const dir = this.getRunDir(conversationId);
5943
- return await readFile11(join12(dir, "README.md"), "utf-8");
6135
+ return await readFile11(join14(dir, "README.md"), "utf-8");
5944
6136
  } catch {
5945
6137
  return null;
5946
6138
  }
@@ -5949,14 +6141,14 @@ ${userInput.trim() || "(empty)"}
5949
6141
  const dir = this.getRunDir(conversationId);
5950
6142
  await mkdir11(dir, { recursive: true });
5951
6143
  await atomicWriteJsonSecure(
5952
- join12(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
6144
+ join14(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
5953
6145
  scenario,
5954
6146
  true
5955
6147
  );
5956
6148
  }
5957
6149
  async loadScenarioSnapshot(conversationId) {
5958
6150
  const dir = this.getRunDir(conversationId);
5959
- return readJsonFileSafe(join12(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
6151
+ return readJsonFileSafe(join14(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
5960
6152
  }
5961
6153
  };
5962
6154
  var scenarioRunStore = new ScenarioRunStore();
@@ -5977,12 +6169,12 @@ var DEFAULT_MODELS = {
5977
6169
  mistral: "mistral-large-latest",
5978
6170
  ollama: "llama3"
5979
6171
  };
5980
- async function getActiveProvider(providerId) {
5981
- const priv = await settingsManager.getPrivateSettings();
6172
+ async function getActiveProvider(providerId, vault) {
6173
+ const priv = await settingsManager.getPrivateSettings(vault);
5982
6174
  const providers = priv.aiProviders ?? [];
5983
6175
  const selected = providerId ? providers.find((p) => p.id === providerId) : providers.find((p) => p.active);
5984
6176
  if (!selected) return null;
5985
- const plaintextKey = selected.encryptedApiKey ? decryptValue(selected.encryptedApiKey) : "";
6177
+ const plaintextKey = selected.encryptedApiKey ? decryptValue(selected.encryptedApiKey, vault) : "";
5986
6178
  return { provider: selected, plaintextKey };
5987
6179
  }
5988
6180
  async function fetchProviderModels(provider, plaintextKey) {
@@ -6004,7 +6196,7 @@ async function fetchProviderModels(provider, plaintextKey) {
6004
6196
  import { randomUUID as randomUUID4 } from "crypto";
6005
6197
  import { mkdir as mkdir12, writeFile as writeFile11, readFile as readFile12, unlink as unlink2, readdir as readdir4 } from "fs/promises";
6006
6198
  import { existsSync } from "fs";
6007
- import { join as join13, extname, resolve as resolve4, sep as sep2 } from "path";
6199
+ import { join as join15, extname, resolve as resolve4, sep as sep2 } from "path";
6008
6200
  var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
6009
6201
  "image/png",
6010
6202
  "image/jpeg",
@@ -6041,7 +6233,7 @@ var ImageStorageService = class {
6041
6233
  const imageId = randomUUID4();
6042
6234
  const safeExt = ext.replace(/[^a-z.]/g, "");
6043
6235
  const fileName = `${imageId}${safeExt}`;
6044
- const filePath = join13(this.dir, fileName);
6236
+ const filePath = join15(this.dir, fileName);
6045
6237
  const resolvedPath = resolve4(filePath);
6046
6238
  const resolvedDir = resolve4(this.dir);
6047
6239
  if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep2)) {
@@ -6067,7 +6259,7 @@ var ImageStorageService = class {
6067
6259
  const files = await readdir4(this.dir);
6068
6260
  const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
6069
6261
  if (!metaFile) return null;
6070
- const raw = JSON.parse(await readFile12(join13(this.dir, metaFile), "utf-8"));
6262
+ const raw = JSON.parse(await readFile12(join15(this.dir, metaFile), "utf-8"));
6071
6263
  if (expectedContextId !== void 0 && raw.contextId !== expectedContextId) return null;
6072
6264
  if (!existsSync(raw.filePath)) return null;
6073
6265
  return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };
@@ -6093,7 +6285,7 @@ var ImageStorageService = class {
6093
6285
  await Promise.all(
6094
6286
  files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
6095
6287
  try {
6096
- const raw = JSON.parse(await readFile12(join13(this.dir, file), "utf-8"));
6288
+ const raw = JSON.parse(await readFile12(join15(this.dir, file), "utf-8"));
6097
6289
  if (raw.contextId === contextId) {
6098
6290
  await unlink2(raw.filePath).catch(() => {
6099
6291
  });
@@ -6123,7 +6315,7 @@ var ImageStorageService = class {
6123
6315
  for (const file of files) {
6124
6316
  if (!file.endsWith(".meta.json")) continue;
6125
6317
  try {
6126
- const raw = JSON.parse(await readFile12(join13(this.dir, file), "utf-8"));
6318
+ const raw = JSON.parse(await readFile12(join15(this.dir, file), "utf-8"));
6127
6319
  if (now - raw.createdAt > ttlMs) expired.push(raw);
6128
6320
  } catch {
6129
6321
  }
@@ -7002,7 +7194,7 @@ function tailTranscript(opts) {
7002
7194
 
7003
7195
  // ../server/src/services/ai-runners/claude-pty-runner.ts
7004
7196
  async function getAgentManager() {
7005
- const { agentManager: agentManager2 } = await import("./agent-manager-KX5MSTAK.js");
7197
+ const { agentManager: agentManager2 } = await import("./agent-manager-RGDHYM64.js");
7006
7198
  return agentManager2;
7007
7199
  }
7008
7200
  function buildPtyArgs(params, sessionId, opts) {
@@ -7655,7 +7847,7 @@ async function shutdownCodexAppServer() {
7655
7847
  }
7656
7848
  }
7657
7849
  async function getAgentManager2() {
7658
- const { agentManager: agentManager2 } = await import("./agent-manager-KX5MSTAK.js");
7850
+ const { agentManager: agentManager2 } = await import("./agent-manager-RGDHYM64.js");
7659
7851
  return agentManager2;
7660
7852
  }
7661
7853
  function parseRuneyaCurlCommand(command) {
@@ -8516,12 +8708,24 @@ async function conversationCwd(conv) {
8516
8708
  }
8517
8709
  var chatRouter = router({
8518
8710
  conversations: router({
8519
- onChanged: protectedProcedure.subscription(async function* () {
8520
- const initial = await conversationStore.list();
8711
+ /**
8712
+ * Le flux des conversations du projet ouvert.
8713
+ *
8714
+ * Filtré comme `list` : l'instantané initial remplace la liste du client,
8715
+ * et sans le même filtre il y reversait toutes les conversations de la
8716
+ * machine, une seconde après un chargement pourtant filtré.
8717
+ */
8718
+ onChanged: protectedProcedure.input(z15.object({
8719
+ projectId: Id128Schema.optional(),
8720
+ orgId: Id128Schema.nullable().optional()
8721
+ }).optional()).subscription(async function* ({ input }) {
8722
+ const belongs = input?.projectId ? await conversationStore.matcherForProject({ id: input.projectId, orgId: input.orgId }) : () => true;
8723
+ const initial = (await conversationStore.list()).filter(belongs);
8521
8724
  yield { type: "initial", conversations: initial };
8522
8725
  const queue = [];
8523
8726
  let resolve7 = null;
8524
8727
  const handler = (event) => {
8728
+ if (event.type !== "deleted" && !belongs(event.conversation)) return;
8525
8729
  if (queue.length >= 500) queue.shift();
8526
8730
  queue.push(event);
8527
8731
  if (resolve7) {
@@ -8550,13 +8754,17 @@ var chatRouter = router({
8550
8754
  * A conversation belongs to the project it was opened from, so the caller
8551
8755
  * says which one it is looking at. Ones that name no project — written
8552
8756
  * before the field existed, or that a migration could not attribute — are
8553
- * returned whatever the project: hiding them would make them unreachable.
8757
+ * returned only for the projects of the vault they were read from: still
8758
+ * reachable where they were written, without leaking into every other
8759
+ * organization's projects.
8554
8760
  */
8555
- list: protectedProcedure.input(z15.object({ projectId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
8556
- const all = await conversationStore.list();
8761
+ list: protectedProcedure.input(z15.object({
8762
+ projectId: Id128Schema.optional(),
8763
+ orgId: Id128Schema.nullable().optional()
8764
+ }).optional()).query(async ({ input }) => {
8557
8765
  const projectId = input?.projectId;
8558
- if (!projectId) return all;
8559
- return all.filter((c) => !c.projectId || c.projectId === projectId);
8766
+ if (!projectId) return conversationStore.list();
8767
+ return conversationStore.listForProject({ id: projectId, orgId: input?.orgId });
8560
8768
  }),
8561
8769
  get: protectedProcedure.input(z15.object({ id: Id128Schema })).query(async ({ input }) => {
8562
8770
  const conv = await conversationStore.get(input.id);
@@ -8579,7 +8787,10 @@ var chatRouter = router({
8579
8787
  })).mutation(async ({ input }) => {
8580
8788
  let scenario = null;
8581
8789
  if (input.scenarioId) {
8582
- scenario = await scenarioStore.get(input.scenarioId);
8790
+ scenario = await scenarioStore.get(
8791
+ input.scenarioId,
8792
+ input.orgId ?? await vaultOfProject(input.projectId)
8793
+ );
8583
8794
  if (!scenario) throw new TRPCError11({ code: "NOT_FOUND", message: "Scenario not found" });
8584
8795
  }
8585
8796
  const conv = await conversationStore.create(
@@ -8644,8 +8855,17 @@ var chatRouter = router({
8644
8855
  return conv;
8645
8856
  })
8646
8857
  }),
8647
- listModels: protectedProcedure.input(z15.object({ providerId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
8648
- const active = await getActiveProvider(input?.providerId);
8858
+ listModels: protectedProcedure.input(z15.object({
8859
+ providerId: Id128Schema.optional(),
8860
+ // Le projet dit de quelle organisation lire les fournisseurs ; absent,
8861
+ // ce sont ceux du poste.
8862
+ projectId: Id128Schema.optional(),
8863
+ orgId: z15.string().max(128).nullable().optional()
8864
+ }).optional()).query(async ({ input }) => {
8865
+ const active = await getActiveProvider(
8866
+ input?.providerId,
8867
+ input?.orgId ?? await vaultOfProject(input?.projectId)
8868
+ );
8649
8869
  if (!active) return [];
8650
8870
  try {
8651
8871
  return await fetchProviderModels(active.provider, active.plaintextKey);
@@ -8674,9 +8894,14 @@ var chatRouter = router({
8674
8894
  enhanceScenarioPrompt: protectedProcedure.input(z15.object({
8675
8895
  text: z15.string().max(1e4),
8676
8896
  providerId: Id128Schema.optional(),
8677
- model: z15.string().max(200).optional()
8897
+ model: z15.string().max(200).optional(),
8898
+ projectId: Id128Schema.optional(),
8899
+ orgId: z15.string().max(128).nullable().optional()
8678
8900
  })).mutation(async ({ input }) => {
8679
- const active = await getActiveProvider(input.providerId);
8901
+ const active = await getActiveProvider(
8902
+ input.providerId,
8903
+ input.orgId ?? await vaultOfProject(input.projectId)
8904
+ );
8680
8905
  if (!active) throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
8681
8906
  const systemPrompt = `You are an expert at writing clear, concise user request briefs for AI-powered workflows.
8682
8907
  When given a rough user request, rewrite it to be:
@@ -8970,7 +9195,8 @@ ${input.prompt}` : input.prompt;
8970
9195
  throw new TRPCError11({ code: "BAD_REQUEST", message: "Messages are required for new messages" });
8971
9196
  }
8972
9197
  const conv = await conversationStore.get(input.conversationId);
8973
- const active = await getActiveProvider(conv?.providerId);
9198
+ const convRef = conv ? await conversationStore.projectRefOf(conv) : null;
9199
+ const active = await getActiveProvider(conv?.providerId, convRef?.orgId ?? void 0);
8974
9200
  if (!active) {
8975
9201
  throw new TRPCError11({
8976
9202
  code: "PRECONDITION_FAILED",
@@ -9603,7 +9829,10 @@ var gitRouter = router({
9603
9829
  if (!diff.trim()) {
9604
9830
  throw new TRPCError12({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
9605
9831
  }
9606
- const active = await getActiveProvider(input.providerId);
9832
+ const active = await getActiveProvider(
9833
+ input.providerId,
9834
+ input.orgId ?? await vaultOfProject(input.projectId)
9835
+ );
9607
9836
  if (!active) {
9608
9837
  throw new TRPCError12({ code: "BAD_REQUEST", message: "No AI provider configured" });
9609
9838
  }
@@ -9820,7 +10049,7 @@ async function* streamProviderText(input) {
9820
10049
 
9821
10050
  // ../server/src/trpc/ai-generation-procedure-utils.ts
9822
10051
  async function* streamWithActiveProvider(opts) {
9823
- const active = await getActiveProvider(opts.providerId);
10052
+ const active = await getActiveProvider(opts.providerId, opts.vault);
9824
10053
  if (!active) throw new TRPCError14({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
9825
10054
  const { provider, plaintextKey } = active;
9826
10055
  const model = opts.model || provider.model || DEFAULT_MODELS[provider.provider] || "";
@@ -9848,7 +10077,7 @@ async function* streamWithActiveProvider(opts) {
9848
10077
  // ../server/src/services/scenario-runner.ts
9849
10078
  import { mkdir as mkdir14 } from "fs/promises";
9850
10079
  import { randomUUID as randomUUID7 } from "crypto";
9851
- import { join as join14 } from "path";
10080
+ import { join as join16 } from "path";
9852
10081
 
9853
10082
  // ../server/src/services/scenario-presets.ts
9854
10083
  var BUILT_IN_PRESETS = [
@@ -10626,7 +10855,7 @@ async function appendNodeOutput(conversationId, node, output) {
10626
10855
  if (!dir) return;
10627
10856
  const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
10628
10857
  const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
10629
- const filePath = join14(dir, `${slug}_${safeId}.md`);
10858
+ const filePath = join16(dir, `${slug}_${safeId}.md`);
10630
10859
  const content = `## Output
10631
10860
 
10632
10861
  ${output}
@@ -10641,7 +10870,7 @@ async function appendToReadme(conversationId, bus, text) {
10641
10870
  try {
10642
10871
  await mkdir14(dir, { recursive: true });
10643
10872
  const { appendFile: appendFile2 } = await import("fs/promises");
10644
- await appendFile2(join14(dir, "README.md"), line, "utf-8");
10873
+ await appendFile2(join16(dir, "README.md"), line, "utf-8");
10645
10874
  } catch {
10646
10875
  }
10647
10876
  publishToBus(bus, { type: "readme_append", text: line });
@@ -10661,7 +10890,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
10661
10890
 
10662
10891
  ${content}
10663
10892
  `;
10664
- const filePath = join14(dir, "FINAL.md");
10893
+ const filePath = join16(dir, "FINAL.md");
10665
10894
  await atomicWriteSecure(filePath, fullContent);
10666
10895
  await appendToReadme(
10667
10896
  conversationId,
@@ -10719,7 +10948,7 @@ async function runScenario(conversationId, scenarioId, userRequest, runOverrides
10719
10948
  };
10720
10949
  var isSubtreeComplete = isSubtreeComplete2;
10721
10950
  const scenario = await (async () => {
10722
- const base = await scenarioRunStore.loadScenarioSnapshot(conversationId) ?? await scenarioStore.get(scenarioId);
10951
+ const base = await scenarioRunStore.loadScenarioSnapshot(conversationId) ?? await scenarioStore.get(scenarioId, runOverrides?.orgId ?? void 0);
10723
10952
  if (!base) throw new Error(`Scenario ${scenarioId} not found`);
10724
10953
  return applyRunOverridesToScenario(base, runOverrides);
10725
10954
  })();
@@ -11328,9 +11557,19 @@ Modify the scenario. Keep existing ids when possible.`;
11328
11557
  }
11329
11558
  return `Generate an scenario org-chart based on: ${userPrompt}`;
11330
11559
  }
11560
+ var ScopeShape2 = {
11561
+ orgId: z17.string().max(128).nullable().optional(),
11562
+ projectId: Id128Schema.optional()
11563
+ };
11564
+ async function scopeOf2(input) {
11565
+ return input?.orgId ?? await vaultOfProject(input?.projectId);
11566
+ }
11331
11567
  var scenarioRouter = router({
11332
- list: protectedProcedure.query(() => scenarioStore.list()),
11333
- get: protectedProcedure.input(z17.object({ id: Id128Schema })).query(({ input }) => getOrNotFound(() => scenarioStore.get(input.id), "Scenario not found")),
11568
+ list: protectedProcedure.input(z17.object(ScopeShape2).optional()).query(async ({ input }) => scenarioStore.list(await scopeOf2(input))),
11569
+ get: protectedProcedure.input(z17.object({ id: Id128Schema, ...ScopeShape2 })).query(async ({ input }) => getOrNotFound(
11570
+ async () => scenarioStore.get(input.id, await scopeOf2(input)),
11571
+ "Scenario not found"
11572
+ )),
11334
11573
  create: protectedProcedure.input(z17.object({
11335
11574
  name: NameSchema,
11336
11575
  icon: z17.string().max(64).optional(),
@@ -11340,10 +11579,11 @@ var scenarioRouter = router({
11340
11579
  nodes: z17.array(ScenarioNodeSchema).min(1).max(500),
11341
11580
  traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
11342
11581
  presetId: z17.string().max(128).optional(),
11343
- isPublished: z17.boolean().optional()
11582
+ isPublished: z17.boolean().optional(),
11583
+ ...ScopeShape2
11344
11584
  })).mutation(({ input }) => {
11345
11585
  const now = Date.now();
11346
- return createOrBadRequest(() => scenarioStore.create({
11586
+ return createOrBadRequest(async () => scenarioStore.create({
11347
11587
  id: randomUUID8(),
11348
11588
  name: input.name,
11349
11589
  icon: input.icon,
@@ -11368,9 +11608,10 @@ var scenarioRouter = router({
11368
11608
  nodes: z17.array(ScenarioNodeSchema).min(1).max(500).optional(),
11369
11609
  traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
11370
11610
  presetId: z17.string().max(128).optional(),
11371
- isPublished: z17.boolean().optional()
11611
+ isPublished: z17.boolean().optional(),
11612
+ ...ScopeShape2
11372
11613
  })).mutation(({ input }) => updateOrThrow(
11373
- () => scenarioStore.update(input.id, {
11614
+ async () => scenarioStore.update(input.id, {
11374
11615
  name: input.name,
11375
11616
  icon: input.icon,
11376
11617
  description: input.description,
@@ -11381,10 +11622,13 @@ var scenarioRouter = router({
11381
11622
  presetId: input.presetId,
11382
11623
  isPublished: input.isPublished,
11383
11624
  updatedAt: Date.now()
11384
- }),
11625
+ }, await scopeOf2(input)),
11626
+ "Scenario not found"
11627
+ )),
11628
+ delete: protectedProcedure.input(z17.object({ id: Id128Schema, ...ScopeShape2 })).mutation(({ input }) => deleteOrNotFound(
11629
+ async () => scenarioStore.delete(input.id, await scopeOf2(input)),
11385
11630
  "Scenario not found"
11386
11631
  )),
11387
- delete: protectedProcedure.input(z17.object({ id: Id128Schema })).mutation(({ input }) => deleteOrNotFound(() => scenarioStore.delete(input.id), "Scenario not found")),
11388
11632
  run: protectedProcedure.input(z17.object({
11389
11633
  conversationId: ConversationIdSchema,
11390
11634
  scenarioId: Id128Schema,
@@ -11398,7 +11642,7 @@ var scenarioRouter = router({
11398
11642
  orgId: z17.string().max(128).nullable().optional(),
11399
11643
  resume: z17.boolean().optional()
11400
11644
  })).subscription(async function* ({ input, signal }) {
11401
- const scenario = await scenarioStore.get(input.scenarioId);
11645
+ const scenario = await scenarioStore.get(input.scenarioId, await scopeOf2(input));
11402
11646
  if (!scenario) throw new TRPCError15({ code: "NOT_FOUND", message: "Scenario not found" });
11403
11647
  const canStart = input.userRequest.trim().length > 0 || !!input.resume;
11404
11648
  if (!isScenarioRunning(input.conversationId) && canStart) {
@@ -11718,11 +11962,14 @@ var KanbanQueueService = class extends EventEmitter4 {
11718
11962
  item.scenarioId ? "scenario" : void 0,
11719
11963
  item.runner,
11720
11964
  item.boardId,
11721
- item.cardId
11965
+ item.cardId,
11966
+ void 0,
11967
+ item.projectId,
11968
+ item.orgId
11722
11969
  );
11723
11970
  const cardContent = card.title + (card.markdown ? "\n\n" + card.markdown : "");
11724
11971
  if (item.scenarioId) {
11725
- const scenario = await scenarioStore.get(item.scenarioId);
11972
+ const scenario = await scenarioStore.get(item.scenarioId, item.orgId ?? void 0);
11726
11973
  if (scenario) {
11727
11974
  await scenarioRunStore.initializeConversationWorkspace(conv.id, scenario, cardContent);
11728
11975
  }
@@ -11795,14 +12042,21 @@ var kanbanQueueRouter = router({
11795
12042
  cardId: z19.string().max(128),
11796
12043
  runner: z19.enum(["claude-code", "codex"]).optional(),
11797
12044
  model: z19.string().max(128).optional(),
11798
- scenarioId: z19.string().max(128).optional()
12045
+ scenarioId: z19.string().max(128).optional(),
12046
+ // Le projet ouvert quand la carte a été mise en file : la conversation
12047
+ // qui en naîtra lui appartient, plutôt que de rester orpheline et de
12048
+ // s'afficher dans tous les projets.
12049
+ projectId: z19.string().max(128).optional(),
12050
+ orgId: z19.string().max(128).nullable().optional()
11799
12051
  })).mutation(({ input }) => kanbanQueue.add(input)),
11800
12052
  addColumn: protectedProcedure.input(z19.object({
11801
12053
  boardId: z19.string().max(128),
11802
12054
  columnId: z19.string().max(128),
11803
12055
  runner: z19.enum(["claude-code", "codex"]).optional(),
11804
12056
  model: z19.string().max(128).optional(),
11805
- scenarioId: z19.string().max(128).optional()
12057
+ scenarioId: z19.string().max(128).optional(),
12058
+ projectId: z19.string().max(128).optional(),
12059
+ orgId: z19.string().max(128).nullable().optional()
11806
12060
  })).mutation(async ({ input }) => {
11807
12061
  const { boardId, columnId, ...options } = input;
11808
12062
  return kanbanQueue.addColumn(boardId, columnId, options);
@@ -11849,7 +12103,7 @@ var kanbanQueueRouter = router({
11849
12103
  import { mkdir as mkdir15, readdir as readdir5, rm as rm3, stat as stat4, writeFile as writeFile13 } from "fs/promises";
11850
12104
  import { createHash } from "crypto";
11851
12105
  import { tmpdir } from "os";
11852
- import { join as join15 } from "path";
12106
+ import { join as join17 } from "path";
11853
12107
  import { z as z20 } from "zod";
11854
12108
  import { TRPCError as TRPCError17 } from "@trpc/server";
11855
12109
  function summarizeCard(card, boardId, columnTitle) {
@@ -11900,14 +12154,14 @@ function safeDecode(value) {
11900
12154
  }
11901
12155
  var MAX_ATTACHMENT_BYTES = 55 * 1024 * 1024;
11902
12156
  function attachmentCacheDir() {
11903
- return join15(tmpdir(), "runeya-kanban-attachments");
12157
+ return join17(tmpdir(), "runeya-kanban-attachments");
11904
12158
  }
11905
12159
  var ATTACHMENT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
11906
12160
  async function purgeStaleAttachments(dir, ttlMs = ATTACHMENT_CACHE_TTL_MS) {
11907
12161
  try {
11908
12162
  const cutoff = Date.now() - ttlMs;
11909
12163
  for (const name of await readdir5(dir)) {
11910
- const path = join15(dir, name);
12164
+ const path = join17(dir, name);
11911
12165
  const info = await stat4(path).catch(() => null);
11912
12166
  if (info && info.mtimeMs < cutoff) await rm3(path, { force: true });
11913
12167
  }
@@ -11915,14 +12169,14 @@ async function purgeStaleAttachments(dir, ttlMs = ATTACHMENT_CACHE_TTL_MS) {
11915
12169
  }
11916
12170
  }
11917
12171
  function attachmentCacheDirs() {
11918
- return [attachmentCacheDir(), join15(machineRoot(), "kanban-attachments")];
12172
+ return [attachmentCacheDir(), join17(machineRoot(), "kanban-attachments")];
11919
12173
  }
11920
12174
  async function storeAttachment(id, name, body, dirs = attachmentCacheDirs()) {
11921
12175
  const refusals = [];
11922
12176
  for (const dir of dirs) {
11923
12177
  try {
11924
12178
  await mkdir15(dir, { recursive: true });
11925
- const path = join15(dir, safeLocalName(id, name));
12179
+ const path = join17(dir, safeLocalName(id, name));
11926
12180
  await writeFile13(path, body);
11927
12181
  void purgeStaleAttachments(dir);
11928
12182
  return path;
@@ -12242,7 +12496,7 @@ var utilityRouter = router({
12242
12496
  import { z as z23 } from "zod";
12243
12497
  import { TRPCError as TRPCError19 } from "@trpc/server";
12244
12498
  import { readFile as readFile14, readdir as readdir6, stat as stat5, realpath, writeFile as writeFile14, mkdir as mkdir16, rename as rename11, rm as rm4 } from "fs/promises";
12245
- import { resolve as resolvePath, join as join16, relative, sep as sep3, dirname as dirname8 } from "path";
12499
+ import { resolve as resolvePath, join as join18, relative, sep as sep3, dirname as dirname8 } from "path";
12246
12500
  var SCOPE_SCHEMA = z23.union([
12247
12501
  z23.literal("global"),
12248
12502
  z23.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
@@ -12312,7 +12566,7 @@ async function walkLocal(root, dir, depth, counter) {
12312
12566
  for (const e of entries) {
12313
12567
  if (counter.n >= MAX_FILES) break;
12314
12568
  if (e.name.startsWith(".") && e.name !== ".") continue;
12315
- const abs = join16(dir, e.name);
12569
+ const abs = join18(dir, e.name);
12316
12570
  const rel = relative(root, abs).split(sep3).join("/");
12317
12571
  if (e.isDirectory()) {
12318
12572
  if (IGNORED_DIRS.has(e.name)) continue;
@@ -12478,29 +12732,10 @@ var configRouter = router({
12478
12732
  import { z as z24 } from "zod";
12479
12733
  import { TRPCError as TRPCError20 } from "@trpc/server";
12480
12734
 
12481
- // ../server/src/services/org-sync-scope.ts
12482
- import { access as access2 } from "fs/promises";
12483
- import { join as join17 } from "path";
12484
- async function isMaterialized(root) {
12485
- return access2(join17(root, "project.json")).then(() => true, () => false);
12486
- }
12487
- async function orgsToSync() {
12488
- const cloudUrl = getCloudApiUrl();
12489
- const stamps = await readOrgClouds();
12490
- const orgIds = /* @__PURE__ */ new Set();
12491
- for (const vault of await listCloudVaults()) {
12492
- if (orgIds.has(vault.orgId)) continue;
12493
- const stamped = stamps[vault.orgId];
12494
- if (stamped && stamped.replace(/\/+$/, "") !== cloudUrl) continue;
12495
- if (await isMaterialized(vault.root)) orgIds.add(vault.orgId);
12496
- }
12497
- return Array.from(orgIds, (orgId) => ({ orgId, cloudUrl }));
12498
- }
12499
-
12500
12735
  // ../server/src/services/master-password.ts
12501
12736
  import { randomBytes as randomBytes4, createCipheriv, createDecipheriv } from "crypto";
12502
12737
  import { readFile as readFile15, writeFile as writeFile15, chmod as chmod10, stat as stat6, rename as rename12 } from "fs/promises";
12503
- import { join as join18 } from "path";
12738
+ import { join as join19 } from "path";
12504
12739
  import { Entry } from "@napi-rs/keyring";
12505
12740
  import { hashRaw as argon2HashRaw } from "@node-rs/argon2";
12506
12741
  var KEYRING_SERVICE = "runeya";
@@ -12533,7 +12768,7 @@ function aesDecrypt(key, data) {
12533
12768
  }
12534
12769
  async function getPepperFromFile(dataDir) {
12535
12770
  try {
12536
- const raw = await readKeyFile(join18(dataDir, PEPPER_FILENAME));
12771
+ const raw = await readKeyFile(join19(dataDir, PEPPER_FILENAME));
12537
12772
  const hex = raw.toString("utf-8").trim();
12538
12773
  if (/^[0-9a-f]{64}$/i.test(hex)) return Buffer.from(hex, "hex");
12539
12774
  } catch {
@@ -12541,7 +12776,7 @@ async function getPepperFromFile(dataDir) {
12541
12776
  return null;
12542
12777
  }
12543
12778
  async function writePepperToFile(dataDir, pepper) {
12544
- const filePath = join18(dataDir, PEPPER_FILENAME);
12779
+ const filePath = join19(dataDir, PEPPER_FILENAME);
12545
12780
  const tmpPath = filePath + ".tmp";
12546
12781
  await writeFile15(tmpPath, pepper.toString("hex"), "utf-8");
12547
12782
  await chmod10(tmpPath, 384);
@@ -12585,26 +12820,26 @@ async function deriveKey(masterPassword, pepper, salt) {
12585
12820
  });
12586
12821
  return Buffer.from(derived);
12587
12822
  }
12588
- async function writeEncryptedKey(dataDir, masterPassword, encryptionKey) {
12589
- const pepper = await getOrCreatePepper(dataDir);
12823
+ async function writeEncryptedKey(dataDir, masterPassword, encryptionKey, pepperDir = dataDir) {
12824
+ const pepper = await getOrCreatePepper(pepperDir);
12590
12825
  const salt = randomBytes4(16);
12591
12826
  const derivedKey = await deriveKey(masterPassword, pepper, salt);
12592
12827
  const payload = aesEncrypt(derivedKey, encryptionKey);
12593
12828
  const fileContent = Buffer.concat([salt, payload]);
12594
12829
  const encoded = fileContent.toString("base64");
12595
- const filePath = join18(dataDir, ENCRYPTED_KEY_FILENAME);
12830
+ const filePath = join19(dataDir, ENCRYPTED_KEY_FILENAME);
12596
12831
  const tmpPath = filePath + ".tmp";
12597
12832
  await writeFile15(tmpPath, encoded, "utf-8");
12598
12833
  await chmod10(tmpPath, 384);
12599
12834
  await rename12(tmpPath, filePath);
12600
12835
  }
12601
- async function readEncryptedKey(dataDir, masterPassword) {
12602
- const filePath = join18(dataDir, ENCRYPTED_KEY_FILENAME);
12836
+ async function readEncryptedKey(dataDir, masterPassword, pepperDir = dataDir) {
12837
+ const filePath = join19(dataDir, ENCRYPTED_KEY_FILENAME);
12603
12838
  const raw = await readKeyFile(filePath);
12604
12839
  const fileContent = Buffer.from(raw.toString("utf-8").trim(), "base64");
12605
12840
  const salt = fileContent.subarray(0, 16);
12606
12841
  const payload = fileContent.subarray(16);
12607
- const pepper = await getPepperFromKeychain(dataDir);
12842
+ const pepper = await getPepperFromKeychain(pepperDir);
12608
12843
  const derivedKey = await deriveKey(masterPassword, pepper, salt);
12609
12844
  return aesDecrypt(derivedKey, payload);
12610
12845
  }
@@ -12619,13 +12854,13 @@ async function getPepperFromKeychain(dataDir) {
12619
12854
  }
12620
12855
  async function writeVerifyFile(dataDir, encryptionKey) {
12621
12856
  const payload = aesEncrypt(encryptionKey, Buffer.from(CANARY_PLAINTEXT, "utf-8"));
12622
- const filePath = join18(dataDir, VERIFY_FILENAME);
12857
+ const filePath = join19(dataDir, VERIFY_FILENAME);
12623
12858
  const tmpPath = filePath + ".tmp";
12624
12859
  await writeFile15(tmpPath, payload.toString("base64"), "utf-8");
12625
12860
  await rename12(tmpPath, filePath);
12626
12861
  }
12627
12862
  async function verifyEncryptionKey(dataDir, encryptionKey) {
12628
- const filePath = join18(dataDir, VERIFY_FILENAME);
12863
+ const filePath = join19(dataDir, VERIFY_FILENAME);
12629
12864
  const raw = await readKeyFile(filePath);
12630
12865
  if (!matchesCanary(raw.toString("utf-8"), encryptionKey)) {
12631
12866
  throw new Error("Cl\xE9 d'encryption incorrecte");
@@ -12641,14 +12876,14 @@ function matchesCanary(canaryBase64, encryptionKey) {
12641
12876
  }
12642
12877
  async function readVerifyFile(dataDir) {
12643
12878
  try {
12644
- return (await readKeyFile(join18(dataDir, VERIFY_FILENAME))).toString("utf-8").trim();
12879
+ return (await readKeyFile(join19(dataDir, VERIFY_FILENAME))).toString("utf-8").trim();
12645
12880
  } catch {
12646
12881
  return null;
12647
12882
  }
12648
12883
  }
12649
12884
  async function hasVerifyFile(dataDir) {
12650
12885
  try {
12651
- await stat6(join18(dataDir, VERIFY_FILENAME));
12886
+ await stat6(join19(dataDir, VERIFY_FILENAME));
12652
12887
  return true;
12653
12888
  } catch {
12654
12889
  return false;
@@ -12656,13 +12891,32 @@ async function hasVerifyFile(dataDir) {
12656
12891
  }
12657
12892
  async function hasEncryptedKeyFile(dataDir) {
12658
12893
  try {
12659
- await stat6(join18(dataDir, ENCRYPTED_KEY_FILENAME));
12894
+ await stat6(join19(dataDir, ENCRYPTED_KEY_FILENAME));
12660
12895
  return true;
12661
12896
  } catch {
12662
12897
  return false;
12663
12898
  }
12664
12899
  }
12665
12900
 
12901
+ // ../server/src/services/org-sync-scope.ts
12902
+ import { access as access2 } from "fs/promises";
12903
+ import { join as join20 } from "path";
12904
+ async function isMaterialized(root) {
12905
+ return access2(join20(root, "project.json")).then(() => true, () => false);
12906
+ }
12907
+ async function orgsToSync() {
12908
+ const cloudUrl = getCloudApiUrl();
12909
+ const stamps = await readOrgClouds();
12910
+ const orgIds = /* @__PURE__ */ new Set();
12911
+ for (const vault of await listCloudVaults()) {
12912
+ if (orgIds.has(vault.orgId)) continue;
12913
+ const stamped = stamps[vault.orgId];
12914
+ if (stamped && stamped.replace(/\/+$/, "") !== cloudUrl) continue;
12915
+ if (await isMaterialized(vault.root)) orgIds.add(vault.orgId);
12916
+ }
12917
+ return Array.from(orgIds, (orgId) => ({ orgId, cloudUrl }));
12918
+ }
12919
+
12666
12920
  // ../server/src/services/org-canary.ts
12667
12921
  var CanaryUnavailableError = class extends Error {
12668
12922
  code = "CANARY_UNAVAILABLE";
@@ -12681,9 +12935,9 @@ var OrgKeyMismatchError = class extends Error {
12681
12935
  }
12682
12936
  };
12683
12937
  async function reconcileOrgCanary(orgId) {
12684
- const key = getEncryptionKey();
12685
- if (!key) throw new CanaryUnavailableError("Encryption key not loaded.");
12686
- const local = await readVerifyFile(await keyRoot());
12938
+ const key = getEncryptionKey(orgId);
12939
+ if (!key) throw new CanaryUnavailableError("Cl\xE9 de l'organisation non charg\xE9e.");
12940
+ const local = await readVerifyFile(orgVaultRoot(orgId)) ?? await readVerifyFile(await keyRoot());
12687
12941
  if (!local) throw new CanaryUnavailableError("This machine has no key canary yet.");
12688
12942
  const res = await cloudFetch("/api/auth/organization/canary", {
12689
12943
  method: "POST",
@@ -12707,10 +12961,10 @@ import * as Y from "yjs";
12707
12961
  // ../server/src/services/org-lock.ts
12708
12962
  import { open as open2, readFile as readFile16, unlink as unlink4, mkdir as mkdir17 } from "fs/promises";
12709
12963
  import { readFileSync, unlinkSync } from "fs";
12710
- import { join as join19 } from "path";
12964
+ import { join as join21 } from "path";
12711
12965
  var held = /* @__PURE__ */ new Set();
12712
12966
  function lockPath(orgId) {
12713
- return join19(orgVaultRoot(orgId), "org.lock");
12967
+ return join21(orgVaultRoot(orgId), "org.lock");
12714
12968
  }
12715
12969
  function isRunning(pid) {
12716
12970
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -13225,11 +13479,11 @@ var OrgSyncConnection = class {
13225
13479
  }
13226
13480
  async upsertLocally(name, id, fields) {
13227
13481
  if (name === "services") {
13228
- await serviceStore.applyRemoteUpsert(id, fields);
13482
+ await serviceStore.applyRemoteUpsert(id, fields, this.link.orgId);
13229
13483
  return;
13230
13484
  }
13231
13485
  if (name === "environments") {
13232
- await environmentStore.applyRemoteUpsert(id, fields);
13486
+ await environmentStore.applyRemoteUpsert(id, fields, this.link.orgId);
13233
13487
  return;
13234
13488
  }
13235
13489
  const result = await projectStore.applyRemoteUpsert(id, {
@@ -13350,6 +13604,14 @@ var CloudSyncBridge = class {
13350
13604
  connections = /* @__PURE__ */ new Map();
13351
13605
  /** Orgs another instance holds the lock on — reported, not retried blindly. */
13352
13606
  locked = /* @__PURE__ */ new Set();
13607
+ /**
13608
+ * Orgs whose key this machine does not have.
13609
+ *
13610
+ * Held here for the same reason as `locked`: the connection is never opened,
13611
+ * so there is nothing to ask for its state — and an org that cannot sync
13612
+ * until a human supplies a key must not read as merely "stopped".
13613
+ */
13614
+ keyMismatch = /* @__PURE__ */ new Set();
13353
13615
  started = false;
13354
13616
  /**
13355
13617
  * Start syncing every organization this machine holds a project of.
@@ -13369,7 +13631,13 @@ var CloudSyncBridge = class {
13369
13631
  return;
13370
13632
  }
13371
13633
  for (const link of links) await this.connect(link);
13372
- if (links.length > 0) console.log(`[cloud-sync] syncing ${links.length} organization(s)`);
13634
+ const connected = this.connections.size;
13635
+ if (links.length > 0) {
13636
+ const skipped = links.length - connected;
13637
+ console.log(
13638
+ `[cloud-sync] syncing ${connected}/${links.length} organization(s)` + (skipped > 0 ? ` \u2014 ${skipped} not syncing (see the errors above)` : "")
13639
+ );
13640
+ }
13373
13641
  }
13374
13642
  /**
13375
13643
  * Reconnect every link whose session had been rejected. Called after a cloud
@@ -13379,6 +13647,26 @@ var CloudSyncBridge = class {
13379
13647
  const paused = Array.from(this.connections.values()).filter((c) => c.getState() === "unauthenticated").map((c) => c.getLink());
13380
13648
  for (const link of paused) await this.connect(link);
13381
13649
  }
13650
+ /**
13651
+ * Tout reconnecter, quel que soit l'état où chaque organisation s'était
13652
+ * arrêtée.
13653
+ *
13654
+ * `resumeAll` ne reprend que les sessions rejetées ; après un changement de
13655
+ * clé, ce sont justement les organisations écartées pour clé différente qu'il
13656
+ * faut retenter — et elles n'ont pas de connexion à reprendre, seulement un
13657
+ * lien sur le disque.
13658
+ */
13659
+ async restartAll() {
13660
+ this.keyMismatch.clear();
13661
+ let links = [];
13662
+ try {
13663
+ links = await orgsToSync();
13664
+ } catch (err) {
13665
+ console.error("[cloud-sync] could not read which organizations to sync:", err.message);
13666
+ return;
13667
+ }
13668
+ for (const link of links) await this.connect(link);
13669
+ }
13382
13670
  /** Per-org sync state, for the UI. */
13383
13671
  states() {
13384
13672
  const live = Array.from(this.connections.entries()).map(([orgId, conn]) => ({
@@ -13387,7 +13675,8 @@ var CloudSyncBridge = class {
13387
13675
  lastIncompatibleVersion: conn.getLastIncompatibleVersion()
13388
13676
  }));
13389
13677
  const locked = Array.from(this.locked).filter((orgId) => !this.connections.has(orgId)).map((orgId) => ({ orgId, state: "locked", lastIncompatibleVersion: null }));
13390
- return [...live, ...locked];
13678
+ const mismatched = Array.from(this.keyMismatch).filter((orgId) => !this.connections.has(orgId)).map((orgId) => ({ orgId, state: "key-mismatch", lastIncompatibleVersion: null }));
13679
+ return [...live, ...locked, ...mismatched];
13391
13680
  }
13392
13681
  /**
13393
13682
  * Follow one project of an org on this machine, pulling it now.
@@ -13421,6 +13710,7 @@ var CloudSyncBridge = class {
13421
13710
  } catch (err) {
13422
13711
  if (err instanceof OrgKeyMismatchError) {
13423
13712
  console.error("[cloud-sync] org %s: %s", link.orgId, err.message);
13713
+ this.keyMismatch.add(link.orgId);
13424
13714
  return;
13425
13715
  }
13426
13716
  console.warn(
@@ -13429,6 +13719,7 @@ var CloudSyncBridge = class {
13429
13719
  err.message
13430
13720
  );
13431
13721
  }
13722
+ this.keyMismatch.delete(link.orgId);
13432
13723
  const lock = await acquireOrgLock(link.orgId).catch((err) => {
13433
13724
  console.error("[cloud-sync] could not take the lock for org %s:", link.orgId, err.message);
13434
13725
  return { acquired: false, heldBy: 0 };
@@ -13463,11 +13754,185 @@ var CloudSyncBridge = class {
13463
13754
  for (const conn of this.connections.values()) conn.stop();
13464
13755
  this.connections.clear();
13465
13756
  this.locked.clear();
13757
+ this.keyMismatch.clear();
13466
13758
  releaseAllOrgLocksSync();
13467
13759
  }
13468
13760
  };
13469
13761
  var cloudSyncBridge = new CloudSyncBridge();
13470
13762
 
13763
+ // ../server/src/services/key-adoption.ts
13764
+ import { readdir as readdir7, readFile as readFile17, writeFile as writeFile16, mkdir as mkdir20, copyFile, rename as rename13, stat as stat7 } from "fs/promises";
13765
+ import { join as join22 } from "path";
13766
+ import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes5 } from "crypto";
13767
+
13768
+ // ../server/src/services/org-keys.ts
13769
+ import { mkdir as mkdir19 } from "fs/promises";
13770
+ async function unlockOrgKeys(masterPassword) {
13771
+ const machineKey = getEncryptionKey();
13772
+ const report = { opened: [], seeded: [], locked: [] };
13773
+ if (!machineKey) return report;
13774
+ for (const orgId of await listOrgVaults()) {
13775
+ const root = orgVaultRoot(orgId);
13776
+ if (!masterPassword) {
13777
+ setEncryptionKeyInMemory(machineKey, orgId);
13778
+ continue;
13779
+ }
13780
+ const canary = await readVerifyFile(root);
13781
+ if (canary) {
13782
+ try {
13783
+ const key = await readEncryptedKey(root, masterPassword, await keyRoot());
13784
+ if (!matchesCanary(canary, key)) {
13785
+ markVaultLocked(orgId);
13786
+ report.locked.push(orgId);
13787
+ continue;
13788
+ }
13789
+ setEncryptionKeyInMemory(key, orgId);
13790
+ report.opened.push(orgId);
13791
+ } catch {
13792
+ markVaultLocked(orgId);
13793
+ report.locked.push(orgId);
13794
+ }
13795
+ continue;
13796
+ }
13797
+ await mkdir19(root, { recursive: true });
13798
+ await writeEncryptedKey(root, masterPassword, machineKey, await keyRoot());
13799
+ await writeVerifyFile(root, machineKey);
13800
+ setEncryptionKeyInMemory(machineKey, orgId);
13801
+ report.seeded.push(orgId);
13802
+ }
13803
+ return report;
13804
+ }
13805
+ async function installOrgKey(orgId, key, masterPassword) {
13806
+ const root = orgVaultRoot(orgId);
13807
+ await mkdir19(root, { recursive: true });
13808
+ const pepperDir = await keyRoot();
13809
+ await writeEncryptedKey(root, masterPassword, key, pepperDir);
13810
+ const readBack = await readEncryptedKey(root, masterPassword, pepperDir);
13811
+ if (!readBack.equals(key)) {
13812
+ throw new Error("V\xE9rification du fichier de cl\xE9 de l'organisation \xE9chou\xE9e.");
13813
+ }
13814
+ await writeVerifyFile(root, key);
13815
+ setEncryptionKeyInMemory(key, orgId);
13816
+ }
13817
+
13818
+ // ../server/src/services/key-adoption.ts
13819
+ var ALGORITHM = "aes-256-gcm";
13820
+ function isAgentTriplet(value) {
13821
+ const hex = (v) => typeof v === "string" && /^[0-9a-f]+$/i.test(v);
13822
+ return hex(value["encrypted"]) && hex(value["iv"]) && hex(value["authTag"]);
13823
+ }
13824
+ function decryptTriplet(key, t2) {
13825
+ const decipher = createDecipheriv2(ALGORITHM, key, Buffer.from(t2.iv, "hex"), { authTagLength: 16 });
13826
+ decipher.setAuthTag(Buffer.from(t2.authTag, "hex"));
13827
+ return decipher.update(t2.encrypted, "hex", "utf-8") + decipher.final("utf-8");
13828
+ }
13829
+ function encryptTriplet(key, plaintext) {
13830
+ const iv = randomBytes5(16);
13831
+ const cipher = createCipheriv2(ALGORITHM, key, iv);
13832
+ const encrypted = cipher.update(plaintext, "utf-8", "hex") + cipher.final("hex");
13833
+ return { encrypted, iv: iv.toString("hex"), authTag: cipher.getAuthTag().toString("hex") };
13834
+ }
13835
+ function rekeyJsonValue(value, from, to, counter) {
13836
+ if (typeof value === "string") {
13837
+ if (!isEncryptedValue(value)) return value;
13838
+ let plaintext;
13839
+ try {
13840
+ plaintext = decryptValueWith(from, value);
13841
+ } catch {
13842
+ return value;
13843
+ }
13844
+ counter.n += 1;
13845
+ return encryptValueWith(to, plaintext);
13846
+ }
13847
+ if (Array.isArray(value)) return value.map((v) => rekeyJsonValue(v, from, to, counter));
13848
+ if (value && typeof value === "object") {
13849
+ const record = value;
13850
+ if (isAgentTriplet(record)) {
13851
+ try {
13852
+ const plaintext = decryptTriplet(from, record);
13853
+ counter.n += 1;
13854
+ return { ...record, ...encryptTriplet(to, plaintext) };
13855
+ } catch {
13856
+ return record;
13857
+ }
13858
+ }
13859
+ const out = {};
13860
+ for (const [k, v] of Object.entries(record)) out[k] = rekeyJsonValue(v, from, to, counter);
13861
+ return out;
13862
+ }
13863
+ return value;
13864
+ }
13865
+ async function rootsOfOrg(orgId) {
13866
+ const roots = /* @__PURE__ */ new Set([orgVaultRoot(orgId)]);
13867
+ for (const vault of await listCloudVaults()) {
13868
+ if (vault.orgId === orgId) roots.add(vault.root);
13869
+ }
13870
+ return Array.from(roots);
13871
+ }
13872
+ async function jsonFilesIn(root) {
13873
+ try {
13874
+ const entries = await readdir7(root, { withFileTypes: true });
13875
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => e.name);
13876
+ } catch {
13877
+ return [];
13878
+ }
13879
+ }
13880
+ async function exists(path) {
13881
+ return stat7(path).then(() => true, () => false);
13882
+ }
13883
+ async function adoptOrgKey(orgId, newKey, masterPassword, now) {
13884
+ const currentKey = getEncryptionKey(orgId);
13885
+ if (!currentKey) {
13886
+ throw new Error("La cl\xE9 actuelle de cette organisation n'est pas charg\xE9e.");
13887
+ }
13888
+ if (newKey.equals(currentKey)) {
13889
+ throw new Error("Cette cl\xE9 est d\xE9j\xE0 celle de l'organisation.");
13890
+ }
13891
+ const machineRootDir = await keyRoot();
13892
+ await readEncryptedKey(machineRootDir, masterPassword);
13893
+ const pending = [];
13894
+ const touchedRoots = /* @__PURE__ */ new Set();
13895
+ for (const vaultRoot of await rootsOfOrg(orgId)) {
13896
+ for (const name of await jsonFilesIn(vaultRoot)) {
13897
+ const path = join22(vaultRoot, name);
13898
+ let parsed;
13899
+ try {
13900
+ parsed = JSON.parse(await readFile17(path, "utf-8"));
13901
+ } catch {
13902
+ continue;
13903
+ }
13904
+ const counter = { n: 0 };
13905
+ const converted = rekeyJsonValue(parsed, currentKey, newKey, counter);
13906
+ if (counter.n === 0) continue;
13907
+ pending.push({ path, content: JSON.stringify(converted, null, 2), values: counter.n });
13908
+ touchedRoots.add(vaultRoot);
13909
+ }
13910
+ }
13911
+ const stamp = now.toISOString().replace(/[:.]/g, "-").substring(0, 19);
13912
+ const backups = [];
13913
+ for (const vaultRoot of touchedRoots) {
13914
+ const backupDir = join22(vaultRoot, `backup-key-${stamp}`);
13915
+ await mkdir20(backupDir, { recursive: true });
13916
+ for (const name of await jsonFilesIn(vaultRoot)) {
13917
+ await copyFile(join22(vaultRoot, name), join22(backupDir, name));
13918
+ }
13919
+ for (const name of [".encryption.key.enc", ".runeya-verify"]) {
13920
+ if (await exists(join22(vaultRoot, name))) {
13921
+ await copyFile(join22(vaultRoot, name), join22(backupDir, name));
13922
+ }
13923
+ }
13924
+ backups.push(backupDir);
13925
+ }
13926
+ for (const item of pending) {
13927
+ const tmp = item.path + ".tmp";
13928
+ await writeFile16(tmp, item.content, "utf-8");
13929
+ await rename13(tmp, item.path);
13930
+ }
13931
+ await installOrgKey(orgId, newKey, masterPassword);
13932
+ clearVaultLock(orgId);
13933
+ return { converted: pending.map(({ path, values }) => ({ file: path, values })), backups };
13934
+ }
13935
+
13471
13936
  // ../server/src/trpc/routers/cloud-sync.ts
13472
13937
  async function cloudMessage(res) {
13473
13938
  try {
@@ -13530,6 +13995,134 @@ var cloudSyncRouter = router({
13530
13995
  throw err;
13531
13996
  }
13532
13997
  }),
13998
+ /**
13999
+ * Faire passer le projet local de cette machine dans une organisation.
14000
+ *
14001
+ * Le projet n'est pas copié : il déménage. Son coffre quitte
14002
+ * `<cwd>/.runeya` pour celui de l'organisation, en gardant son identifiant,
14003
+ * puis il est suivi comme n'importe quel projet cloud — la réconciliation le
14004
+ * pousse alors dans le doc, services et environnements compris.
14005
+ *
14006
+ * Le déménagement est ce qui rend le geste sûr : il ne peut jamais y avoir
14007
+ * deux copies du même projet, donc rien à écraser. Un projet que
14008
+ * l'organisation porte déjà sous le même identifiant est refusé plutôt que
14009
+ * fusionné.
14010
+ */
14011
+ publishProject: protectedProcedure.input(
14012
+ z24.object({
14013
+ orgId: z24.string().min(1).max(128),
14014
+ cloudUrl: z24.string().url().max(2048),
14015
+ projectId: z24.string().min(1).max(128)
14016
+ })
14017
+ ).mutation(async ({ input }) => {
14018
+ const local = await projectStore.get({ id: input.projectId, orgId: null });
14019
+ if (!local) {
14020
+ throw new TRPCError20({ code: "NOT_FOUND", message: "PROJECT_NOT_LOCAL" });
14021
+ }
14022
+ if (await projectStore.get({ id: input.projectId, orgId: input.orgId })) {
14023
+ throw new TRPCError20({ code: "CONFLICT", message: "PROJECT_ALREADY_IN_ORG" });
14024
+ }
14025
+ const connected = cloudSyncBridge.states().some((s) => s.orgId === input.orgId);
14026
+ if (!connected) {
14027
+ await assertOrgKey(input.orgId);
14028
+ await stampOrgCloud(input.orgId, input.cloudUrl);
14029
+ await cloudSyncBridge.connect({ orgId: input.orgId, cloudUrl: input.cloudUrl });
14030
+ }
14031
+ await projectStore.moveToOrg(input.projectId, input.orgId);
14032
+ await conversationStore.relocateAll();
14033
+ try {
14034
+ await cloudSyncBridge.followProject(input.orgId, input.projectId);
14035
+ } catch (err) {
14036
+ if (err instanceof ProjectHeldElsewhereError) {
14037
+ throw new TRPCError20({ code: "CONFLICT", message: err.code, cause: err });
14038
+ }
14039
+ throw err;
14040
+ }
14041
+ return { state: syncState(input.orgId) };
14042
+ }),
14043
+ /**
14044
+ * Cette clé est-elle celle de l'organisation ? Rien n'est écrit.
14045
+ *
14046
+ * L'étape à faire avant tout le reste : on ne re-chiffre pas une machine
14047
+ * entière sur une clé dont on n'a pas la preuve qu'elle ouvre le canari
14048
+ * publié. Le cloud, lui, ne peut pas répondre — il n'a que du chiffré sous
14049
+ * les yeux — donc le canari est rapatrié et jugé ici.
14050
+ */
14051
+ checkOrgKey: protectedProcedure.input(z24.object({
14052
+ orgId: z24.string().min(1).max(128),
14053
+ keyHex: z24.string().length(64).regex(/^[0-9a-f]+$/i)
14054
+ })).query(async ({ input }) => {
14055
+ const res = await cloudFetch2(
14056
+ `/api/auth/organization/canary?organizationId=${encodeURIComponent(input.orgId)}`,
14057
+ { method: "GET" }
14058
+ );
14059
+ if (!res.ok) {
14060
+ throw new TRPCError20({ code: "BAD_GATEWAY", message: await cloudMessage(res) || `CANARY_READ_FAILED (${res.status})` });
14061
+ }
14062
+ const { canary } = await res.json();
14063
+ if (typeof canary !== "string") return { matches: false, published: false };
14064
+ return { matches: matchesCanary(canary, Buffer.from(input.keyHex, "hex")), published: true };
14065
+ }),
14066
+ /**
14067
+ * Faire adopter à cette machine la clé d'une organisation.
14068
+ *
14069
+ * Le pendant de `resetOrgKey` : là où celle-ci fait plier l'organisation à la
14070
+ * clé d'ici, celle-là fait plier cette machine à la sienne — et c'est la
14071
+ * seule des deux qui rend lisible ce qui avait déjà été chiffré sous elle.
14072
+ *
14073
+ * Bornée au coffre de l'organisation et à ceux de ses projets : les autres
14074
+ * organisations et la machine gardent leur clé.
14075
+ */
14076
+ adoptOrgKey: protectedProcedure.input(z24.object({
14077
+ orgId: z24.string().min(1).max(128),
14078
+ keyHex: z24.string().length(64).regex(/^[0-9a-f]+$/i),
14079
+ masterPassword: z24.string().min(1).max(1024)
14080
+ })).mutation(async ({ input }) => {
14081
+ const report = await adoptOrgKey(
14082
+ input.orgId,
14083
+ Buffer.from(input.keyHex, "hex"),
14084
+ input.masterPassword,
14085
+ /* @__PURE__ */ new Date()
14086
+ );
14087
+ await cloudSyncBridge.restartAll();
14088
+ return { converted: report.converted.length, backups: report.backups, state: syncState(input.orgId) };
14089
+ }),
14090
+ /**
14091
+ * Réinitialiser la clé de référence d'une organisation avec celle de cette
14092
+ * machine, puis relancer sa synchronisation.
14093
+ *
14094
+ * Le canari publié par le cloud est write-once : c'est ce qui empêche un
14095
+ * membre tenant la mauvaise clé d'enfermer les autres dehors. Le revers, c'est
14096
+ * qu'une clé machine qui change — réinstallation, `~/.runeya` perdu, migration
14097
+ * d'un ancien coffre — laisse l'organisation insynchronisable pour toujours.
14098
+ * Cette route est la sortie de secours, réservée au propriétaire côté cloud.
14099
+ *
14100
+ * Elle ne re-chiffre rien : ce qui a déjà été synchronisé sous l'ancienne clé
14101
+ * reste illisible. L'interface le dit avant de la déclencher.
14102
+ */
14103
+ resetOrgKey: protectedProcedure.input(z24.object({ orgId: z24.string().min(1).max(128) })).mutation(async ({ input }) => {
14104
+ const canary = await readVerifyFile(await keyRoot());
14105
+ if (!canary) {
14106
+ throw new TRPCError20({
14107
+ code: "PRECONDITION_FAILED",
14108
+ message: "Cette machine n'a pas encore de cl\xE9 de chiffrement."
14109
+ });
14110
+ }
14111
+ const res = await cloudFetch2("/api/auth/organization/canary/reset", {
14112
+ method: "POST",
14113
+ body: { organizationId: input.orgId, canary }
14114
+ });
14115
+ if (!res.ok) {
14116
+ const detail = await cloudMessage(res);
14117
+ throw new TRPCError20({
14118
+ code: res.status === 403 ? "FORBIDDEN" : "BAD_GATEWAY",
14119
+ message: detail || `ORG_KEY_RESET_FAILED (${res.status})`
14120
+ });
14121
+ }
14122
+ const cloudUrl = (await orgsToSync()).find((l) => l.orgId === input.orgId)?.cloudUrl ?? getCloudApiUrl();
14123
+ await cloudSyncBridge.connect({ orgId: input.orgId, cloudUrl });
14124
+ return { state: syncState(input.orgId) };
14125
+ }),
13533
14126
  /**
13534
14127
  * Stop materializing one project of an org on this machine.
13535
14128
  *
@@ -13621,15 +14214,15 @@ var cloudSyncRouter = router({
13621
14214
  });
13622
14215
 
13623
14216
  // ../server/src/trpc/routers/filesystem.ts
13624
- import { readdir as readdir7, stat as stat7 } from "fs/promises";
14217
+ import { readdir as readdir8, stat as stat8 } from "fs/promises";
13625
14218
  import { homedir as homedir5 } from "os";
13626
- import { isAbsolute as isAbsolute4, join as join20, resolve as resolve5, dirname as dirname9, basename as basename2 } from "path";
14219
+ import { isAbsolute as isAbsolute4, join as join23, resolve as resolve5, dirname as dirname9, basename as basename2 } from "path";
13627
14220
  import { z as z25 } from "zod";
13628
14221
  var MAX_ENTRIES = 500;
13629
14222
  var PATH_SCHEMA = z25.string().min(1).max(4096);
13630
14223
  function safeResolve(input) {
13631
14224
  if (input.includes("\0")) throw new Error("Chemin invalide");
13632
- const expanded = input.startsWith("~") ? join20(homedir5(), input.slice(1)) : input;
14225
+ const expanded = input.startsWith("~") ? join23(homedir5(), input.slice(1)) : input;
13633
14226
  if (!isAbsolute4(expanded)) throw new Error("Le chemin doit \xEAtre absolu");
13634
14227
  return resolve5(expanded);
13635
14228
  }
@@ -13652,20 +14245,20 @@ var filesystemRouter = router({
13652
14245
  */
13653
14246
  listDirectories: protectedProcedure.input(z25.object({ path: PATH_SCHEMA })).query(async ({ input }) => {
13654
14247
  const dir = safeResolve(input.path);
13655
- const info = await stat7(dir).catch(() => null);
14248
+ const info = await stat8(dir).catch(() => null);
13656
14249
  if (!info?.isDirectory()) {
13657
14250
  throw new Error(`Ce n'est pas un r\xE9pertoire : ${dir}`);
13658
14251
  }
13659
14252
  let names;
13660
14253
  try {
13661
- names = (await readdir7(dir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
14254
+ names = (await readdir8(dir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
13662
14255
  } catch {
13663
14256
  throw new Error(`R\xE9pertoire illisible : ${dir}`);
13664
14257
  }
13665
14258
  const truncated = names.length > MAX_ENTRIES;
13666
14259
  const entries = [];
13667
14260
  for (const name of names.slice(0, MAX_ENTRIES)) {
13668
- const path = join20(dir, name);
14261
+ const path = join23(dir, name);
13669
14262
  entries.push({ name, path, hasRuneyaProject: await holdsRuneyaProject(path) });
13670
14263
  }
13671
14264
  return {
@@ -13690,7 +14283,7 @@ var filesystemRouter = router({
13690
14283
  orgId: z25.string().max(128).nullable().optional()
13691
14284
  })).query(async ({ input }) => {
13692
14285
  const dir = safeResolve(input.path);
13693
- const info = await stat7(dir).catch(() => null);
14286
+ const info = await stat8(dir).catch(() => null);
13694
14287
  if (!info?.isDirectory()) {
13695
14288
  return { ok: false, reason: "missing", path: dir, otherProjectId: null };
13696
14289
  }
@@ -13765,7 +14358,7 @@ var MigrationEngine = class {
13765
14358
  var migrationEngine = new MigrationEngine();
13766
14359
 
13767
14360
  // ../server/src/services/file-security.ts
13768
- import { chmod as chmod11, stat as stat8 } from "fs/promises";
14361
+ import { chmod as chmod11, stat as stat9 } from "fs/promises";
13769
14362
  var SENSITIVE_FILES = [
13770
14363
  "agents.json",
13771
14364
  "settings.json",
@@ -13779,7 +14372,7 @@ async function ensureFilePermissions() {
13779
14372
  for (const filename of SENSITIVE_FILES) {
13780
14373
  for (const filepath of [machineFilePath(filename), legacyMachineFilePath(filename)]) {
13781
14374
  try {
13782
- const stats = await stat8(filepath);
14375
+ const stats = await stat9(filepath);
13783
14376
  const mode = stats.mode & 511;
13784
14377
  if (mode !== SECURE_MODE) {
13785
14378
  console.warn("[security] File", filepath, "has mode", mode.toString(8) + ", fixing to 600");
@@ -13795,8 +14388,8 @@ async function ensureFilePermissions() {
13795
14388
  }
13796
14389
 
13797
14390
  // ../server/src/services/vault-migration.ts
13798
- import { readdir as readdir8, readFile as readFile17, writeFile as writeFile16, mkdir as mkdir19, copyFile, unlink as unlink5, rename as rename13, stat as stat9 } from "fs/promises";
13799
- import { join as join21 } from "path";
14391
+ import { readdir as readdir9, readFile as readFile18, writeFile as writeFile17, mkdir as mkdir21, copyFile as copyFile2, unlink as unlink5, rename as rename14, stat as stat10 } from "fs/promises";
14392
+ import { join as join24 } from "path";
13800
14393
  var KEY_FILES = [".encryption.key.enc", ".runeya-verify"];
13801
14394
  var MACHINE_FILES = [
13802
14395
  "agents.json",
@@ -13806,35 +14399,14 @@ var MACHINE_FILES = [
13806
14399
  "api-keys.private.json",
13807
14400
  "settings.private.json"
13808
14401
  ];
13809
- function rekeyValue(value, from, to, counter) {
13810
- if (typeof value === "string") {
13811
- if (!isEncryptedValue(value)) return value;
13812
- let plaintext;
13813
- try {
13814
- plaintext = decryptValueWith(from, value);
13815
- } catch {
13816
- return value;
13817
- }
13818
- counter.n += 1;
13819
- return encryptValueWith(to, plaintext);
13820
- }
13821
- if (Array.isArray(value)) return value.map((v) => rekeyValue(v, from, to, counter));
13822
- if (value && typeof value === "object") {
13823
- const out = {};
13824
- for (const [k, v] of Object.entries(value)) {
13825
- out[k] = rekeyValue(v, from, to, counter);
13826
- }
13827
- return out;
13828
- }
13829
- return value;
13830
- }
14402
+ var rekeyValue = rekeyJsonValue;
13831
14403
  async function vaultJsonFiles(root) {
13832
- const entries = await readdir8(root, { withFileTypes: true });
14404
+ const entries = await readdir9(root, { withFileTypes: true });
13833
14405
  return entries.filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => e.name);
13834
14406
  }
13835
- async function exists(path) {
14407
+ async function exists2(path) {
13836
14408
  try {
13837
- await stat9(path);
14409
+ await stat10(path);
13838
14410
  return true;
13839
14411
  } catch {
13840
14412
  return false;
@@ -13847,17 +14419,17 @@ async function openLegacyVault(legacyRoot, masterPassword) {
13847
14419
  }
13848
14420
  async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machineKey, now) {
13849
14421
  const stamp = now.toISOString().replace(/[:.]/g, "-").substring(0, 19);
13850
- const backupDir = join21(legacyRoot, `backup-${stamp}`);
13851
- await mkdir19(backupDir, { recursive: true });
14422
+ const backupDir = join24(legacyRoot, `backup-${stamp}`);
14423
+ await mkdir21(backupDir, { recursive: true });
13852
14424
  const jsonFiles = await vaultJsonFiles(legacyRoot);
13853
14425
  for (const name of [...jsonFiles, ...KEY_FILES]) {
13854
- if (await exists(join21(legacyRoot, name))) {
13855
- await copyFile(join21(legacyRoot, name), join21(backupDir, name));
14426
+ if (await exists2(join24(legacyRoot, name))) {
14427
+ await copyFile2(join24(legacyRoot, name), join24(backupDir, name));
13856
14428
  }
13857
14429
  }
13858
14430
  const rekeyed = [];
13859
14431
  for (const name of jsonFiles) {
13860
- const raw = await readFile17(join21(legacyRoot, name), "utf-8");
14432
+ const raw = await readFile18(join24(legacyRoot, name), "utf-8");
13861
14433
  let parsed;
13862
14434
  try {
13863
14435
  parsed = JSON.parse(raw);
@@ -13867,13 +14439,13 @@ async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machine
13867
14439
  const counter = { n: 0 };
13868
14440
  const converted = rekeyValue(parsed, legacyKey, machineKey, counter);
13869
14441
  if (counter.n === 0) continue;
13870
- const tmp = join21(legacyRoot, `${name}.tmp`);
13871
- await writeFile16(tmp, JSON.stringify(converted, null, 2), "utf-8");
13872
- await rename13(tmp, join21(legacyRoot, name));
14442
+ const tmp = join24(legacyRoot, `${name}.tmp`);
14443
+ await writeFile17(tmp, JSON.stringify(converted, null, 2), "utf-8");
14444
+ await rename14(tmp, join24(legacyRoot, name));
13873
14445
  rekeyed.push({ file: name, values: counter.n });
13874
14446
  }
13875
14447
  for (const { file, values } of rekeyed) {
13876
- const parsed = JSON.parse(await readFile17(join21(legacyRoot, file), "utf-8"));
14448
+ const parsed = JSON.parse(await readFile18(join24(legacyRoot, file), "utf-8"));
13877
14449
  const check = { n: 0 };
13878
14450
  rekeyValue(parsed, machineKey, machineKey, check);
13879
14451
  if (check.n !== values) {
@@ -13882,41 +14454,93 @@ async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machine
13882
14454
  );
13883
14455
  }
13884
14456
  }
13885
- await mkdir19(machineRootDir, { recursive: true });
14457
+ await mkdir21(machineRootDir, { recursive: true });
13886
14458
  const moved = [];
13887
14459
  const skipped = [];
13888
14460
  for (const name of MACHINE_FILES) {
13889
- const from = join21(legacyRoot, name);
13890
- if (!await exists(from)) continue;
13891
- const to = join21(machineRootDir, name);
13892
- if (await exists(to)) {
14461
+ const from = join24(legacyRoot, name);
14462
+ if (!await exists2(from)) continue;
14463
+ const to = join24(machineRootDir, name);
14464
+ if (await exists2(to)) {
13893
14465
  skipped.push(name);
13894
14466
  continue;
13895
14467
  }
13896
- await copyFile(from, to);
14468
+ await copyFile2(from, to);
13897
14469
  await unlink5(from);
13898
14470
  moved.push(name);
13899
14471
  }
13900
14472
  for (const name of KEY_FILES) {
13901
- const path = join21(legacyRoot, name);
13902
- if (await exists(path)) await unlink5(path);
14473
+ const path = join24(legacyRoot, name);
14474
+ if (await exists2(path)) await unlink5(path);
13903
14475
  }
13904
14476
  return { backupDir, rekeyed, moved, skipped };
13905
14477
  }
13906
14478
 
14479
+ // ../server/src/services/settings-migration.ts
14480
+ import { readFile as readFile19, writeFile as writeFile18, rename as rename15, unlink as unlink6, mkdir as mkdir22, chmod as chmod12, stat as stat11 } from "fs/promises";
14481
+ import { join as join25 } from "path";
14482
+ var SETTINGS_FILE2 = "settings.json";
14483
+ var SETTINGS_PRIVATE_FILE2 = "settings.private.json";
14484
+ async function exists3(path) {
14485
+ return stat11(path).then(() => true, () => false);
14486
+ }
14487
+ async function legacySource(filename) {
14488
+ for (const path of [machineFilePath(filename), legacyMachineFilePath(filename)]) {
14489
+ if (await exists3(path)) return path;
14490
+ }
14491
+ return null;
14492
+ }
14493
+ async function moveInto(root, filename, from) {
14494
+ await mkdir22(root, { recursive: true });
14495
+ const to = join25(root, filename);
14496
+ const contents = await readFile19(from, "utf-8");
14497
+ const tmp = to + ".tmp";
14498
+ await writeFile18(tmp, contents, "utf-8");
14499
+ await rename15(tmp, to);
14500
+ await chmod12(to, 384);
14501
+ await unlink6(from);
14502
+ }
14503
+ async function migrateSettingsIntoVaults() {
14504
+ const report = { movedTo: null, files: [], ambiguous: [] };
14505
+ const sources = [];
14506
+ for (const filename of [SETTINGS_FILE2, SETTINGS_PRIVATE_FILE2]) {
14507
+ const from = await legacySource(filename);
14508
+ if (from) sources.push({ filename, from });
14509
+ }
14510
+ if (sources.length === 0) return report;
14511
+ const orgs = await listOrgVaults();
14512
+ if (orgs.length === 0) return report;
14513
+ if (orgs.length > 1) {
14514
+ report.ambiguous = orgs;
14515
+ return report;
14516
+ }
14517
+ const orgId = orgs[0];
14518
+ const root = orgVaultRoot(orgId);
14519
+ for (const { filename, from } of sources) {
14520
+ if (await exists3(join25(root, filename))) continue;
14521
+ await moveInto(root, filename, from);
14522
+ report.files.push(filename);
14523
+ }
14524
+ if (report.files.length > 0) {
14525
+ report.movedTo = orgId;
14526
+ settingsManager.forget();
14527
+ }
14528
+ return report;
14529
+ }
14530
+
13907
14531
  // ../server/src/migrations/migrate-from-legacy.ts
13908
14532
  import { createRequire } from "module";
13909
- import { randomBytes as randomBytes5, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
14533
+ import { randomBytes as randomBytes6, createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3 } from "crypto";
13910
14534
  import { randomUUID as randomUUID12 } from "crypto";
13911
14535
  import {
13912
- readFile as readFile18,
13913
- writeFile as writeFile17,
13914
- rename as rename14,
13915
- mkdir as mkdir20,
14536
+ readFile as readFile20,
14537
+ writeFile as writeFile19,
14538
+ rename as rename16,
14539
+ mkdir as mkdir23,
13916
14540
  access as access3,
13917
- readdir as readdir9
14541
+ readdir as readdir10
13918
14542
  } from "fs/promises";
13919
- import { join as join22, dirname as dirname10 } from "path";
14543
+ import { join as join26, dirname as dirname10 } from "path";
13920
14544
  var _require = createRequire(import.meta.url);
13921
14545
  var PARSER_ID_MAP = {
13922
14546
  "stack-monitor-parser-jsons": "native:json",
@@ -13929,8 +14553,8 @@ var PARSER_ID_MAP = {
13929
14553
  function encryptValue2(plaintext) {
13930
14554
  const key = getEncryptionKey();
13931
14555
  if (!key) throw new Error("[legacy-migration] Encryption key not loaded");
13932
- const iv = randomBytes5(16);
13933
- const cipher = createCipheriv2("aes-256-gcm", key, iv);
14556
+ const iv = randomBytes6(16);
14557
+ const cipher = createCipheriv3("aes-256-gcm", key, iv);
13934
14558
  let enc = cipher.update(plaintext, "utf-8", "hex");
13935
14559
  enc += cipher.final("hex");
13936
14560
  const authTag = cipher.getAuthTag().toString("hex");
@@ -13977,7 +14601,7 @@ function decryptMigrationValue(encrypted) {
13977
14601
  const parts = encrypted.split(":");
13978
14602
  if (parts.length !== 3) return null;
13979
14603
  const [ivHex, authHex, ct] = parts;
13980
- const dec = createDecipheriv2("aes-256-gcm", key, Buffer.from(ivHex, "hex"), { authTagLength: 16 });
14604
+ const dec = createDecipheriv3("aes-256-gcm", key, Buffer.from(ivHex, "hex"), { authTagLength: 16 });
13981
14605
  dec.setAuthTag(Buffer.from(authHex, "hex"));
13982
14606
  return dec.update(ct, "hex", "utf-8") + dec.final("utf-8");
13983
14607
  } catch {
@@ -14025,7 +14649,7 @@ async function getSodium() {
14025
14649
  return _sodium;
14026
14650
  }
14027
14651
  async function decryptLegacyFile(filePath, key, sodium) {
14028
- const raw = (await readFile18(filePath, "utf-8")).trim();
14652
+ const raw = (await readFile20(filePath, "utf-8")).trim();
14029
14653
  if (!raw || raw === "[]" || raw === "{}") {
14030
14654
  try {
14031
14655
  return JSON.parse(raw);
@@ -14045,12 +14669,12 @@ async function decryptLegacyFile(filePath, key, sodium) {
14045
14669
  }
14046
14670
  async function atomicWrite(path, data) {
14047
14671
  const tmp = path + ".tmp";
14048
- await writeFile17(tmp, JSON.stringify(data, null, 2), "utf-8");
14049
- await rename14(tmp, path);
14672
+ await writeFile19(tmp, JSON.stringify(data, null, 2), "utf-8");
14673
+ await rename16(tmp, path);
14050
14674
  }
14051
14675
  async function runLegacyMigration(dataDir) {
14052
14676
  const legacyRoot = env.DATA_DIR;
14053
- const legacyKeyPath = join22(legacyRoot, "dbs", "encryption-key.json");
14677
+ const legacyKeyPath = join26(legacyRoot, "dbs", "encryption-key.json");
14054
14678
  try {
14055
14679
  await access3(legacyKeyPath);
14056
14680
  } catch {
@@ -14058,20 +14682,20 @@ async function runLegacyMigration(dataDir) {
14058
14682
  }
14059
14683
  console.log("[legacy-migration] Legacy Runeya data detected, starting migration\u2026");
14060
14684
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
14061
- const legacyTmp = join22(abCwd, ".runeya-tmp");
14062
- await rename14(legacyRoot, legacyTmp);
14063
- await mkdir20(dataDir, { recursive: true });
14064
- const legacyOld = join22(dataDir, `backup-${timestamp}`);
14065
- await rename14(legacyTmp, legacyOld);
14685
+ const legacyTmp = join26(abCwd, ".runeya-tmp");
14686
+ await rename16(legacyRoot, legacyTmp);
14687
+ await mkdir23(dataDir, { recursive: true });
14688
+ const legacyOld = join26(dataDir, `backup-${timestamp}`);
14689
+ await rename16(legacyTmp, legacyOld);
14066
14690
  console.log(`[legacy-migration] Legacy data backed up to ${legacyOld}`);
14067
- const keyJson = JSON.parse(await readFile18(join22(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
14691
+ const keyJson = JSON.parse(await readFile20(join26(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
14068
14692
  const sodium = await getSodium();
14069
14693
  const sodiumKey = sodium.from_base64(keyJson.encryptionKey);
14070
14694
  const keyBuffer = Buffer.from(sodiumKey);
14071
14695
  const runyeaKeyHex = keyBuffer.toString("hex");
14072
14696
  await setEncryptionKey(runyeaKeyHex);
14073
14697
  console.log("[legacy-migration] Encryption key loaded in memory (temporary)");
14074
- const dbsDir = join22(legacyOld, "dbs");
14698
+ const dbsDir = join26(legacyOld, "dbs");
14075
14699
  async function readEncrypted(filePath) {
14076
14700
  try {
14077
14701
  return await decryptLegacyFile(filePath, sodiumKey, sodium);
@@ -14080,29 +14704,29 @@ async function runLegacyMigration(dataDir) {
14080
14704
  return null;
14081
14705
  }
14082
14706
  }
14083
- const envsDir = join22(dbsDir, "envs");
14084
- const envFiles = (await readdir9(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14707
+ const envsDir = join26(dbsDir, "envs");
14708
+ const envFiles = (await readdir10(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14085
14709
  const legacyEnvs = {};
14086
14710
  for (const file of envFiles) {
14087
14711
  const name = file.replace(".encrypted.json", "");
14088
- legacyEnvs[name] = await readEncrypted(join22(envsDir, file));
14712
+ legacyEnvs[name] = await readEncrypted(join26(envsDir, file));
14089
14713
  }
14090
- const servicesDir = join22(dbsDir, "services");
14091
- const serviceFiles = (await readdir9(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14714
+ const servicesDir = join26(dbsDir, "services");
14715
+ const serviceFiles = (await readdir10(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14092
14716
  const legacyServices = {};
14093
14717
  for (const file of serviceFiles) {
14094
14718
  const name = file.replace(".encrypted.json", "");
14095
14719
  if (!name) continue;
14096
- const data = await readEncrypted(join22(servicesDir, file));
14720
+ const data = await readEncrypted(join26(servicesDir, file));
14097
14721
  if (data?.label) legacyServices[name] = data;
14098
14722
  }
14099
- const overridesDir = join22(dbsDir, "overrides");
14100
- const overrideFiles = (await readdir9(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14723
+ const overridesDir = join26(dbsDir, "overrides");
14724
+ const overrideFiles = (await readdir10(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14101
14725
  const legacyServiceOverrides = {};
14102
14726
  const legacyEnvOverrides = {};
14103
14727
  for (const file of overrideFiles) {
14104
14728
  const baseName = file.replace(".encrypted.json", "");
14105
- const data = await readEncrypted(join22(overridesDir, file));
14729
+ const data = await readEncrypted(join26(overridesDir, file));
14106
14730
  if (!data) continue;
14107
14731
  if (baseName.endsWith("-envs")) {
14108
14732
  legacyServiceOverrides[baseName.slice(0, -5)] = data;
@@ -14110,11 +14734,11 @@ async function runLegacyMigration(dataDir) {
14110
14734
  legacyEnvOverrides[baseName.slice(0, -12)] = data;
14111
14735
  }
14112
14736
  }
14113
- const parsersDir = join22(dbsDir, "parsers");
14114
- const parserFiles = (await readdir9(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14737
+ const parsersDir = join26(dbsDir, "parsers");
14738
+ const parserFiles = (await readdir10(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
14115
14739
  const legacyParsers = [];
14116
14740
  for (const file of parserFiles) {
14117
- const data = await readEncrypted(join22(parsersDir, file));
14741
+ const data = await readEncrypted(join26(parsersDir, file));
14118
14742
  if (data?.id) legacyParsers.push(data);
14119
14743
  }
14120
14744
  const projectMonoId = randomUUID12();
@@ -14455,7 +15079,7 @@ async function runLegacyMigration(dataDir) {
14455
15079
  }
14456
15080
  let existingSettings = {};
14457
15081
  try {
14458
- existingSettings = JSON.parse(await readFile18(join22(dataDir, "settings.json"), "utf-8"));
15082
+ existingSettings = JSON.parse(await readFile20(join26(dataDir, "settings.json"), "utf-8"));
14459
15083
  } catch {
14460
15084
  }
14461
15085
  const migratedParsers = legacyParsers.map((p) => ({
@@ -14476,11 +15100,11 @@ ${p.transform ?? ""}`,
14476
15100
  ...migratedParsers
14477
15101
  ]
14478
15102
  };
14479
- await atomicWrite(join22(dataDir, "projects.json"), runeProjects);
14480
- await atomicWrite(join22(dataDir, "services.json"), runeServices);
14481
- await atomicWrite(join22(dataDir, "environments.json"), runeEnvs);
14482
- await atomicWrite(join22(dataDir, "environment-overrides.json"), flatOverrides);
14483
- await atomicWrite(join22(dataDir, "settings.json"), newSettings);
15103
+ await atomicWrite(join26(dataDir, "projects.json"), runeProjects);
15104
+ await atomicWrite(join26(dataDir, "services.json"), runeServices);
15105
+ await atomicWrite(join26(dataDir, "environments.json"), runeEnvs);
15106
+ await atomicWrite(join26(dataDir, "environment-overrides.json"), flatOverrides);
15107
+ await atomicWrite(join26(dataDir, "settings.json"), newSettings);
14484
15108
  const svcEnvCount = runeEnvs.filter((e) => e.scope === "service").length;
14485
15109
  console.log(`[legacy-migration] Written: ${runeProjects.length} projects, ${runeServices.length} services, ${runeEnvs.length} environments (${svcEnvCount} service-scoped)`);
14486
15110
  console.log(`[legacy-migration] Overrides: ${Object.keys(flatOverrides).length}, custom parsers: ${migratedParsers.length} (all disabled \u2014 rewrite required)`);
@@ -14491,12 +15115,12 @@ ${p.transform ?? ""}`,
14491
15115
  }
14492
15116
  async function findLegacyKeyInBackup(dataDir) {
14493
15117
  try {
14494
- const entries = await readdir9(dataDir);
15118
+ const entries = await readdir10(dataDir);
14495
15119
  const backupDirs = entries.filter((e) => e.startsWith("backup-")).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).reverse();
14496
15120
  for (const dir of backupDirs) {
14497
- const keyJsonPath = join22(dataDir, dir, "dbs", "encryption-key.json");
15121
+ const keyJsonPath = join26(dataDir, dir, "dbs", "encryption-key.json");
14498
15122
  try {
14499
- const raw = await readFile18(keyJsonPath, "utf-8");
15123
+ const raw = await readFile20(keyJsonPath, "utf-8");
14500
15124
  const parsed = JSON.parse(raw);
14501
15125
  if (!parsed.encryptionKey) continue;
14502
15126
  const sodium = await getSodium();
@@ -14517,14 +15141,14 @@ async function runOverrideOnlyMigration(dataDir) {
14517
15141
  const { resolve: resolve7 } = await import("path");
14518
15142
  const abDataDir = resolve7(dataDir);
14519
15143
  const candidateDirs = [
14520
- join22(dirname10(env.DATA_DIR), ".runeya", "dbs", "overrides"),
14521
- join22(abDataDir, "dbs", "overrides")
15144
+ join26(dirname10(env.DATA_DIR), ".runeya", "dbs", "overrides"),
15145
+ join26(abDataDir, "dbs", "overrides")
14522
15146
  ];
14523
15147
  let overridesDir = null;
14524
15148
  let overrideFiles = [];
14525
15149
  for (const dir of candidateDirs) {
14526
15150
  try {
14527
- const files = (await readdir9(dir)).filter((f) => f.endsWith(".encrypted.json"));
15151
+ const files = (await readdir10(dir)).filter((f) => f.endsWith(".encrypted.json"));
14528
15152
  if (files.length > 0) {
14529
15153
  overridesDir = dir;
14530
15154
  overrideFiles = files;
@@ -14534,7 +15158,7 @@ async function runOverrideOnlyMigration(dataDir) {
14534
15158
  }
14535
15159
  }
14536
15160
  if (!overridesDir || overrideFiles.length === 0) return false;
14537
- const legacyKeyPath = join22(overridesDir, "..", "encryption-key.json");
15161
+ const legacyKeyPath = join26(overridesDir, "..", "encryption-key.json");
14538
15162
  try {
14539
15163
  await access3(legacyKeyPath);
14540
15164
  return false;
@@ -14552,7 +15176,7 @@ async function runOverrideOnlyMigration(dataDir) {
14552
15176
  const envNameToId = {};
14553
15177
  let envsRaw;
14554
15178
  try {
14555
- envsRaw = JSON.parse(await readFile18(join22(abDataDir, "environments.json"), "utf-8"));
15179
+ envsRaw = JSON.parse(await readFile20(join26(abDataDir, "environments.json"), "utf-8"));
14556
15180
  for (const _env of envsRaw) {
14557
15181
  const env2 = _env;
14558
15182
  if (env2["name"] && env2["id"]) {
@@ -14571,7 +15195,7 @@ async function runOverrideOnlyMigration(dataDir) {
14571
15195
  let servicesRaw = [];
14572
15196
  const serviceNameToId = {};
14573
15197
  try {
14574
- servicesRaw = JSON.parse(await readFile18(join22(abDataDir, "services.json"), "utf-8"));
15198
+ servicesRaw = JSON.parse(await readFile20(join26(abDataDir, "services.json"), "utf-8"));
14575
15199
  for (const _svc of servicesRaw) {
14576
15200
  const svc = _svc;
14577
15201
  if (svc["name"] && svc["id"]) serviceNameToId[svc["name"]] = svc["id"];
@@ -14588,7 +15212,7 @@ async function runOverrideOnlyMigration(dataDir) {
14588
15212
  const envName = baseName.slice(0, -"-environment".length);
14589
15213
  let data;
14590
15214
  try {
14591
- data = await decryptLegacyFile(join22(overridesDir, file), sodiumKey, sodium);
15215
+ data = await decryptLegacyFile(join26(overridesDir, file), sodiumKey, sodium);
14592
15216
  } catch (err) {
14593
15217
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
14594
15218
  continue;
@@ -14615,7 +15239,7 @@ async function runOverrideOnlyMigration(dataDir) {
14615
15239
  }
14616
15240
  }
14617
15241
  if (Object.keys(flatOverrides).length > 0) {
14618
- await atomicWrite(join22(abDataDir, "environment-overrides.json"), flatOverrides);
15242
+ await atomicWrite(join26(abDataDir, "environment-overrides.json"), flatOverrides);
14619
15243
  console.log(`[override-migration] Written ${Object.keys(flatOverrides).length} override(s) to environment-overrides.json`);
14620
15244
  }
14621
15245
  let envsModified = false;
@@ -14625,7 +15249,7 @@ async function runOverrideOnlyMigration(dataDir) {
14625
15249
  const serviceName = baseName.slice(0, -"-envs".length);
14626
15250
  let data;
14627
15251
  try {
14628
- data = await decryptLegacyFile(join22(overridesDir, file), sodiumKey, sodium);
15252
+ data = await decryptLegacyFile(join26(overridesDir, file), sodiumKey, sodium);
14629
15253
  } catch (err) {
14630
15254
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
14631
15255
  continue;
@@ -14684,12 +15308,12 @@ async function runOverrideOnlyMigration(dataDir) {
14684
15308
  }
14685
15309
  }
14686
15310
  if (envsModified) {
14687
- await atomicWrite(join22(abDataDir, "environments.json"), envsRaw);
15311
+ await atomicWrite(join26(abDataDir, "environments.json"), envsRaw);
14688
15312
  console.log("[override-migration] Merged service overrides into environments.json");
14689
15313
  }
14690
15314
  const migratedDir = overridesDir.replace(/overrides$/, "overrides-migrated");
14691
15315
  try {
14692
- await rename14(overridesDir, migratedDir);
15316
+ await rename16(overridesDir, migratedDir);
14693
15317
  console.log(`[override-migration] Moved overrides/ \u2192 overrides-migrated/`);
14694
15318
  } catch (err) {
14695
15319
  console.warn(`[override-migration] Could not rename overrides dir: ${err.message}`);
@@ -14699,8 +15323,8 @@ async function runOverrideOnlyMigration(dataDir) {
14699
15323
  }
14700
15324
 
14701
15325
  // ../server/src/services/server-state.ts
14702
- import { join as join23 } from "path";
14703
- import { stat as stat10 } from "fs/promises";
15326
+ import { join as join27 } from "path";
15327
+ import { stat as stat12 } from "fs/promises";
14704
15328
  var ENCRYPTED_KEY_FILENAME2 = ".encryption.key.enc";
14705
15329
  var VERIFY_FILENAME2 = ".runeya-verify";
14706
15330
  async function getServerState() {
@@ -14709,22 +15333,22 @@ async function getServerState() {
14709
15333
  let hasVerify = false;
14710
15334
  let hasData = false;
14711
15335
  try {
14712
- await stat10(join23(dataDir, ENCRYPTED_KEY_FILENAME2));
15336
+ await stat12(join27(dataDir, ENCRYPTED_KEY_FILENAME2));
14713
15337
  hasEncFile = true;
14714
15338
  } catch {
14715
15339
  }
14716
15340
  try {
14717
- await stat10(join23(dataDir, VERIFY_FILENAME2));
15341
+ await stat12(join27(dataDir, VERIFY_FILENAME2));
14718
15342
  hasVerify = true;
14719
15343
  } catch {
14720
15344
  }
14721
15345
  try {
14722
- await stat10(join23(dataDir, "projects.json"));
15346
+ await stat12(join27(dataDir, "projects.json"));
14723
15347
  hasData = true;
14724
15348
  } catch {
14725
15349
  }
14726
15350
  try {
14727
- await stat10(join23(dataDir, "project.json"));
15351
+ await stat12(join27(dataDir, "project.json"));
14728
15352
  hasData = true;
14729
15353
  } catch {
14730
15354
  }
@@ -15158,9 +15782,25 @@ async function createLocalServer() {
15158
15782
  credentials: true
15159
15783
  }));
15160
15784
  let postUnlockDone = false;
15161
- async function runPostUnlock() {
15785
+ async function runPostUnlock(masterPassword = null) {
15162
15786
  if (postUnlockDone) return;
15163
15787
  postUnlockDone = true;
15788
+ const orgKeys = await unlockOrgKeys(masterPassword);
15789
+ if (orgKeys.seeded.length > 0) {
15790
+ console.log(`[org-keys] ${orgKeys.seeded.length} organisation(s) ont re\xE7u leur propre cl\xE9 de chiffrement`);
15791
+ }
15792
+ for (const orgId of orgKeys.locked) {
15793
+ console.error(`[org-keys] org ${orgId}: sa cl\xE9 ne s'ouvre pas avec ce master password \u2014 ce coffre reste ferm\xE9.`);
15794
+ }
15795
+ const settingsMove = await migrateSettingsIntoVaults();
15796
+ if (settingsMove.movedTo) {
15797
+ console.log(`[settings] r\xE9glages d\xE9plac\xE9s vers l'organisation ${settingsMove.movedTo} : ${settingsMove.files.join(", ")}`);
15798
+ }
15799
+ if (settingsMove.ambiguous.length > 0) {
15800
+ console.warn(
15801
+ `[settings] r\xE9glages encore au niveau du poste : ${settingsMove.ambiguous.length} organisations sur cette machine, impossible de deviner \xE0 laquelle ils appartiennent \u2014 r\xE9partissez-les depuis les r\xE9glages.`
15802
+ );
15803
+ }
15164
15804
  await runOverrideOnlyMigration(env.DATA_DIR);
15165
15805
  if (env.MODE === "local-simple") {
15166
15806
  const projects = await projectStore.list();
@@ -15416,7 +16056,7 @@ async function createLocalServer() {
15416
16056
  }
15417
16057
  const encKey = await readEncryptedKey(await keyRoot(), masterPassword);
15418
16058
  setEncryptionKeyInMemory(encKey);
15419
- await runPostUnlock();
16059
+ await runPostUnlock(masterPassword);
15420
16060
  const token = await signJwt();
15421
16061
  res.json({ token });
15422
16062
  } catch (err) {
@@ -15462,8 +16102,8 @@ async function createLocalServer() {
15462
16102
  }
15463
16103
  encKey = Buffer.from(encryptionKeyHex, "hex");
15464
16104
  } else {
15465
- const { randomBytes: randomBytes6 } = await import("crypto");
15466
- encKey = randomBytes6(32);
16105
+ const { randomBytes: randomBytes7 } = await import("crypto");
16106
+ encKey = randomBytes7(32);
15467
16107
  }
15468
16108
  }
15469
16109
  await writeEncryptedKey(await keyRoot(), masterPassword, encKey);
@@ -15479,7 +16119,7 @@ async function createLocalServer() {
15479
16119
  if (isLegacyMigration) {
15480
16120
  clearPendingLegacyKey();
15481
16121
  }
15482
- await runPostUnlock();
16122
+ await runPostUnlock(masterPassword);
15483
16123
  const token = await signJwt();
15484
16124
  res.json({ token });
15485
16125
  } catch (err) {
@@ -15938,4 +16578,4 @@ export {
15938
16578
  createLocalServer,
15939
16579
  pullEnv
15940
16580
  };
15941
- //# sourceMappingURL=src-R6JOPDNL.js.map
16581
+ //# sourceMappingURL=src-4GK5OK3K.js.map