@runeya/runeya 2.0.137 → 2.0.139

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 (402) hide show
  1. package/agent/index.js +36 -10
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-NAGIMH2T.js +11 -0
  4. package/chat-Q2UMYXQL.js +21 -0
  5. package/{chunk-ZFELQG4B.js → chunk-EV3TBM7E.js} +78 -5
  6. package/chunk-EV3TBM7E.js.map +1 -0
  7. package/{chunk-FJLMXLMK.js → chunk-GLIU3J47.js} +354 -128
  8. package/chunk-GLIU3J47.js.map +1 -0
  9. package/{chunk-BDQOBEZ3.js → chunk-GOMF7NWS.js} +2 -2
  10. package/{desktop-launcher-3COY6F6H.js → chunk-GWQV5KKU.js} +193 -13
  11. package/chunk-GWQV5KKU.js.map +1 -0
  12. package/{chunk-5EMEJNVU.js → chunk-LE6XQ3OV.js} +13 -1
  13. package/chunk-LE6XQ3OV.js.map +1 -0
  14. package/{chunk-J4GLFY7G.js → chunk-MSICJEZD.js} +5 -5
  15. package/{chunk-NURPXHLB.js → chunk-OZBSOSAS.js} +26 -12
  16. package/chunk-OZBSOSAS.js.map +1 -0
  17. package/{chunk-P65BO7WZ.js → chunk-WNCXZ6E7.js} +165 -125
  18. package/chunk-WNCXZ6E7.js.map +1 -0
  19. package/{chunk-I7JHF5FE.js → chunk-XHGJHYDC.js} +4 -4
  20. package/chunk-YSYLL57J.js +87 -0
  21. package/chunk-YSYLL57J.js.map +1 -0
  22. package/{companion-Q67RAO3N.js → companion-MGX62L4T.js} +9 -7
  23. package/desktop-installer-page-OJTOKP7V.js +598 -0
  24. package/desktop-installer-page-OJTOKP7V.js.map +1 -0
  25. package/desktop-launcher-HKXP4LRI.js +43 -0
  26. package/desktop-wizard-XIDDAG7L.js +15 -0
  27. package/index.js +30 -21
  28. package/index.js.map +1 -1
  29. package/mcp-server/index.js +49 -1
  30. package/mcp-server/index.js.map +1 -1
  31. package/{node-4UUJN2WG.js → node-RN4URNZ5.js} +4 -4
  32. package/package.json +1 -1
  33. package/settings-manager-BBNCDDQJ.js +10 -0
  34. package/settings-manager-BBNCDDQJ.js.map +1 -0
  35. package/{src-7O3GCEIB.js → src-KWLVQD36.js} +193 -92
  36. package/src-KWLVQD36.js.map +1 -0
  37. package/{vault-resolver-57Q2DVOR.js → vault-resolver-B5P2OUI5.js} +20 -2
  38. package/vault-resolver-B5P2OUI5.js.map +1 -0
  39. package/web/assets/AgentScalarChatInterface.vue-CqGVVIo8.js +13 -0
  40. package/web/assets/AgentTerminalPage-mVD1jsWC.js +1 -0
  41. package/web/assets/AiChatPage-J-JAXhoT.js +1 -0
  42. package/web/assets/AppLayout-6eKmu_TP.css +1 -0
  43. package/web/assets/AppLayout-CsUnvWA0.js +1 -0
  44. package/web/assets/AuditPage-gzMJbY4c.js +1 -0
  45. package/web/assets/CloudAccountPage-CIUmdyoI.js +1 -0
  46. package/web/assets/CloudAccountPage-Cm1ibhZY.js +1 -0
  47. package/web/assets/CloudAccountPage-eoNciq1Q.css +1 -0
  48. package/web/assets/CloudMessagingPage-EV4MwYrk.js +2 -0
  49. package/web/assets/DashboardPage-CrMccWRA.js +1 -0
  50. package/web/assets/DirectoryPickerDialog-Cnr4SkLc.js +1 -0
  51. package/web/assets/DirectoryPickerDialog-D1lQ83i5.css +1 -0
  52. package/web/assets/{EditorIcon-DXYoMZnL.js → EditorIcon-Dlr9ami0.js} +2 -2
  53. package/web/assets/{EnvironmentForm-D3pZuxVn.js → EnvironmentForm-NiG4dzT9.js} +2 -2
  54. package/web/assets/EnvironmentFormPage-TJzOWwS7.js +1 -0
  55. package/web/assets/EnvironmentListPage-oR335kl1.js +1 -0
  56. package/web/assets/KanbanBoardPage-Cta-iCIL.css +1 -0
  57. package/web/assets/KanbanBoardPage-Dol6K6rI.js +4 -0
  58. package/web/assets/KanbanListPage-C2yhMB2L.js +1 -0
  59. package/web/assets/{KanbanListPage-DxJFx6A6.css → KanbanListPage-CEJ9Vluh.css} +1 -1
  60. package/web/assets/LaunchProjectPage-BGIRMKdM.js +1 -0
  61. package/web/assets/LaunchProjectPage-CU2iF25_.css +1 -0
  62. package/web/assets/LaunchServicesPage-9aAno4Gc.css +1 -0
  63. package/web/assets/LaunchServicesPage-DqIG8ZDk.js +1 -0
  64. package/web/assets/LaunchShell-C7RrqA4k.js +1 -0
  65. package/web/assets/LaunchShell-CGtjrl6_.css +1 -0
  66. package/web/assets/LogViewer-BWtCyqa4.js +3 -0
  67. package/web/assets/OrgKeyPrompt-B343sYVi.css +1 -0
  68. package/web/assets/OrgKeyPrompt-IjlZPHOI.js +1 -0
  69. package/web/assets/OrgLogo-B0TayK5W.js +1 -0
  70. package/web/assets/ProcessControls-Bw28Duzs.js +1 -0
  71. package/web/assets/ProcessDetailPage-CVYQJ4aI.js +1 -0
  72. package/web/assets/{ProcessMetrics-CdQc_8Z0.js → ProcessMetrics-D0HzDnGO.js} +3 -3
  73. package/web/assets/ProjectCreateDialog-CLFPf8ds.js +1 -0
  74. package/web/assets/ProjectCreateDialog-UEIYMerY.css +1 -0
  75. package/web/assets/ProjectLocationGate-D68jdBcV.js +1 -0
  76. package/web/assets/ProjectPublishDialog-CDpS6xcQ.css +1 -0
  77. package/web/assets/ProjectPublishDialog-Cvvjeuyq.js +1 -0
  78. package/web/assets/RandomJobTestPage-CuUJfwwb.js +2 -0
  79. package/web/assets/ScenarioEditorPage-Ckq6XNl2.js +14 -0
  80. package/web/assets/SendToChatDialog-4a4_2amD.js +1 -0
  81. package/web/assets/ServiceControlsBar-OC7cDCM7.js +1 -0
  82. package/web/assets/ServiceDetailPage-BMlS1q4i.js +2 -0
  83. package/web/assets/ServiceFormPage-CozmJNy1.js +1 -0
  84. package/web/assets/{SettingsPage-DA7lVKfg.css → SettingsPage-CF548HPl.css} +1 -1
  85. package/web/assets/SettingsPage-CkJQwWCW.js +2063 -0
  86. package/web/assets/UnlockPage-94m6Ap-H.js +1 -0
  87. package/web/assets/VaultMigrationPage-Bj6KgIUi.js +1 -0
  88. package/web/assets/VersionBadge-DjTAz-tn.js +1 -0
  89. package/web/assets/WikiPage-n_vxXevo.js +1 -0
  90. package/web/assets/WikiView-lxEITfCa.js +1 -0
  91. package/web/assets/add-scalar-classes-lFkCrmFv.js +387 -0
  92. package/web/assets/{autocomplete-DobAPPYJ.js → autocomplete-bClqtFV-.js} +3 -3
  93. package/web/assets/{basic-shape-diamond.svg-FbeAPLc-.js → basic-shape-diamond.svg-DFdUK3gN.js} +1 -1
  94. package/web/assets/{basic-shape-hexagon.svg-CtwcdpTe.js → basic-shape-hexagon.svg-BKiT9xxU.js} +1 -1
  95. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-DsSs3S2_.js → basic-shape-primary-circle-ellipse-round.svg-tmXcRxRQ.js} +1 -1
  96. package/web/assets/{basic-shape-primary-square-rectangle.svg-CNe1RNx8.js → basic-shape-primary-square-rectangle.svg-C9duFSNc.js} +1 -1
  97. package/web/assets/{basic-shape-shield.svg-DwZMLfah.js → basic-shape-shield.svg-CLzO94DF.js} +1 -1
  98. package/web/assets/{browser-Dz1gvldT.js → browser-BEDlGGfe.js} +1 -1
  99. package/web/assets/{browser-DsZ9HxVm.js → browser-CivlztYP.js} +1 -1
  100. package/web/assets/card-wcTFC8yG.js +32 -0
  101. package/web/assets/{checkbox-DPknldTJ.js → checkbox-TtO_mmrY.js} +2 -2
  102. package/web/assets/{chevronup-D2Lr2GiV.js → chevronup-Cd93cHVj.js} +4 -4
  103. package/web/assets/{chip-Caiakd-N.js → chip-omKmLLWH.js} +2 -2
  104. package/web/assets/client-BKIls6aD.js +24 -0
  105. package/web/assets/cloud-chat.store-Bskr_EXO.js +1 -0
  106. package/web/assets/cloud-client-rcfqjfUK.js +1 -0
  107. package/web/assets/cloud-session-CycNU7YV.js +1 -0
  108. package/web/assets/cloud-session-DiaAeCJI.js +2 -0
  109. package/web/assets/column-DgtEciO-.js +763 -0
  110. package/web/assets/composables-Ce8WccBy.js +1 -0
  111. package/web/assets/{computer-device-desktop-monitor.svg-CwiGN14T.js → computer-device-desktop-monitor.svg-D3YaocxY.js} +1 -1
  112. package/web/assets/{computer-device-desktop.svg-BC2VdXDZ.js → computer-device-desktop.svg-lnn_cyCx.js} +1 -1
  113. package/web/assets/{computer-device-laptop.svg-BOp8AKN6.js → computer-device-laptop.svg-9f5_Ipnq.js} +1 -1
  114. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-CmbpPX1u.js → computer-device-mobile-phone-android-samsung-back.svg-DxHwZI5u.js} +1 -1
  115. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-_WGKWVtZ.js → computer-device-mobile-phone-android-samsung.svg-CnYqoXt4.js} +1 -1
  116. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-DmLh97KG.js → computer-device-mobile-phone-iphone-x-back.svg-D8JfsF_0.js} +1 -1
  117. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-C7-5mdyV.js → computer-device-mobile-phone-iphone-x.svg-CUbRWc7v.js} +1 -1
  118. package/web/assets/{computer-device-mobile-tablet-touch.svg-DiUaSnhU.js → computer-device-mobile-tablet-touch.svg-CBX2kbI1.js} +1 -1
  119. package/web/assets/{computer-device-mobile-tablet.svg-B6nfbYm_.js → computer-device-mobile-tablet.svg-CIn05KEg.js} +1 -1
  120. package/web/assets/{computer-device-network-ethernet-cat6.svg-DwtVKy79.js → computer-device-network-ethernet-cat6.svg-Dj0GRqXd.js} +1 -1
  121. package/web/assets/{computer-device-network-lan-www.svg-vQKfG3Xz.js → computer-device-network-lan-www.svg-D-G7js6t.js} +1 -1
  122. package/web/assets/{computer-device-network-wifi-connection.svg-DhHaOUge.js → computer-device-network-wifi-connection.svg-JnvMwBMA.js} +1 -1
  123. package/web/assets/{computer-device-network-wifi-router.svg-1dQS4k4J.js → computer-device-network-wifi-router.svg--XxSrk_J.js} +1 -1
  124. package/web/assets/{cssMode-CdQL22W5.js → cssMode-C3gHbz_w.js} +1 -1
  125. package/web/assets/{divider-DhYR4BAO.js → divider-eMI33mv1.js} +2 -2
  126. package/web/assets/{docx-preview-D-jP_CCs.js → docx-preview-CTkzTOcl.js} +1 -1
  127. package/web/assets/{ecology-science-erlenmeyer-flask.svg-D76wQVA2.js → ecology-science-erlenmeyer-flask.svg-DrHKX7bH.js} +1 -1
  128. package/web/assets/{fieldset-BtOjs_Hh.js → fieldset-CiIJH8Xe.js} +2 -2
  129. package/web/assets/{freemarker2-BADW9vhy.js → freemarker2-DWxB7Wcd.js} +1 -1
  130. package/web/assets/{handlebars-CwWQD_C8.js → handlebars-CWUdFsNH.js} +1 -1
  131. package/web/assets/{html-y87CEbe3.js → html-DhvF7iHg.js} +1 -1
  132. package/web/assets/{htmlMode-CVagtvVh.js → htmlMode-CIuDa6YW.js} +1 -1
  133. package/web/assets/{image-flash-lightning.svg-OvpKi3a3.js → image-flash-lightning.svg-qFK_1eYx.js} +1 -1
  134. package/web/assets/{image-picture-flower.svg-DLOePozT.js → image-picture-flower.svg-qAmkHnOu.js} +1 -1
  135. package/web/assets/{index-CcIrffU_.js → index-CzSAFmCu.js} +363 -363
  136. package/web/assets/index-D0IPNhsg.css +1 -0
  137. package/web/assets/{interface-alert-exclamation-diamond.svg-4Vk7SoQ1.js → interface-alert-exclamation-diamond.svg-Dugh0ymo.js} +1 -1
  138. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-BLAvc4am.js → interface-alert-exclamation-triangle-warning.svg-8neoRFVP.js} +1 -1
  139. package/web/assets/{interface-alert-information-circle.svg-CaAXpDAI.js → interface-alert-information-circle.svg-DM9S7DBv.js} +1 -1
  140. package/web/assets/{interface-award-crown.svg-CW7XG0wx.js → interface-award-crown.svg--68HCcUG.js} +1 -1
  141. package/web/assets/{interface-bookmark.svg-BpLEuOv2.js → interface-bookmark-tag.svg-BESGrlZw.js} +1 -1
  142. package/web/assets/{interface-bookmark-tag.svg-DPWVVeZU.js → interface-bookmark.svg-CVadIwuA.js} +1 -1
  143. package/web/assets/{interface-calendar-date-one.svg-DbBgvI4W.js → interface-calendar-date-one.svg-D9XP9Dv_.js} +1 -1
  144. package/web/assets/{interface-content-book-open-pages.svg-BbsuvPZM.js → interface-content-book-open-pages.svg-DMI3yFmk.js} +1 -1
  145. package/web/assets/{interface-content-book-page.svg-DbRPz1j8.js → interface-content-book-page.svg-BPxSHq3g.js} +1 -1
  146. package/web/assets/{interface-content-file.svg-OJPKABxS.js → interface-content-file.svg-D0Mbjb8x.js} +1 -1
  147. package/web/assets/interface-content-folder.svg-D3wbwhAD.js +1 -0
  148. package/web/assets/{interface-content-folder.svg-3D_5OE_-.js → interface-content-folder.svg-EUO1l7Mn.js} +1 -1
  149. package/web/assets/{interface-copy-clipboard.svg-DqthVh9J.js → interface-copy-clipboard.svg-DI-wp72J.js} +1 -1
  150. package/web/assets/{interface-edit-attachment.svg-BAPCnZ0B.js → interface-edit-attachment.svg-CrWbaWn_.js} +1 -1
  151. package/web/assets/{interface-edit-binocular.svg-BoV39ouF.js → interface-edit-binocular.svg-BzUDq1Il.js} +1 -1
  152. package/web/assets/{interface-edit-magic-wand.svg-Cz36nKVn.js → interface-edit-magic-wand.svg-DaAdEL5x.js} +1 -1
  153. package/web/assets/{interface-edit-tool-paint-roller.svg-DZUQotD5.js → interface-edit-tool-paint-roller.svg-C972BXSM.js} +1 -1
  154. package/web/assets/{interface-edit-tool-pencil.svg-DBf1Y9Tj.js → interface-edit-tool-pencil.svg-47QCYH9f.js} +1 -1
  155. package/web/assets/{interface-favorite-award.svg-B93sRjxf.js → interface-favorite-award.svg-DROP-rzN.js} +1 -1
  156. package/web/assets/{interface-favorite-flag.svg-DKHuT11J.js → interface-favorite-flag.svg-B1gpMdkg.js} +1 -1
  157. package/web/assets/{interface-favorite-heart.svg-Bw5FZQ7d.js → interface-favorite-heart.svg-Dc0MMGJl.js} +1 -1
  158. package/web/assets/{interface-favorite-star.svg-CgLbct_b.js → interface-favorite-star.svg-D6A5KWft.js} +1 -1
  159. package/web/assets/{interface-favorite-stars-sparkles.svg-B7cYS4ef.js → interface-favorite-stars-sparkles.svg-CpEr4y41.js} +1 -1
  160. package/web/assets/{interface-hierarchy-flowchart.svg-zwuaRdWq.js → interface-hierarchy-flowchart.svg-DjvgRFcZ.js} +1 -1
  161. package/web/assets/{interface-home-house.svg-B85yMFCe.js → interface-home-house.svg-BMmVKlSc.js} +1 -1
  162. package/web/assets/{interface-hyperlink.svg-US6b-Svn.js → interface-hyperlink.svg-BewFXeyZ.js} +1 -1
  163. package/web/assets/{interface-lighting-brightness.svg-mZWkhb7f.js → interface-lighting-brightness.svg-B74_Fwt9.js} +1 -1
  164. package/web/assets/{interface-lock-closed.svg-9oHuNwuY.js → interface-lock-closed.svg-B_kTyHSd.js} +1 -1
  165. package/web/assets/{interface-lock-open-unlock.svg-DBetdJny.js → interface-lock-open-unlock.svg-BpmOnQWL.js} +1 -1
  166. package/web/assets/{interface-login-key.svg-BZnZTYd1.js → interface-login-key.svg-CjYpI4bh.js} +1 -1
  167. package/web/assets/{interface-search.svg-CMXAOoam.js → interface-search.svg-FJmFWdB4.js} +1 -1
  168. package/web/assets/{interface-setting-cog.svg-CKckatt7.js → interface-setting-cog.svg-CfzSne-G.js} +1 -1
  169. package/web/assets/{interface-share-megaphone-bullhorn.svg-DH7kVqN-.js → interface-share-megaphone-bullhorn.svg-CIIzDsMB.js} +1 -1
  170. package/web/assets/{interface-share-rocket.svg-BNXcMugi.js → interface-share-rocket.svg-DqTBI7lO.js} +1 -1
  171. package/web/assets/{interface-share-satellite.svg-Bb8t607w.js → interface-share-satellite.svg-nJfwIxpE.js} +1 -1
  172. package/web/assets/{interface-share-space-ship.svg-Dk4rbLtF.js → interface-share-space-ship.svg-hSmFC9Bt.js} +1 -1
  173. package/web/assets/{interface-share.svg-CtX8dq5i.js → interface-share.svg-DW5W5LS6.js} +1 -1
  174. package/web/assets/{interface-signal-square.svg-DmUIlYTd.js → interface-signal-square.svg-CxjWE1n9.js} +1 -1
  175. package/web/assets/{interface-time-clock-circle.svg-D2ZNaXT3.js → interface-time-clock-circle.svg-OliTsm3U.js} +1 -1
  176. package/web/assets/{interface-time-hour-glass.svg-Ddo-8hHe.js → interface-time-hour-glass.svg-Cq5G77ft.js} +1 -1
  177. package/web/assets/{interface-users-multiple.svg-Jh7S0sT4.js → interface-users-multiple.svg-BZxMlLou.js} +1 -1
  178. package/web/assets/{interface-weather-moon.svg-DbdQFgL2.js → interface-weather-moon.svg-lFXrwEyz.js} +1 -1
  179. package/web/assets/{javascript-BVGFyXBk.js → javascript-BVZ1omk1.js} +1 -1
  180. package/web/assets/{jsonMode-vnFlxYl9.js → jsonMode-CiBARvaV.js} +2 -2
  181. package/web/assets/{liquid-DieKMEvN.js → liquid-CUYREU3G.js} +1 -1
  182. package/web/assets/{lspLanguageFeatures-DoeI-8Y7.js → lspLanguageFeatures-BCfkeNgB.js} +3 -3
  183. package/web/assets/{mail-chat-bubble-square.svg-CApdU2ea.js → mail-chat-bubble-square.svg-DbOh-u2f.js} +1 -1
  184. package/web/assets/{mail-send-email-paper-airplane.svg-jjZ0KB32.js → mail-send-email-paper-airplane.svg-Ci3654Ap.js} +1 -1
  185. package/web/assets/{mail-send-envelope.svg-B6Tsoh-8.js → mail-send-envelope.svg-BUCSUoVb.js} +1 -1
  186. package/web/assets/{mdx-BCkszXid.js → mdx-GNBOgT3C.js} +1 -1
  187. package/web/assets/{minus-DfLQX3s2.js → minus-qBqqwoxz.js} +2 -2
  188. package/web/assets/modal-CsKPvO45.js +5 -0
  189. package/web/assets/modal-E9FRG--u.js +1 -0
  190. package/web/assets/{money-cashier-receipt.svg-DM-I8Snr.js → money-cashier-receipt.svg-qOZhZZs2.js} +1 -1
  191. package/web/assets/{money-currency-dollar-pay.svg-CkDRewrV.js → money-currency-dollar-pay.svg-WYlopKIm.js} +1 -1
  192. package/web/assets/{money-graph-arrow-increase.svg-BT4KLHWz.js → money-graph-arrow-increase.svg-DAcyYWIw.js} +1 -1
  193. package/web/assets/{money-graph-bar-chart-increase.svg-EvyIJ3Uu.js → money-graph-bar-chart-increase.svg-Bcjxz1Kd.js} +1 -1
  194. package/web/assets/multiselect-DiGSzibm.js +251 -0
  195. package/web/assets/{nature-ecology-leaf.svg-uZBwXdg9.js → nature-ecology-leaf.svg-d0AKmyxH.js} +1 -1
  196. package/web/assets/{password-D1R5w1qL.js → password-BwMjgK0Q.js} +4 -4
  197. package/web/assets/{phone-telephone.svg-BSvOskIB.js → phone-telephone.svg-BzKK2wbN.js} +1 -1
  198. package/web/assets/{plus-CNGSRnxy.js → plus-D6Rlmwbp.js} +6 -6
  199. package/web/assets/process.store-I5VZVT0x.js +1 -0
  200. package/web/assets/{programming-bug.svg-CLnGs4K-.js → programming-bug.svg-BtSROvd5.js} +1 -1
  201. package/web/assets/{programming-cloud.svg-CxBovXBR.js → programming-cloud.svg-YDPFJdYt.js} +1 -1
  202. package/web/assets/{programming-computer-database-server.svg-C1JmOyio.js → programming-computer-database-server.svg-C_7cgE9Q.js} +1 -1
  203. package/web/assets/{programming-computer-database.svg-BRjyOzP4.js → programming-computer-database.svg-CLw76mik.js} +1 -1
  204. package/web/assets/{programming-module-four-layout.svg-Blwzw6Qz.js → programming-module-four-layout.svg-BsoC5ySI.js} +1 -1
  205. package/web/assets/{programming-module-three.svg-6bkrmWsU.js → programming-module-three.svg-CThoMoAk.js} +1 -1
  206. package/web/assets/{programming-module.svg-CBS9xKxX.js → programming-module.svg-BQ1YjjAj.js} +1 -1
  207. package/web/assets/{programming-script-code.svg-BVhyoAD3.js → programming-script-code.svg-BKT_wilO.js} +1 -1
  208. package/web/assets/project-launch.store-CBnJKGQe.js +1 -0
  209. package/web/assets/project.store-JIcOcKSg.js +1 -0
  210. package/web/assets/{python-CiPYtj_d.js → python-BXwGt-Ip.js} +1 -1
  211. package/web/assets/{radiobutton--KWBHcDP.js → radiobutton-D20sH1UN.js} +3 -3
  212. package/web/assets/{razor-CSoWGP30.js → razor-RXambmP2.js} +1 -1
  213. package/web/assets/router-BXj_oCbf.js +1 -0
  214. package/web/assets/{selectbutton-C0NL5n4w.js → selectbutton-rLcf19Xf.js} +4 -4
  215. package/web/assets/settings-scope-Cy_9OGE1.js +1 -0
  216. package/web/assets/settings.store-DrKeFdgW.js +1 -0
  217. package/web/assets/{shopping-cart.svg-C-8NG_Po.js → shopping-cart.svg-EjTn3XQF.js} +1 -1
  218. package/web/assets/{shopping-gift-present.svg-Bp6q5VzJ.js → shopping-gift-present.svg-AQNlzazh.js} +1 -1
  219. package/web/assets/{shopping-shipping-box-parcel-package.svg-DeWaEIwm.js → shopping-shipping-box-parcel-package.svg-VB9n6UNi.js} +1 -1
  220. package/web/assets/src-ByEHJyfr.js +1 -0
  221. package/web/assets/{src-BHR3l7pa.js → src-CUwD1xPP.js} +35 -35
  222. package/web/assets/src-C_auNJtg.css +1 -0
  223. package/web/assets/{tag-new-circle.svg-CTljXsqE.js → tag-new-circle.svg-BllOd5JW.js} +1 -1
  224. package/web/assets/theme-DuAHBQRP.js +1 -0
  225. package/web/assets/{toggleswitch-d41l5g27.js → toggleswitch-C-88_GnB.js} +2 -2
  226. package/web/assets/{travel-map-earth-globe.svg-BUkcntCp.js → travel-map-earth-globe.svg-ChLDmmU9.js} +1 -1
  227. package/web/assets/trpc-BoKwANhj.js +1 -0
  228. package/web/assets/{tsMode-B2c-rCAa.js → tsMode-CIN3QIw8.js} +6 -6
  229. package/web/assets/{typescript-CmW4u_GQ.js → typescript-wXu_7Eym.js} +1 -1
  230. package/web/assets/useAiChatDrawer-Bs1Gxhn2.js +1 -0
  231. package/web/assets/{useCloudOrg-E4l-99WA.js → useCloudOrg-0AZ58M3V.js} +1 -1
  232. package/web/assets/useEnvVarAutocomplete-BXC_JUF7.js +102 -0
  233. package/web/assets/{useEnvVarAutocomplete-DsxEF6Ya.css → useEnvVarAutocomplete-dxAmKsn7.css} +1 -1
  234. package/web/assets/useProjectCloudOrg-UIfVePy9.js +1 -0
  235. package/web/assets/useServiceUrlHost-BXbIxPaT.js +1 -0
  236. package/web/assets/{xml-BxMqu3hT.js → xml-FTYGqn2R.js} +1 -1
  237. package/web/assets/{yaml-Djx7E3R1.js → yaml-DQEEK2dU.js} +1 -1
  238. package/web/index.html +2 -2
  239. package/agent-manager-FUU72VGH.js +0 -11
  240. package/chat-C26CBAZX.js +0 -21
  241. package/chunk-5EMEJNVU.js.map +0 -1
  242. package/chunk-FJLMXLMK.js.map +0 -1
  243. package/chunk-NURPXHLB.js.map +0 -1
  244. package/chunk-P65BO7WZ.js.map +0 -1
  245. package/chunk-ZFELQG4B.js.map +0 -1
  246. package/desktop-launcher-3COY6F6H.js.map +0 -1
  247. package/desktop-wizard-4CYANX2O.js +0 -209
  248. package/desktop-wizard-4CYANX2O.js.map +0 -1
  249. package/settings-manager-J4QBIU3T.js +0 -10
  250. package/src-7O3GCEIB.js.map +0 -1
  251. package/web/assets/AgentScalarChatInterface.vue-BGGkpyp3.js +0 -13
  252. package/web/assets/AgentTerminalPage-B8gLray2.js +0 -1
  253. package/web/assets/AiChatPage-BKITsyMx.js +0 -1
  254. package/web/assets/AppLayout-B72S4nTi.css +0 -1
  255. package/web/assets/AppLayout-BYw7v2sp.js +0 -1
  256. package/web/assets/AuditPage-nHTwzyia.js +0 -1
  257. package/web/assets/CloudAccountPage-DYOvfMjD.css +0 -1
  258. package/web/assets/CloudAccountPage-DuaZz2j4.js +0 -1
  259. package/web/assets/CloudAccountPage-XHxI-ixD.js +0 -1
  260. package/web/assets/CloudMessagingPage-B2N5K-Xs.js +0 -2
  261. package/web/assets/DashboardPage-VoWJeWh_.js +0 -1
  262. package/web/assets/DirectoryPickerDialog-B5SuJOhv.js +0 -1
  263. package/web/assets/DirectoryPickerDialog-Ctp1lYoS.css +0 -1
  264. package/web/assets/EnvironmentFormPage-BIXbPTN5.js +0 -1
  265. package/web/assets/EnvironmentListPage-CHa-Ni3w.js +0 -1
  266. package/web/assets/KanbanBoardPage-B46zbE2H.css +0 -1
  267. package/web/assets/KanbanBoardPage-BFTNpm8P.js +0 -4
  268. package/web/assets/KanbanListPage-CYuIrKXH.js +0 -1
  269. package/web/assets/LaunchProjectPage-9hhGBdOh.css +0 -1
  270. package/web/assets/LaunchProjectPage-jy-R3Fiw.js +0 -1
  271. package/web/assets/LaunchServicesPage-BMtm_8yP.css +0 -1
  272. package/web/assets/LaunchServicesPage-DX7nyo36.js +0 -1
  273. package/web/assets/LaunchShell-BHfseuPM.js +0 -1
  274. package/web/assets/LaunchShell-HenrOlHV.css +0 -1
  275. package/web/assets/LogViewer-C-pHXudR.js +0 -3
  276. package/web/assets/OrgKeyPrompt-CrHXZdKL.css +0 -1
  277. package/web/assets/OrgKeyPrompt-OPnK5oPm.js +0 -1
  278. package/web/assets/OrgLogo-Cxa49Tl3.js +0 -1
  279. package/web/assets/ProcessControls-DXDyR6XX.js +0 -1
  280. package/web/assets/ProcessDetailPage-DNKTwgPW.js +0 -1
  281. package/web/assets/RandomJobTestPage-BE8HkgSb.js +0 -2
  282. package/web/assets/ScenarioEditorPage-Coo6ExzZ.js +0 -14
  283. package/web/assets/SendToChatDialog-ClXerpCU.js +0 -1
  284. package/web/assets/ServiceControlsBar-C7HrPjQI.js +0 -1
  285. package/web/assets/ServiceDetailPage-Cxuy0Yix.js +0 -2
  286. package/web/assets/ServiceFormPage-CCOu1h8v.js +0 -1
  287. package/web/assets/SettingsPage-Dt1ViDQe.js +0 -2063
  288. package/web/assets/UnlockPage-C5iEOAKH.js +0 -1
  289. package/web/assets/VaultMigrationPage-CwRWvFY7.js +0 -1
  290. package/web/assets/VersionBadge-Bqb72GXf.js +0 -1
  291. package/web/assets/WikiPage-Bl1jDKKp.js +0 -1
  292. package/web/assets/WikiView-58u6vU3q.js +0 -1
  293. package/web/assets/add-scalar-classes-CT5KQbzH.js +0 -387
  294. package/web/assets/card-46tM5yU0.js +0 -32
  295. package/web/assets/client-KL5c1Dog.js +0 -24
  296. package/web/assets/cloud-chat.store-Belo5SKd.js +0 -1
  297. package/web/assets/cloud-client-Cqa2Udt8.js +0 -1
  298. package/web/assets/cloud-session-56bUtkSL.js +0 -2
  299. package/web/assets/cloud-session-Dqm0C6H5.js +0 -1
  300. package/web/assets/column-DQ53_qc6.js +0 -763
  301. package/web/assets/composables-CHyZa9ag.js +0 -1
  302. package/web/assets/index-CKOtBNaZ.css +0 -1
  303. package/web/assets/interface-content-folder.svg-8mYqkDao.js +0 -1
  304. package/web/assets/modal-CCco27Sn.js +0 -5
  305. package/web/assets/modal-D4J85_wC.js +0 -1
  306. package/web/assets/multiselect-BmjidXlj.js +0 -251
  307. package/web/assets/process.store-p1LqnpY2.js +0 -1
  308. package/web/assets/project-launch.store-BBYtCcv8.js +0 -1
  309. package/web/assets/project.store-D_2v7amt.js +0 -1
  310. package/web/assets/router-CRKT3825.js +0 -1
  311. package/web/assets/settings-scope-DblmUa3N.js +0 -1
  312. package/web/assets/settings.store-DKTQyerJ.js +0 -1
  313. package/web/assets/src-BN5gTC76.css +0 -1
  314. package/web/assets/src-BRLkogf3.js +0 -1
  315. package/web/assets/theme-BZzt_Sgl.js +0 -1
  316. package/web/assets/trpc-Db0Atu8A.js +0 -1
  317. package/web/assets/useAiChatDrawer-0QJJVXul.js +0 -1
  318. package/web/assets/useEnvVarAutocomplete-BDqUWoGr.js +0 -102
  319. package/web/assets/useProjectCatalog-Cxt-hL1H.js +0 -1
  320. package/web/assets/useProjectCloudOrg-CwLTQLIO.js +0 -1
  321. package/web/assets/useServiceUrlHost-VIA3uP5-.js +0 -1
  322. /package/{agent-manager-FUU72VGH.js.map → agent-manager-NAGIMH2T.js.map} +0 -0
  323. /package/{chat-C26CBAZX.js.map → chat-Q2UMYXQL.js.map} +0 -0
  324. /package/{chunk-BDQOBEZ3.js.map → chunk-GOMF7NWS.js.map} +0 -0
  325. /package/{chunk-J4GLFY7G.js.map → chunk-MSICJEZD.js.map} +0 -0
  326. /package/{chunk-I7JHF5FE.js.map → chunk-XHGJHYDC.js.map} +0 -0
  327. /package/{companion-Q67RAO3N.js.map → companion-MGX62L4T.js.map} +0 -0
  328. /package/{node-4UUJN2WG.js.map → desktop-launcher-HKXP4LRI.js.map} +0 -0
  329. /package/{settings-manager-J4QBIU3T.js.map → desktop-wizard-XIDDAG7L.js.map} +0 -0
  330. /package/{vault-resolver-57Q2DVOR.js.map → node-RN4URNZ5.js.map} +0 -0
  331. /package/web/assets/{abap-BwLGULgU.js → abap-CRsXUZTC.js} +0 -0
  332. /package/web/assets/{apex-BmwhxS2n.js → apex-CN6ZSzwM.js} +0 -0
  333. /package/web/assets/{azcli-7RvE_Zal.js → azcli-ULFzG1xV.js} +0 -0
  334. /package/web/assets/{bat-DRPmS6Na.js → bat-BVH8lwB4.js} +0 -0
  335. /package/web/assets/{bicep-BwfkS7ZV.js → bicep-B8jMqZ8i.js} +0 -0
  336. /package/web/assets/{browser-D3d3061H.js → browser-xa0YgePN.js} +0 -0
  337. /package/web/assets/{cameligo-BJ_mdC8E.js → cameligo-DuuRwBiE.js} +0 -0
  338. /package/web/assets/{clojure-CQKq7DWt.js → clojure-DUpiEmPZ.js} +0 -0
  339. /package/web/assets/{coffee-DJRj2iwd.js → coffee-BO4ou798.js} +0 -0
  340. /package/web/assets/{cpp-BC1yJTUW.js → cpp-CHOtY2DW.js} +0 -0
  341. /package/web/assets/{csharp-D6U2fAAT.js → csharp-D3t5O1XD.js} +0 -0
  342. /package/web/assets/{csp-B2Ue_UmV.js → csp-CJpJWgPE.js} +0 -0
  343. /package/web/assets/{css-CEzlcbMY.js → css-CAeYpQS5.js} +0 -0
  344. /package/web/assets/{cypher-D5Sg38Yj.js → cypher-D9-TC6ep.js} +0 -0
  345. /package/web/assets/{dart-BhjKRv6v.js → dart-CYoMdUUD.js} +0 -0
  346. /package/web/assets/{dockerfile-BnivEbtE.js → dockerfile-B2ffS5e2.js} +0 -0
  347. /package/web/assets/{ecl-TS1StLK1.js → ecl-BL_MLv5o.js} +0 -0
  348. /package/web/assets/{elixir-BX9ta6DO.js → elixir-DTTK5HpM.js} +0 -0
  349. /package/web/assets/{flow9-CoOB42ee.js → flow9-CnqIliGA.js} +0 -0
  350. /package/web/assets/{fsharp-DmQslDFn.js → fsharp-BtsygJkw.js} +0 -0
  351. /package/web/assets/{go-BbEGGooY.js → go-DksUoC0w.js} +0 -0
  352. /package/web/assets/{graphql-E24y5sn9.js → graphql-DCtJGb0Q.js} +0 -0
  353. /package/web/assets/{hcl-pBZphP8G.js → hcl-Vm6MaTNZ.js} +0 -0
  354. /package/web/assets/{ini-BSbRgr25.js → ini-DdyyiZLB.js} +0 -0
  355. /package/web/assets/{java-D8KvzsMp.js → java-D9tw0Sq3.js} +0 -0
  356. /package/web/assets/{julia-BDRytf15.js → julia-DsaDhMSi.js} +0 -0
  357. /package/web/assets/{kotlin-CNcQo-uP.js → kotlin-eMyqSg05.js} +0 -0
  358. /package/web/assets/{less-0HRV-2Rz.js → less-BTExquT2.js} +0 -0
  359. /package/web/assets/{lexon-DdJG3_pW.js → lexon-Btiu1CPR.js} +0 -0
  360. /package/web/assets/{lua-Blq8Mukj.js → lua-uIHSqSt_.js} +0 -0
  361. /package/web/assets/{m3-CXbmzr9W.js → m3-DtY-QwwW.js} +0 -0
  362. /package/web/assets/{markdown-DFIISzT8.js → markdown-DalwAnMR.js} +0 -0
  363. /package/web/assets/{mips-C2XSYglv.js → mips-BocGRfi2.js} +0 -0
  364. /package/web/assets/{msdax-D3Jl6oAY.js → msdax-CQFbyz50.js} +0 -0
  365. /package/web/assets/{mysql-mCqxt_Co.js → mysql-BM8uo0J8.js} +0 -0
  366. /package/web/assets/{objective-c-CUKLr4E9.js → objective-c-H3c5-SZb.js} +0 -0
  367. /package/web/assets/{pascal-BpqDI6Ur.js → pascal-BgiV8eY7.js} +0 -0
  368. /package/web/assets/{pascaligo-BVchPyL4.js → pascaligo-DT9ZcxD2.js} +0 -0
  369. /package/web/assets/{perl-Dgc9v23P.js → perl-B0U4-W-w.js} +0 -0
  370. /package/web/assets/{pgsql-Dk9IX1G_.js → pgsql-B6kZRXs9.js} +0 -0
  371. /package/web/assets/{php-XeFLn3yL.js → php-DG9vCwQM.js} +0 -0
  372. /package/web/assets/{pla-DQMfrdXZ.js → pla-B4Y4wk5I.js} +0 -0
  373. /package/web/assets/{postiats-DiKrdL3K.js → postiats-C_eWfeAP.js} +0 -0
  374. /package/web/assets/{powerquery-Cj9Q91Zv.js → powerquery-DfX2HFDF.js} +0 -0
  375. /package/web/assets/{powershell-DxLElnzU.js → powershell-Bxsmga8Z.js} +0 -0
  376. /package/web/assets/{protobuf-7vT2HdZD.js → protobuf-BjgmCR9H.js} +0 -0
  377. /package/web/assets/{pug-oi96TUbQ.js → pug-ryXuxA3i.js} +0 -0
  378. /package/web/assets/{qsharp-DNr9IwS2.js → qsharp-91Kv1jwy.js} +0 -0
  379. /package/web/assets/{r-C-REi_Zk.js → r-j6OkO-Lp.js} +0 -0
  380. /package/web/assets/{redis-BvOCrLoN.js → redis-B2Usw_gW.js} +0 -0
  381. /package/web/assets/{redshift-jNEw-REz.js → redshift-Bl5n-UlH.js} +0 -0
  382. /package/web/assets/{restructuredtext-B9ET-x7N.js → restructuredtext-IIWeXmeu.js} +0 -0
  383. /package/web/assets/{ruby-BIEBiJDZ.js → ruby-DQH1R4pC.js} +0 -0
  384. /package/web/assets/{rust-BnoIVewm.js → rust-BJi0001n.js} +0 -0
  385. /package/web/assets/{sb-Bwvy9clh.js → sb-C34JQX0H.js} +0 -0
  386. /package/web/assets/{scala-DPv5zS47.js → scala-BqHjuqsC.js} +0 -0
  387. /package/web/assets/{scheme-BQqNVxf_.js → scheme-DIfjDH23.js} +0 -0
  388. /package/web/assets/{scss-Ck1AFDEz.js → scss-MxgEDdb7.js} +0 -0
  389. /package/web/assets/{shell-C4IYs9Z7.js → shell-D_SMEq6l.js} +0 -0
  390. /package/web/assets/{solidity-D3bS77xq.js → solidity-CyBHDO6p.js} +0 -0
  391. /package/web/assets/{sophia-BSTVfueD.js → sophia-8_L-7Xrk.js} +0 -0
  392. /package/web/assets/{sparql-BGcXD7s2.js → sparql-CXxyP-1c.js} +0 -0
  393. /package/web/assets/{sql-CIsSgyam.js → sql-DNd4NFJw.js} +0 -0
  394. /package/web/assets/{st-dV1d_AXx.js → st-DPFJcK-4.js} +0 -0
  395. /package/web/assets/{swift-CK8gXWQn.js → swift-DE_LJ5Ub.js} +0 -0
  396. /package/web/assets/{systemverilog-B9tHBIJU.js → systemverilog-IgPgwLQ7.js} +0 -0
  397. /package/web/assets/{tcl-BgRT60RV.js → tcl-Cr-fmmH0.js} +0 -0
  398. /package/web/assets/{twig-CGkLO4Nl.js → twig-Dd_ODPfV.js} +0 -0
  399. /package/web/assets/{typespec-DMP9WuzC.js → typespec-CeK8X8vB.js} +0 -0
  400. /package/web/assets/{uuid-H0yY5A03.js → uuid-6HQMUkhT.js} +0 -0
  401. /package/web/assets/{vb-DfSbhl3d.js → vb-CyLVxxH6.js} +0 -0
  402. /package/web/assets/{wgsl-CaJmoLY5.js → wgsl-CmRFGM3r.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/desktop-installer-page.ts","../src/desktop-installer-html.ts"],"sourcesContent":["/**\n * La page d'installation du raccourci de bureau, servie par `runeya install-desktop`.\n *\n * Runeya n'est PAS démarrée : ni agent, ni répertoire de données. Un serveur\n * HTTP éphémère, sur la boucle locale et un port tiré par le système, sert une\n * page dédiée où l'on choisit le nom et les ports. Le raccourci créé ici est\n * toujours global ; celui d'un projet se crée depuis l'app, dans les réglages. Le raccourci\n * écrit, il s'arrête et rend la main au terminal.\n *\n * Deux gardes, parce qu'un port local est joignable par n'importe quel onglet :\n * l'en-tête `Host` doit nommer ce serveur (pas de rebinding DNS), et une\n * écriture doit venir de sa propre origine, en JSON — ce qu'un formulaire d'un\n * autre site ne peut pas envoyer sans pré-vol.\n */\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { isAbsolute } from 'node:path';\nimport type { AddressInfo } from 'node:net';\nimport { deriveIdentity, launcherProblems, type InstalledLauncher, type LauncherResult } from './desktop-launcher.js';\nimport { findFreePortPair, isPortFree } from './free-port.js';\nimport { shellArg, whereToFind } from './desktop-wizard.js';\nimport { installerHtml } from './desktop-installer-html.js';\n\n/** Les ports par défaut de Runeya, proposés parmi les choix. */\nconst DEFAULT_PORT = 4000;\n\n/** Au-delà, une requête n'est pas un formulaire de quatre champs. */\nconst MAX_BODY_BYTES = 16 * 1024;\n\nexport type InstallerAnswers = {\n workingDir: string | null;\n name: string;\n port: number | null;\n agentPort: number | null;\n};\n\n/** Ce que la page affiche avant tout choix. */\nexport type InstallerState = {\n /** Le nom proposé : celui d'un raccourci global. */\n name: string;\n /** Une paire libre tirée au hasard, `null` si aucune n'a été trouvée. */\n suggestedPort: number | null;\n defaultPort: number;\n defaultsFree: boolean;\n};\n\nfunction isPort(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65_535;\n}\n\n/**\n * Valider le corps d'une installation. Rejette plutôt que de corriger : un nom\n * trop long ou un port hors borne est une erreur à montrer, pas à raboter.\n */\nexport function parseInstallRequest(body: unknown): InstallerAnswers | string {\n if (typeof body !== 'object' || body === null) return 'Requête invalide.';\n const b = body as Record<string, unknown>;\n\n if (typeof b['name'] !== 'string') return 'Nom manquant.';\n const name = b['name'].trim();\n if (name.length === 0) return 'Le nom ne peut pas être vide.';\n if (name.length > 120) return 'Le nom ne doit pas dépasser 120 caractères.';\n\n const workingDir = b['workingDir'];\n if (workingDir !== null && (typeof workingDir !== 'string' || workingDir.length > 4096 || !isAbsolute(workingDir))) {\n return 'Le dossier de travail doit être un chemin absolu.';\n }\n\n const port = b['port'];\n const agentPort = b['agentPort'];\n if (port !== null && !isPort(port)) return 'Port de Runeya invalide.';\n if (agentPort !== null && !isPort(agentPort)) return 'Port de l’agent invalide.';\n if ((port === null) !== (agentPort === null)) return 'Les deux ports vont ensemble.';\n if (port !== null && port === agentPort) return 'Runeya et l’agent ne peuvent pas partager un port.';\n\n return { name, workingDir: workingDir as string | null, port: port as number | null, agentPort: agentPort as number | null };\n}\n\n/** L'en-tête `Host` nomme-t-il ce serveur ? */\nexport function isOwnHost(host: string | undefined, port: number): boolean {\n return host === `127.0.0.1:${port}` || host === `localhost:${port}`;\n}\n\nasync function readJson(req: IncomingMessage): Promise<unknown> {\n let size = 0;\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n size += (chunk as Buffer).length;\n if (size > MAX_BODY_BYTES) throw new Error('too-large');\n chunks.push(chunk as Buffer);\n }\n return JSON.parse(Buffer.concat(chunks).toString('utf8'));\n}\n\nfunction send(res: ServerResponse, status: number, body: unknown, type = 'application/json; charset=utf-8'): void {\n res.writeHead(status, {\n 'Content-Type': type,\n 'Cache-Control': 'no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'Referrer-Policy': 'no-referrer',\n 'Content-Security-Policy': \"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'; img-src data:\",\n });\n res.end(typeof body === 'string' ? body : JSON.stringify(body));\n}\n\nexport type InstallerOutcome = { answers: InstallerAnswers; result: LauncherResult };\n\n/** Un raccourci existant, tel que la page le montre. */\nexport type LauncherView = {\n id: string;\n name: string;\n workingDir: string | null;\n port: number | null;\n agentPort: number | null;\n paths: string[];\n /** Ce qui l'empêcherait de démarrer. */\n problems: string[];\n /** Il vise un autre Node ou un autre Runeya que celui-ci : le recréer le repointe. */\n outdated: boolean;\n};\n\nasync function toView(l: InstalledLauncher, current: { nodePath: string; cliEntry: string }): Promise<LauncherView> {\n return {\n id: l.id,\n name: l.name,\n workingDir: l.workingDir,\n port: l.port,\n agentPort: l.agentPort,\n paths: l.paths,\n problems: await launcherProblems(l),\n outdated: l.nodePath !== current.nodePath || l.cliEntry !== current.cliEntry,\n };\n}\n\n/** Le corps d'une action sur un raccourci existant : son id, rien d'autre. */\nfunction parseLauncherRef(body: unknown): string | null {\n const id = (body as { id?: unknown } | null)?.id;\n return typeof id === 'string' && id.length > 0 && id.length <= 200 ? id : null;\n}\n\n/**\n * Servir la page et attendre qu'on y installe — ou qu'on y renonce.\n *\n * `install` écrit le raccourci ; il est appelé ici, dans la requête, pour que la\n * page affiche le résultat réel et non une promesse. Rend `null` si l'on annule\n * depuis la page ou par Ctrl+C.\n */\nexport async function runInstallerPage(opts: {\n openUrl: (url: string) => Promise<unknown>;\n install: (answers: InstallerAnswers) => Promise<LauncherResult>;\n /** Les raccourcis déjà posés, relus à chaque demande. */\n list: () => Promise<InstalledLauncher[]>;\n uninstall: (launcher: InstalledLauncher) => Promise<LauncherResult>;\n /** Le Node et l'entrée du CLI en cours — ceux qu'un raccourci recréé visera. */\n current: { nodePath: string; cliEntry: string };\n}): Promise<InstallerOutcome | null> {\n const suggestedPort = await findFreePortPair();\n const state: InstallerState = {\n name: deriveIdentity(undefined, null).name,\n suggestedPort,\n defaultPort: DEFAULT_PORT,\n defaultsFree: (await isPortFree(DEFAULT_PORT)) && (await isPortFree(DEFAULT_PORT + 1)),\n };\n\n return new Promise((resolvePromise) => {\n let settled = false;\n let installing = false;\n\n const server = createServer((req, res) => {\n void handle(req, res).catch(() => send(res, 500, { error: 'Erreur interne.' }));\n });\n\n function finish(outcome: InstallerOutcome | null): void {\n if (settled) return;\n settled = true;\n process.off('SIGINT', onSigint);\n // Laisser partir la réponse en cours avant de couper les connexions.\n setImmediate(() => {\n server.close();\n server.closeAllConnections();\n resolvePromise(outcome);\n });\n }\n\n function onSigint(): void {\n console.log('\\nAnnulé.');\n finish(null);\n }\n\n async function handle(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const { port } = server.address() as AddressInfo;\n if (!isOwnHost(req.headers.host, port)) return send(res, 421, { error: 'Hôte inattendu.' });\n\n const url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);\n\n if (req.method === 'GET' && url.pathname === '/') {\n return send(res, 200, installerHtml(), 'text/html; charset=utf-8');\n }\n if (req.method === 'GET' && url.pathname === '/api/state') {\n return send(res, 200, state);\n }\n if (req.method === 'GET' && url.pathname === '/api/launchers') {\n const launchers = await opts.list();\n return send(res, 200, await Promise.all(launchers.map((l) => toView(l, opts.current))));\n }\n\n if (req.method === 'POST') {\n const origin = req.headers.origin;\n if (!origin || !isOwnHost(origin.replace(/^http:\\/\\//, ''), port)) {\n return send(res, 403, { error: 'Origine refusée.' });\n }\n if (!req.headers['content-type']?.startsWith('application/json')) {\n return send(res, 415, { error: 'JSON attendu.' });\n }\n\n if (url.pathname === '/api/cancel') {\n send(res, 200, { ok: true });\n console.log('\\nAnnulé depuis la page.');\n return finish(null);\n }\n\n if (url.pathname === '/api/uninstall' || url.pathname === '/api/reinstall') {\n if (installing || settled) return send(res, 409, { error: 'Une opération est déjà en cours.' });\n let body: unknown;\n try {\n body = await readJson(req);\n } catch {\n return send(res, 400, { error: 'Requête invalide.' });\n }\n const id = parseLauncherRef(body);\n // Seul un raccourci relu sur le disque peut être visé : un nom reçu\n // tel quel deviendrait un chemin à supprimer.\n const launcher = id === null ? undefined : (await opts.list()).find((l) => l.id === id);\n if (!launcher) return send(res, 404, { error: 'Raccourci introuvable.' });\n\n installing = true;\n try {\n const result = url.pathname === '/api/uninstall'\n ? await opts.uninstall(launcher)\n : await opts.install({\n name: launcher.name,\n workingDir: launcher.workingDir,\n port: launcher.port,\n agentPort: launcher.agentPort,\n });\n return send(res, 200, { paths: result.paths, warnings: result.warnings });\n } catch (err) {\n return send(res, 500, { error: (err as Error).message });\n } finally {\n installing = false;\n }\n }\n\n if (url.pathname === '/api/install') {\n if (installing || settled) return send(res, 409, { error: 'Installation déjà en cours.' });\n let body: unknown;\n try {\n body = await readJson(req);\n } catch {\n return send(res, 400, { error: 'Requête invalide.' });\n }\n const parsed = parseInstallRequest(body);\n if (typeof parsed === 'string') return send(res, 400, { error: parsed });\n // Toujours global d'ici : un raccourci vers un projet se crée depuis l'app.\n const answers: InstallerAnswers = { ...parsed, workingDir: null };\n\n installing = true;\n try {\n const result = await opts.install(answers);\n send(res, 200, {\n name: answers.name,\n paths: result.paths,\n warnings: result.warnings,\n whereToFind: whereToFind(),\n uninstall: `runeya uninstall-desktop --name ${shellArg(answers.name)}`,\n });\n return finish({ answers, result });\n } catch (err) {\n // L'échec reste sur la page : corriger un champ et réessayer.\n return send(res, 500, { error: (err as Error).message });\n } finally {\n installing = false;\n }\n }\n }\n\n send(res, 404, { error: 'Introuvable.' });\n }\n\n process.on('SIGINT', onSigint);\n server.listen(0, '127.0.0.1', () => {\n const { port } = server.address() as AddressInfo;\n const url = `http://127.0.0.1:${port}/`;\n console.log(`\\nInstallateur du raccourci Runeya : ${url}`);\n console.log('Choisissez les réglages dans la page. Ctrl+C pour annuler.');\n opts.openUrl(url).catch(() => {\n console.log('Impossible d’ouvrir le navigateur — ouvrez l’adresse ci-dessus.');\n });\n });\n });\n}\n","/**\n * La page de l'installateur de raccourci, autonome : ni police, ni script, ni\n * feuille externes — le serveur qui la sert refuse tout le reste (CSP), et elle\n * doit s'afficher hors ligne.\n *\n * Deux langues, choisies d'après le navigateur ; le français par défaut, comme\n * les messages du CLI.\n */\nexport function installerHtml(): string {\n return `<!doctype html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Installer Runeya</title>\n<link rel=\"icon\" href=\"data:image/svg+xml,${encodeURIComponent(MARK)}\">\n<style>\n:root {\n --bg: #f6f6f9; --surface: #ffffff; --border: #e3e3ea; --text: #1d1d27;\n --muted: #6b6b7b; --primary: #6366f1; --primary-text: #ffffff;\n --danger: #dc2626; --ok: #16a34a; --warn: #b45309;\n --ease: cubic-bezier(0.23, 1, 0.32, 1);\n color-scheme: light;\n}\n@media (prefers-color-scheme: dark) {\n :root {\n --bg: #111118; --surface: #1a1a24; --border: #2c2c3a; --text: #ececf2;\n --muted: #9a9aab; --primary: #818cf8; --primary-text: #111118;\n --danger: #f87171; --ok: #4ade80; --warn: #fbbf24;\n color-scheme: dark;\n }\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;\n padding: 3rem 1rem; background: var(--bg); color: var(--text);\n font: 15px/1.5 system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n}\nmain { width: 100%; max-width: 1600px; display: flex; flex-direction: column; gap: 1.5rem;\n animation: enter 400ms var(--ease) both; }\nheader { display: flex; flex-direction: column; align-items: center; text-align: center; gap: .5rem; }\nheader svg { width: 48px; height: 48px; }\nh1 { margin: .25rem 0 0; font-size: 1.375rem; font-weight: 650; letter-spacing: -.01em; }\n.lead { margin: 0; color: var(--muted); font-size: .875rem; }\n.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 1.25rem;\n display: flex; flex-direction: column; gap: 1.25rem; box-shadow: 0 1px 2px rgb(0 0 0 / .04); }\nfieldset { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }\nlegend, label.title { font-size: .75rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;\n color: var(--muted); margin-bottom: .5rem; padding: 0; }\n.choice { display: flex; gap: .75rem; align-items: flex-start; padding: .75rem .875rem; border: 1px solid var(--border);\n border-radius: 10px; cursor: pointer; transition: border-color 150ms ease, box-shadow 150ms ease; }\n.choice:has(input:checked) { border-color: var(--primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent); }\n.choice input { margin: .2rem 0 0; accent-color: var(--primary); }\n.choice strong { display: block; font-weight: 600; font-size: .9375rem; }\n.choice span { display: block; color: var(--muted); font-size: .8125rem; overflow-wrap: anywhere; }\n.choice .warn { color: var(--danger); }\ninput[type=text], input[type=number] { width: 100%; padding: .625rem .75rem; border: 1px solid var(--border);\n border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; }\ninput:focus-visible, button:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n.ports { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }\n.ports label { font-size: .8125rem; color: var(--muted); display: flex; flex-direction: column; gap: .25rem; }\n[hidden] { display: none !important; }\n.actions { display: flex; gap: .75rem; }\nbutton { flex: 1; padding: .7rem 1rem; border-radius: 10px; border: 1px solid transparent; font: inherit;\n font-weight: 600; cursor: pointer; transition: transform 160ms var(--ease), opacity 150ms ease; }\nbutton:active { transform: scale(.97); }\nbutton:disabled { opacity: .6; cursor: default; }\n.primary { background: var(--primary); color: var(--primary-text); }\n.secondary { flex: 0 0 auto; background: transparent; color: var(--muted); border-color: var(--border); }\n.error { margin: 0; color: var(--danger); font-size: .875rem; }\n.done { text-align: center; align-items: center; width: 100%; max-width: 520px; align-self: center; }\n.done .check { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;\n background: color-mix(in srgb, var(--ok) 15%, var(--surface)); color: var(--ok); font-size: 1.4rem; }\n.done h2 { margin: 0; font-size: 1.125rem; }\n.done dl { width: 100%; margin: 0; text-align: left; display: flex; flex-direction: column; gap: .75rem; }\n.done dt { font-size: .75rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }\n.done dd { margin: .25rem 0 0; font-size: .875rem; overflow-wrap: anywhere; }\ncode { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .8125rem;\n background: color-mix(in srgb, var(--text) 7%, var(--surface)); padding: .15rem .4rem; border-radius: 6px; }\n.close { color: var(--muted); font-size: .8125rem; margin: 0; }\n.hint { margin: .375rem 0 0; color: var(--muted); font-size: .8125rem; }\n.split { display: grid; grid-template-columns: minmax(0, 520px); justify-content: center; gap: 1.5rem; align-items: start; }\n/*\n * Des raccourcis à gérer : la création reste au centre, à sa largeur ; les\n * existants prennent la colonne de droite, la gauche restant vide pour que le\n * centre ne bouge pas. En dessous de 1200 px, ils passent sous la création.\n */\n.split.has-existing { grid-template-columns: minmax(0, 1fr) minmax(0, 520px) minmax(0, 1fr); }\n.split.has-existing #form { grid-column: 2; grid-row: 1; }\n.split.has-existing .existing { grid-column: 3; grid-row: 1; }\n@media (max-width: 1200px) {\n .split.has-existing { grid-template-columns: minmax(0, 520px); }\n .split.has-existing #form, .split.has-existing .existing { grid-column: 1; grid-row: auto; }\n}\n.existing { gap: .75rem; order: 2; }\n.existing h2 { margin: 0; font-size: .75rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }\n.launcher-list { display: flex; flex-direction: column; gap: .5rem; }\n.launcher { display: flex; gap: .75rem; align-items: center; padding: .75rem .875rem; border: 1px solid var(--border); border-radius: 10px; }\n.launcher__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .125rem; }\n.launcher__body strong { font-weight: 600; font-size: .9375rem; }\n.launcher__body span { color: var(--muted); font-size: .8125rem; overflow-wrap: anywhere; }\n.launcher__body .problem { color: var(--danger); }\n.launcher__body .stale { color: var(--warn); }\n.launcher__actions { display: flex; gap: .375rem; flex-shrink: 0; }\n.launcher__actions button { flex: 0 0 auto; padding: .4rem .7rem; font-size: .8125rem; background: transparent;\n color: var(--text); border-color: var(--border); }\n.launcher__actions .danger { color: var(--danger); }\n.launcher__actions .confirm { background: var(--danger); border-color: var(--danger); color: var(--primary-text); }\n@media (max-width: 480px) { .launcher { flex-direction: column; align-items: stretch; } }\n@keyframes enter { from { opacity: 0; transform: translateY(8px); } }\n@media (prefers-reduced-motion: reduce) { main { animation: none; } button:active { transform: none; } }\n@media (max-width: 480px) { .ports { grid-template-columns: 1fr; } }\n</style>\n</head>\n<body>\n<main>\n <header>\n ${MARK}\n <h1 data-t=\"title\"></h1>\n <p class=\"lead\" data-t=\"lead\"></p>\n </header>\n\n <div class=\"split\" id=\"split\">\n <section class=\"card existing\" id=\"existing\" hidden>\n <h2 data-t=\"existingTitle\"></h2>\n <div id=\"launchers\" class=\"launcher-list\"></div>\n <p class=\"error\" id=\"list-error\" role=\"alert\" hidden></p>\n </section>\n\n <form id=\"form\" class=\"card\" novalidate>\n\n <div>\n <label class=\"title\" for=\"name\" data-t=\"name\"></label>\n <input id=\"name\" type=\"text\" maxlength=\"120\" autocomplete=\"off\" required>\n <p class=\"hint\" data-t=\"globalHint\"></p>\n </div>\n\n <fieldset>\n <legend data-t=\"ports\"></legend>\n <label class=\"choice\" id=\"ports-dedicated-row\"><input type=\"radio\" name=\"ports\" value=\"dedicated\">\n <div><strong data-t=\"portsDedicated\"></strong><span id=\"ports-dedicated\"></span></div></label>\n <label class=\"choice\"><input type=\"radio\" name=\"ports\" value=\"default\">\n <div><strong data-t=\"portsDefault\"></strong><span id=\"ports-default\"></span></div></label>\n <label class=\"choice\"><input type=\"radio\" name=\"ports\" value=\"custom\">\n <div><strong data-t=\"portsCustom\"></strong><span data-t=\"portsCustomHint\"></span></div></label>\n <div class=\"ports\" id=\"custom\" hidden>\n <label><span data-t=\"portRuneya\"></span><input id=\"port\" type=\"number\" min=\"1\" max=\"65535\"></label>\n <label><span data-t=\"portAgent\"></span><input id=\"agent-port\" type=\"number\" min=\"1\" max=\"65535\"></label>\n </div>\n </fieldset>\n\n <p class=\"error\" id=\"error\" role=\"alert\" hidden></p>\n\n <div class=\"actions\">\n <button type=\"button\" class=\"secondary\" id=\"cancel\" data-t=\"cancel\"></button>\n <button type=\"submit\" class=\"primary\" id=\"submit\" data-t=\"install\"></button>\n </div>\n </form>\n </div>\n\n <section class=\"card done\" id=\"done\" hidden aria-live=\"polite\">\n <div class=\"check\" aria-hidden=\"true\">✓</div>\n <h2 id=\"done-title\"></h2>\n <dl>\n <div><dt data-t=\"where\"></dt><dd id=\"where\"></dd></div>\n <div><dt data-t=\"files\"></dt><dd id=\"files\"></dd></div>\n <div id=\"warnings-row\" hidden><dt data-t=\"warnings\"></dt><dd id=\"warnings\"></dd></div>\n <div><dt data-t=\"remove\"></dt><dd><code id=\"uninstall\"></code></dd></div>\n </dl>\n <p class=\"close\" data-t=\"closeTab\"></p>\n </section>\n\n <section class=\"card done\" id=\"cancelled\" hidden>\n <h2 data-t=\"cancelledTitle\"></h2>\n <p class=\"close\" data-t=\"closeTab\"></p>\n </section>\n</main>\n<script>\nconst T = {\n fr: {\n title: 'Installer Runeya',\n lead: 'Un lanceur dans le menu de votre bureau, pour démarrer Runeya sans terminal.',\n globalHint: 'Le raccourci démarre Runeya dans votre dossier personnel et range les données dans ~/.runeya. Pour un raccourci vers un projet, passez par les réglages de Runeya.',\n name: 'Nom du raccourci', ports: 'Ports',\n portsDedicated: 'Ports dédiés (recommandé)', portsDefault: 'Ports par défaut de Runeya',\n portsCustom: 'Choisir moi-même', portsCustomHint: 'Saisir le port de Runeya, puis celui de l’agent',\n portRuneya: 'Runeya', portAgent: 'Agent',\n dedicatedHint: (p) => p + ' et ' + (p + 1) + ', choisis parmi les ports libres',\n defaultFree: (p) => p + ' et ' + (p + 1) + ', libres en ce moment',\n defaultTaken: (p) => p + ' ou ' + (p + 1) + ' est occupé : le raccourci ne démarrera pas tant qu’il le reste',\n cancel: 'Fermer', install: 'Créer le raccourci', installing: 'Création…',\n existingTitle: 'Raccourcis existants', global: 'Global (dossier personnel)',\n portsOf: (p, a) => 'Ports ' + p + ' · ' + a, defaultPorts: 'Ports par défaut',\n outdated: 'Vise un autre Node ou une autre installation de Runeya',\n recreate: 'Recréer', removeAction: 'Supprimer', confirmRemove: 'Confirmer',\n working: '…',\n created: (n) => 'Raccourci « ' + n + ' » créé',\n where: 'Où le trouver', files: 'Fichiers écrits', warnings: 'Avertissements', remove: 'Pour le supprimer',\n closeTab: 'Vous pouvez fermer cet onglet.',\n cancelledTitle: 'Installateur fermé',\n nameRequired: 'Donnez un nom au raccourci.',\n portsInvalid: 'Deux ports différents entre 1 et 65535.',\n unreachable: 'L’installateur ne répond plus. Relancez « runeya install-desktop ».',\n },\n en: {\n title: 'Install Runeya',\n lead: 'A launcher in your desktop menu, to start Runeya without a terminal.',\n globalHint: 'The shortcut starts Runeya in your home folder and stores data in ~/.runeya. For a shortcut to a project, use the Runeya settings.',\n name: 'Shortcut name', ports: 'Ports',\n portsDedicated: 'Dedicated ports (recommended)', portsDefault: 'Runeya default ports',\n portsCustom: 'Choose myself', portsCustomHint: 'Enter the Runeya port, then the agent’s',\n portRuneya: 'Runeya', portAgent: 'Agent',\n dedicatedHint: (p) => p + ' and ' + (p + 1) + ', chosen among free ports',\n defaultFree: (p) => p + ' and ' + (p + 1) + ', currently free',\n defaultTaken: (p) => p + ' or ' + (p + 1) + ' is in use: the shortcut will not start until it is free',\n cancel: 'Close', install: 'Create shortcut', installing: 'Creating…',\n existingTitle: 'Existing shortcuts', global: 'Global (home folder)',\n portsOf: (p, a) => 'Ports ' + p + ' · ' + a, defaultPorts: 'Default ports',\n outdated: 'Points to another Node or another Runeya install',\n recreate: 'Recreate', removeAction: 'Remove', confirmRemove: 'Confirm',\n working: '…',\n created: (n) => 'Shortcut “' + n + '” created',\n where: 'Where to find it', files: 'Files written', warnings: 'Warnings', remove: 'To remove it',\n closeTab: 'You can close this tab.',\n cancelledTitle: 'Installer closed',\n nameRequired: 'Give the shortcut a name.',\n portsInvalid: 'Two different ports between 1 and 65535.',\n unreachable: 'The installer no longer answers. Run “runeya install-desktop” again.',\n },\n};\nconst lang = (navigator.language || 'fr').toLowerCase().startsWith('fr') ? 'fr' : 'en';\nconst t = T[lang];\ndocument.documentElement.lang = lang;\ndocument.title = t.title;\ndocument.querySelectorAll('[data-t]').forEach((el) => { el.textContent = t[el.dataset.t]; });\n\nconst $ = (id) => document.getElementById(id);\nconst form = $('form');\nlet state = null;\n\nasync function post(path, body) {\n const res = await fetch(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) throw new Error(data.error || res.statusText);\n return data;\n}\n\nfunction showError(message) {\n $('error').textContent = message;\n $('error').hidden = !message;\n}\n\nasync function load() {\n state = await (await fetch('/api/state')).json();\n if (state.suggestedPort === null) {\n $('ports-dedicated-row').hidden = true;\n form.elements.ports.value = 'default';\n } else {\n $('ports-dedicated').textContent = t.dedicatedHint(state.suggestedPort);\n form.elements.ports.value = 'dedicated';\n }\n $('ports-default').textContent = state.defaultsFree ? t.defaultFree(state.defaultPort) : t.defaultTaken(state.defaultPort);\n if (!state.defaultsFree) $('ports-default').classList.add('warn');\n $('port').value = state.suggestedPort ?? state.defaultPort;\n $('agent-port').value = (state.suggestedPort ?? state.defaultPort) + 1;\n $('name').value = state.name;\n $('name').focus();\n}\n\nform.addEventListener('change', (e) => {\n if (e.target.name === 'ports') $('custom').hidden = portsChoice() !== 'custom';\n});\n\nform.addEventListener('submit', async (e) => {\n e.preventDefault();\n showError('');\n const name = $('name').value.trim();\n if (!name) { showError(t.nameRequired); $('name').focus(); return; }\n\n let port = null, agentPort = null;\n if (portsChoice() === 'dedicated') { port = state.suggestedPort; agentPort = port + 1; }\n if (portsChoice() === 'custom') {\n port = Number($('port').value); agentPort = Number($('agent-port').value);\n const ok = [port, agentPort].every((p) => Number.isInteger(p) && p >= 1 && p <= 65535) && port !== agentPort;\n if (!ok) { showError(t.portsInvalid); return; }\n }\n\n const submit = $('submit');\n submit.disabled = true; $('cancel').disabled = true;\n submit.textContent = t.installing;\n try {\n const r = await post('/api/install', { name, workingDir: null, port, agentPort });\n $('done-title').textContent = t.created(r.name);\n $('where').textContent = r.whereToFind;\n $('files').replaceChildren();\n for (const p of r.paths) { const d = document.createElement('div'); d.textContent = p; $('files').append(d); }\n if (r.warnings.length) {\n $('warnings-row').hidden = false;\n $('warnings').textContent = r.warnings.join(' · ');\n }\n $('uninstall').textContent = r.uninstall;\n $('split').hidden = true; $('done').hidden = false;\n } catch (err) {\n showError(err instanceof TypeError ? t.unreachable : err.message);\n submit.disabled = false; $('cancel').disabled = false;\n submit.textContent = t.install;\n }\n});\n\n$('cancel').addEventListener('click', async () => {\n try { await post('/api/cancel', {}); } catch { /* déjà fermé */ }\n $('split').hidden = true; $('cancelled').hidden = false;\n});\n\nfunction el(tag, className, text) {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction listError(message) {\n $('list-error').textContent = message;\n $('list-error').hidden = !message;\n}\n\n/** Une action sur un raccourci existant ; la liste est relue ensuite. */\nasync function act(path, launcher, button) {\n listError('');\n const label = button.textContent;\n button.disabled = true; button.textContent = t.working;\n try {\n await post(path, { id: launcher.id });\n await loadLaunchers();\n } catch (err) {\n listError(err instanceof TypeError ? t.unreachable : err.message);\n button.disabled = false; button.textContent = label;\n }\n}\n\nfunction renderLauncher(l) {\n const row = el('div', 'launcher');\n const body = el('div', 'launcher__body');\n body.append(el('strong', '', l.name));\n body.append(el('span', '', l.workingDir ?? t.global));\n body.append(el('span', '', l.port === null ? t.defaultPorts : t.portsOf(l.port, l.agentPort)));\n for (const p of l.problems) body.append(el('span', 'problem', p));\n if (l.outdated && !l.problems.length) body.append(el('span', 'stale', t.outdated));\n row.append(body);\n\n const actions = el('div', 'launcher__actions');\n // Recréer n'a de sens que si le raccourci ne vise plus ce Runeya-ci.\n if (l.outdated || l.problems.length) {\n const recreate = el('button', '', t.recreate);\n recreate.type = 'button';\n recreate.addEventListener('click', () => act('/api/reinstall', l, recreate));\n actions.append(recreate);\n }\n // Deux temps, sans boîte de dialogue : le premier clic arme, le second supprime.\n const remove = el('button', 'danger', t.removeAction);\n remove.type = 'button';\n let armed = false;\n remove.addEventListener('click', () => {\n if (!armed) {\n armed = true; remove.textContent = t.confirmRemove; remove.className = 'confirm';\n setTimeout(() => { if (armed && !remove.disabled) { armed = false; remove.textContent = t.removeAction; remove.className = 'danger'; } }, 3000);\n return;\n }\n act('/api/uninstall', l, remove);\n });\n actions.append(remove);\n row.append(actions);\n return row;\n}\n\nasync function loadLaunchers() {\n const list = await (await fetch('/api/launchers')).json();\n $('launchers').replaceChildren(...list.map(renderLauncher));\n $('existing').hidden = list.length === 0;\n $('split').classList.toggle('has-existing', list.length > 0);\n}\n\nload().catch(() => showError(t.unreachable));\nloadLaunchers().catch(() => listError(t.unreachable));\n</script>\n</body>\n</html>`;\n}\n\n/** Le logo, tel que dans `assets/icon.svg`. */\nconst MARK = '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 28 28\" aria-hidden=\"true\"><rect width=\"28\" height=\"28\" rx=\"8\" fill=\"#6366f1\"/><path d=\"M7 20 L14 8 L21 20\" stroke=\"white\" stroke-width=\"2.5\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M10 16 L18 16\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\"/></svg>';\n"],"mappings":"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","names":[]}
@@ -0,0 +1,43 @@
1
+ import {
2
+ desktopExecQuote,
3
+ installDesktopLauncher,
4
+ launcherArgs,
5
+ launcherProblems,
6
+ launcherWorkingDir,
7
+ listDesktopLaunchers,
8
+ parseDesktopEntry,
9
+ parseLauncherCommand,
10
+ parseMacApp,
11
+ parseVbs,
12
+ psQuote,
13
+ splitDesktopExec,
14
+ uninstallDesktopLauncher,
15
+ vbsQuote,
16
+ windowsVbsScript
17
+ } from "./chunk-GWQV5KKU.js";
18
+ import {
19
+ deriveLauncherIdentity,
20
+ launcherIdFromName,
21
+ slugifyLauncherName
22
+ } from "./chunk-LE6XQ3OV.js";
23
+ export {
24
+ deriveLauncherIdentity as deriveIdentity,
25
+ desktopExecQuote,
26
+ launcherIdFromName as idFromName,
27
+ installDesktopLauncher,
28
+ launcherArgs,
29
+ launcherProblems,
30
+ launcherWorkingDir,
31
+ listDesktopLaunchers,
32
+ parseDesktopEntry,
33
+ parseLauncherCommand,
34
+ parseMacApp,
35
+ parseVbs,
36
+ psQuote,
37
+ slugifyLauncherName as slugify,
38
+ splitDesktopExec,
39
+ uninstallDesktopLauncher,
40
+ vbsQuote,
41
+ windowsVbsScript
42
+ };
43
+ //# sourceMappingURL=desktop-launcher-HKXP4LRI.js.map
@@ -0,0 +1,15 @@
1
+ import {
2
+ printInstallReport,
3
+ printUninstallReport,
4
+ shellArg,
5
+ shouldOpenInstallPage,
6
+ whereToFind
7
+ } from "./chunk-YSYLL57J.js";
8
+ export {
9
+ printInstallReport,
10
+ printUninstallReport,
11
+ shellArg,
12
+ shouldOpenInstallPage,
13
+ whereToFind
14
+ };
15
+ //# sourceMappingURL=desktop-wizard-XIDDAG7L.js.map
package/index.js CHANGED
@@ -84,8 +84,8 @@ for (const { flag, env: key } of ENV_FLAGS) {
84
84
  if (args.includes("--debug")) process.env["RUNEYA_DEBUG"] = "1";
85
85
  var subCommand = args[0];
86
86
  if (subCommand === "install-desktop" || subCommand === "uninstall-desktop") {
87
- const { installDesktopLauncher, uninstallDesktopLauncher, deriveIdentity } = await import("./desktop-launcher-3COY6F6H.js");
88
- const { shouldRunWizard, runInstallWizard, printInstallReport, printUninstallReport } = await import("./desktop-wizard-4CYANX2O.js");
87
+ const { installDesktopLauncher, uninstallDesktopLauncher, listDesktopLaunchers, deriveIdentity } = await import("./desktop-launcher-HKXP4LRI.js");
88
+ const { shouldOpenInstallPage, printInstallReport, printUninstallReport } = await import("./desktop-wizard-XIDDAG7L.js");
89
89
  const { parsePort } = await import("./free-port-565NMFIZ.js");
90
90
  const rest = args.slice(1);
91
91
  const subFlagsWithValues = /* @__PURE__ */ new Set(["--name", "--port", "--agent-port"]);
@@ -99,25 +99,34 @@ if (subCommand === "install-desktop" || subCommand === "uninstall-desktop") {
99
99
  process.exit(1);
100
100
  }
101
101
  }
102
- let port = parsePort(portFlagRaw);
103
- let agentPort = parsePort(agentPortFlagRaw);
104
- let workingDir = rest.includes("--no-cwd") ? null : resolve(positional ?? process.cwd());
105
- let displayName = nameFlag;
106
- if (subCommand === "install-desktop" && shouldRunWizard(rest, positional !== void 0)) {
107
- const answers = await runInstallWizard(process.cwd());
108
- if (!answers) process.exit(130);
109
- workingDir = answers.workingDir;
110
- displayName = answers.name;
111
- port = answers.port;
112
- agentPort = answers.agentPort;
102
+ const port = parsePort(portFlagRaw);
103
+ const agentPort = parsePort(agentPortFlagRaw);
104
+ const workingDir = rest.includes("--no-cwd") ? null : resolve(positional ?? process.cwd());
105
+ const displayName = nameFlag;
106
+ const launcherBase = { nodePath: process.execPath, cliEntry: fileURLToPath(import.meta.url), distDir: __dirname };
107
+ if (subCommand === "install-desktop" && shouldOpenInstallPage(rest, positional !== void 0)) {
108
+ const { runInstallerPage } = await import("./desktop-installer-page-OJTOKP7V.js");
109
+ const outcome = await runInstallerPage({
110
+ openUrl: (url) => open(url),
111
+ install: (answers2) => installDesktopLauncher({
112
+ ...launcherBase,
113
+ ...answers2,
114
+ ...deriveIdentity(answers2.name, answers2.workingDir)
115
+ }),
116
+ list: listDesktopLaunchers,
117
+ uninstall: (launcher) => uninstallDesktopLauncher(launcher.name, launcher.id),
118
+ current: { nodePath: launcherBase.nodePath, cliEntry: launcherBase.cliEntry }
119
+ });
120
+ if (!outcome) process.exit(130);
121
+ const { answers, result } = outcome;
122
+ printInstallReport({ ...answers, nodePath: process.execPath, result });
123
+ process.exit(0);
113
124
  }
114
125
  const { name, id } = deriveIdentity(displayName, workingDir);
115
126
  try {
116
127
  if (subCommand === "install-desktop") {
117
128
  const result = await installDesktopLauncher({
118
- nodePath: process.execPath,
119
- cliEntry: fileURLToPath(import.meta.url),
120
- distDir: __dirname,
129
+ ...launcherBase,
121
130
  workingDir,
122
131
  name,
123
132
  id,
@@ -135,7 +144,7 @@ if (subCommand === "install-desktop" || subCommand === "uninstall-desktop") {
135
144
  }
136
145
  }
137
146
  if (subCommand === "instances") {
138
- const { listInstances, killInstance, restartInstance } = await import("./src-7O3GCEIB.js");
147
+ const { listInstances, killInstance, restartInstance } = await import("./src-KWLVQD36.js");
139
148
  const { runInstancesCommand } = await import("./instances-command-KZ5UGXOR.js");
140
149
  process.exit(await runInstancesCommand(args.slice(1), {
141
150
  listInstances,
@@ -169,7 +178,7 @@ if (args.includes("--help") || args.includes("-h")) {
169
178
 
170
179
  Commands:
171
180
  install-desktop [path] Create a desktop/menu launcher for this machine
172
- Bare on a terminal, this opens a short wizard.
181
+ Bare on a terminal, this opens a dedicated installer page.
173
182
  uninstall-desktop Remove a launcher created by install-desktop
174
183
  --name <label> Launcher name (default: "Runeya \u2014 <folder>")
175
184
  --no-cwd Global launcher \u2014 do not pin a working directory
@@ -295,7 +304,7 @@ function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500)
295
304
  });
296
305
  }
297
306
  async function main() {
298
- const { ensureNodeBinOnPath, ensureUserBinsOnPath, stampConsole } = await import("./node-4UUJN2WG.js");
307
+ const { ensureNodeBinOnPath, ensureUserBinsOnPath, stampConsole } = await import("./node-RN4URNZ5.js");
299
308
  ensureNodeBinOnPath();
300
309
  await ensureUserBinsOnPath();
301
310
  if (!isPullEnv) {
@@ -307,7 +316,7 @@ async function main() {
307
316
  console.warn(`[cli] Journal sur disque indisponible : ${err.message}`);
308
317
  }
309
318
  }
310
- const { createLocalServer, pullEnv, PullEnvError, registerInstance, registerCliLocation } = await import("./src-7O3GCEIB.js");
319
+ const { createLocalServer, pullEnv, PullEnvError, registerInstance, registerCliLocation } = await import("./src-KWLVQD36.js");
311
320
  registerCliLocation({ execPath: process.execPath, cliEntry: fileURLToPath(import.meta.url) });
312
321
  if (isPullEnv) {
313
322
  if (!serviceArg) {
@@ -416,7 +425,7 @@ async function main() {
416
425
  networkInfo,
417
426
  setLanMode,
418
427
  resolveTrayIcon
419
- } = await import("./src-7O3GCEIB.js");
428
+ } = await import("./src-KWLVQD36.js");
420
429
  tray = await startInstanceTray({
421
430
  info: { cwd: process.cwd(), port, version, url: appUrl },
422
431
  assetsDir: join(__dirname, "assets"),
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/port-resolution.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { Server } from 'node:http';\nimport type { WebSocketServer } from 'ws';\nimport type { Request, Response } from 'express';\nimport { fileURLToPath, URL as NodeURL } from 'node:url';\nimport { dirname, join, resolve } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport express from 'express';\nimport dotenv from 'dotenv'\nimport open from 'open';\nimport { resolvePort } from './port-resolution.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst { version } = JSON.parse(\n readFileSync(new NodeURL('./package.json', import.meta.url), 'utf8'),\n) as { version: string };\n\nfunction printStartupTable(port: number, appUrl: string): void {\n const rows: [string, string][] = [\n ['Version', version],\n ['Port', String(port)],\n ['Url', appUrl],\n ];\n\n const col0 = Math.max(...rows.map(([k]) => k.length));\n const col1 = Math.max(...rows.map(([, v]) => v.length));\n\n const pad = (s: string, n: number) => s.padEnd(n);\n const hr = (l: string, m: string, r: string, f: string) =>\n l + f.repeat(col0 + 2) + m + f.repeat(col1 + 2) + r;\n\n console.log('');\n console.log(hr('┌', '┬', '┐', '─'));\n for (const [key, value] of rows) {\n console.log(`│ ${pad(key, col0)} │ ${pad(value, col1)} │`);\n }\n console.log(hr('└', '┴', '┘', '─'));\n console.log('');\n}\n\n// Parse CLI flags\nconst args = process.argv.slice(2);\n\n/**\n * Set by the desktop launcher. It means nobody is watching a terminal, so a\n * startup failure has to be reported through a window of its own.\n */\nconst isFromDesktop = args.includes('--from-desktop');\n\n// Capture must start before anything logs, hence this early import.\nconst startupFailure = isFromDesktop ? await import('./startup-failure.js') : null;\nstartupFailure?.captureOutput();\n\nfunction getFlagValue(flags: string[]): string | undefined {\n for (const flag of flags) {\n const idx = args.indexOf(flag);\n if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];\n }\n return undefined;\n}\n\n/**\n * Flags that mirror a RUNEYA_* environment variable. The flag always wins over\n * the variable, and both are equivalent otherwise.\n *\n * Secrets (RUNEYA_ENCRYPTION_KEY, RUNEYA_MASTER_PASSWORD) deliberately have no\n * flag: process arguments are world-readable through `ps` on most systems.\n */\nconst ENV_FLAGS: { flag: string; env: string }[] = [\n { flag: '--host', env: 'RUNEYA_HOST' },\n { flag: '--agent-port', env: 'RUNEYA_AGENT_PORT' },\n { flag: '--agent-host', env: 'RUNEYA_AGENT_HOST' },\n { flag: '--agent-binary-path', env: 'RUNEYA_AGENT_BINARY_PATH' },\n { flag: '--data-dir', env: 'RUNEYA_DATA_DIR' },\n { flag: '--mode', env: 'RUNEYA_MODE' },\n { flag: '--cloud-url', env: 'RUNEYA_CLOUD_URL' },\n { flag: '--cloud-app-url', env: 'RUNEYA_CLOUD_APP_URL' },\n { flag: '--cors-origin', env: 'RUNEYA_CORS_ORIGIN' },\n { flag: '--claude-pty', env: 'RUNEYA_CLAUDE_PTY' },\n];\n\n// Flags taking a value — needed so their value is not mistaken for the target directory\nconst flagsWithValues = new Set([\n '--port',\n '--name',\n '-e', '--environment',\n '-s', '--service',\n ...ENV_FLAGS.map((f) => f.flag),\n]);\n\n// Apply value flags to their env var (flag wins over an already-set variable).\n// Done before dotenv so a .env file can never override an explicit flag.\nfor (const { flag, env: key } of ENV_FLAGS) {\n const value = getFlagValue([flag]);\n if (value !== undefined) process.env[key] = value;\n}\n// --debug is a boolean shortcut for RUNEYA_DEBUG=1\nif (args.includes('--debug')) process.env['RUNEYA_DEBUG'] = '1';\n\n/**\n * `install-desktop` / `uninstall-desktop` — handled before anything else so the\n * sub-command name is never mistaken for a target directory, and so the current\n * working directory is still the user's when we capture it.\n *\n * Bare on a TTY it opens a wizard; any flag that pre-answers a question skips\n * straight to the action so scripts stay unattended.\n */\nconst subCommand = args[0];\nif (subCommand === 'install-desktop' || subCommand === 'uninstall-desktop') {\n const { installDesktopLauncher, uninstallDesktopLauncher, deriveIdentity } =\n await import('./desktop-launcher.js');\n const { shouldRunWizard, runInstallWizard, printInstallReport, printUninstallReport } =\n await import('./desktop-wizard.js');\n\n const { parsePort } = await import('./free-port.js');\n\n const rest = args.slice(1);\n // Every flag of this sub-command that takes a value: without this, the value\n // itself (a port number, a name) is mistaken for the target directory.\n const subFlagsWithValues = new Set(['--name', '--port', '--agent-port']);\n const positional = rest.find((a, i) => !a.startsWith('-') && !subFlagsWithValues.has(rest[i - 1] ?? ''));\n const nameFlag = getFlagValue(['--name']);\n\n // Ports baked into the shortcut. Null means \"follow Runeya's default\", so a\n // launcher created without them keeps tracking the default if it changes.\n const portFlagRaw = getFlagValue(['--port']);\n const agentPortFlagRaw = getFlagValue(['--agent-port']);\n for (const [flag, raw] of [['--port', portFlagRaw], ['--agent-port', agentPortFlagRaw]] as const) {\n if (raw !== undefined && parsePort(raw) === null) {\n console.error(`Erreur : ${flag} attend un entier entre 1 et 65535 (reçu ${JSON.stringify(raw)}).`);\n process.exit(1);\n }\n }\n let port = parsePort(portFlagRaw);\n let agentPort = parsePort(agentPortFlagRaw);\n\n // The data directory is resolved from the working directory, so a launcher\n // that does not pin one would start Runeya in the desktop's cwd (usually the\n // home directory) and create a stray .runeya there. Pin the cwd by default.\n let workingDir = rest.includes('--no-cwd') ? null : resolve(positional ?? process.cwd());\n let displayName = nameFlag;\n\n if (subCommand === 'install-desktop' && shouldRunWizard(rest, positional !== undefined)) {\n const answers = await runInstallWizard(process.cwd());\n if (!answers) process.exit(130); // aborted with Ctrl+C\n workingDir = answers.workingDir;\n displayName = answers.name;\n port = answers.port;\n agentPort = answers.agentPort;\n }\n\n const { name, id } = deriveIdentity(displayName, workingDir);\n\n try {\n if (subCommand === 'install-desktop') {\n const result = await installDesktopLauncher({\n nodePath: process.execPath,\n cliEntry: fileURLToPath(import.meta.url),\n distDir: __dirname,\n workingDir,\n name,\n id,\n port,\n agentPort,\n });\n printInstallReport({ name, workingDir, nodePath: process.execPath, port, agentPort, result });\n } else {\n printUninstallReport(name, await uninstallDesktopLauncher(name, id));\n }\n process.exit(0);\n } catch (err) {\n console.error(`Erreur : ${(err as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * `runeya instances` — voir et piloter les Runeya de cette machine depuis un\n * terminal. Traité ici, avec les autres sous-commandes, pour que le mot\n * « instances » ne soit jamais pris pour un répertoire cible.\n *\n * Le pendant de l'icône de barre système, qui ne montre qu'une instance — la\n * sienne — et n'existe pas partout. Celle-ci les montre toutes, partout.\n *\n * Aucun serveur n'est démarré : le registre machine est un dossier de fichiers,\n * et le lire ne demande rien d'autre que de le lire.\n */\nif (subCommand === 'instances') {\n // Importé ici et pas en tête de fichier : charger `@runeya/apps-server` a un\n // coût, et un `runeya` ordinaire n'a pas à le payer pour une sous-commande\n // qu'il n'utilise pas.\n const { listInstances, killInstance, restartInstance } = await import('@runeya/apps-server');\n const { runInstancesCommand } = await import('./instances-command.js');\n\n process.exit(await runInstancesCommand(args.slice(1), {\n listInstances,\n stopInstance: killInstance,\n restartInstance: async (pid: number) => {\n const { pid: newPid, portReleased } = await restartInstance(pid);\n return { pid: newPid, portReleased };\n },\n openUrl: (url: string) => open(url),\n log: (message: string) => console.log(message),\n warn: (message: string) => console.error(message),\n }));\n}\n\n// Resolve target directory (first positional arg, skipping flag values)\nconst targetDir = args.find((a, i) => !a.startsWith('-') && !flagsWithValues.has(args[i - 1] ?? ''));\nif (targetDir) {\n const resolved = resolve(targetDir);\n process.chdir(resolved);\n console.log(`[cli] Working directory: ${resolved}`);\n} else if (process.env['RUNEYA_DATA_DIR']) {\n const resolved = resolve(process.env['RUNEYA_DATA_DIR'], '..');\n process.chdir(resolved);\n console.log(`[cli] Working directory: ${resolved} (from RUNEYA_DATA_DIR)`);\n} else if (process.cwd() === '/') {\n /**\n * On n'écrit jamais à la racine.\n *\n * Un lanceur global — installé sans dossier épinglé — démarre avec le cwd que\n * lui donne le système, et le Finder comme les services systemd donnent `/`.\n * `DATA_DIR` y vaudrait `/.runeya` : sous macOS la racine est le volume\n * système signé, monté en lecture seule, et la première écriture locale\n * échouerait en EROFS — que même root ne peut pas lever sans\n * `/etc/synthetic.conf` et un redémarrage. Sous Linux elle demanderait les\n * droits du superutilisateur pour un dossier qui n'a rien à faire là.\n *\n * Le repli vaut aussi pour les lanceurs DÉJÀ installés, que corriger le stub\n * ne rattraperait pas.\n */\n const home = homedir();\n process.chdir(home);\n console.log(`[cli] Working directory: ${home} (lanceur sans dossier épinglé)`);\n}\n\ndotenv.config({ quiet: true });\n\nif (args.includes('--help') || args.includes('-h')) {\n console.log(`Usage: runeya [path-to-your-stack] [options]\n\nCommands:\n install-desktop [path] Create a desktop/menu launcher for this machine\n Bare on a terminal, this opens a short wizard.\n uninstall-desktop Remove a launcher created by install-desktop\n --name <label> Launcher name (default: \"Runeya — <folder>\")\n --no-cwd Global launcher — do not pin a working directory\n --port <n> Port baked into the launcher (default: Runeya's own)\n --agent-port <n> Agent port baked in (default: launcher port + 1)\n -y, --yes Skip the wizard and take the defaults\n instances [open|stop|restart <pid>]\n List the Runeya running on this machine, and act on\n one. --json for the raw records.\n --from-desktop Set by desktop launchers: report startup failures\n in a browser window instead of a silent exit\n\nOptions:\n --version Show version number\n --pe, --pull-env Print env vars for a service to stdout (requires -e and -s)\n -e, --environment <n> Environment name or id\n -s, --service <name> Service name or id\n --lan Expose server on the local network (shortcut for --host 0.0.0.0)\n --no-open Do not open browser on startup\n --no-tray Do not put this instance in the system tray\n --agent Run as agent only (no server, no UI) — for remote machines\n -h, --help Show this help message\n\nConfiguration:\n Each setting has a flag and an equivalent RUNEYA_ environment variable; the\n flag wins. Variables are prefixed so they cannot collide with those of the\n services you manage. A .env file in the working directory is loaded\n automatically, but never overrides a flag.\n\n Flag Environment variable Default\n --port <n> RUNEYA_HTTP_PORT 4000\n (binds the agent instead when --agent is used)\n --host <address> RUNEYA_HOST 127.0.0.1\n --agent-port <n> RUNEYA_AGENT_PORT server port + 1\n --agent-host <address> RUNEYA_AGENT_HOST 127.0.0.1\n --agent-binary-path <path> RUNEYA_AGENT_BINARY_PATH bundled with the CLI\n --data-dir <path> RUNEYA_DATA_DIR <cwd>/.runeya\n --mode <mode> RUNEYA_MODE local-simple\n 'local-simple' | 'cloud'\n --cloud-url <url> RUNEYA_CLOUD_URL —\n --cloud-app-url <url> RUNEYA_CLOUD_APP_URL —\n --cors-origin <origin> RUNEYA_CORS_ORIGIN —\n --lan RUNEYA_LAN_PROXY false\n --debug RUNEYA_DEBUG false\n --claude-pty <0|1> RUNEYA_CLAUDE_PTY 1\n PTY mode for the Claude Code runner\n\n Secrets have no flag — process arguments are readable by other users via 'ps':\n RUNEYA_ENCRYPTION_KEY 64-char hex key for CI/CD — unlocks secrets without a\n master password AND disables tRPC authentication.\n Never set this on a machine reachable by others.\n RUNEYA_MASTER_PASSWORD Master password used by --pull-env instead of prompting\n\n Standard variables Runeya also honours: NODE_ENV, CI (skips opening the browser).\n`);\n process.exit(0);\n}\n\nif (args.includes('--version')) {\n console.log(version);\n process.exit(0);\n}\n\n/**\n * L'icône de barre système, sauf `--no-tray`.\n *\n * Chaque Runeya pose la sienne au démarrage et la retire en s'arrêtant : une\n * icône par instance en cours. Là où la barre système n'existe pas (serveur\n * sans bureau, arm64, paquet optionnel absent), il n'y a rien à désactiver —\n * l'icône ne se pose pas, et Runeya démarre pareil.\n */\nconst noTray = args.includes('--no-tray');\n\nconst noOpen =\n args.includes('--no-open') ||\n !!process.env['VITEST'] ||\n !!process.env['CI'] ||\n process.env['NODE_ENV'] === 'test';\n\n// --pull-env flag — detect early so we can silence stdout before any log\nconst isPullEnv = args.includes('--pull-env') || args.includes('--pe');\n\n// In pull-env mode, redirect console.log/info to stderr so only KEY=VALUE goes to stdout\nif (isPullEnv) {\n const toStderr = (...a: unknown[]) => process.stderr.write(a.join(' ') + '\\n');\n console.log = toStderr;\n console.info = toStderr;\n}\n\nconst serviceArg = getFlagValue(['-s', '--service']);\nconst environmentArg = getFlagValue(['-e', '--environment']);\n\n// Configure environment BEFORE importing server (env is parsed at module load)\n// --agent mode: run as standalone agent only (no server, no UI)\nconst isAgentOnly = args.includes('--agent');\n\n// --port targets the server, except in agent mode where it binds the agent\nconst portFlag = getFlagValue(['--port']);\nif (portFlag) {\n process.env[isAgentOnly ? 'RUNEYA_AGENT_PORT' : 'RUNEYA_HTTP_PORT'] = portFlag;\n}\n\n// --lan binds to 0.0.0.0. As an explicit flag it outranks RUNEYA_HOST; coming\n// from RUNEYA_LAN_PROXY it only fills in a host nobody else set.\nconst hostFlag = getFlagValue(['--host']);\nconst isLan = args.includes('--lan');\nconst isLanProxy = isLan || process.env.RUNEYA_LAN_PROXY === 'true' || process.env.RUNEYA_LAN_PROXY === '1';\nif (isLanProxy) {\n process.env.RUNEYA_LAN_PROXY = 'true';\n if (!hostFlag && isLan) process.env.RUNEYA_HOST = '0.0.0.0';\n else process.env.RUNEYA_HOST ??= '0.0.0.0';\n}\n\n// In agent mode --host binds the agent rather than the server\nif (isAgentOnly) {\n if (hostFlag) {\n process.env.RUNEYA_AGENT_HOST = hostFlag;\n } else if (isLan) {\n process.env.RUNEYA_AGENT_HOST = '0.0.0.0';\n } else if (isLanProxy) {\n process.env.RUNEYA_AGENT_HOST ??= '0.0.0.0';\n }\n}\n\n// Agent binary shipped inside the CLI dist — overridable via --agent-binary-path\nprocess.env.RUNEYA_AGENT_BINARY_PATH ??= join(__dirname, 'agent/index.js');\n// Expose CLI version to server\nprocess.env.RUNEYA_VERSION = version;\n// Tell the server it was launched from a shortcut, so the UI can say so\nif (isFromDesktop) process.env.RUNEYA_FROM_DESKTOP = '1';\n// Data directory defaults to CWD/.runeya (server env.ts default)\n// so Runeya data lives alongside the target project.\n\n/**\n * Listen with retry for EADDRINUSE — handles the race condition when the CLI\n * restarts the server before the old process has fully released the port.\n *\n * The `wss` parameter is needed because the `ws` library re-emits HTTP server\n * errors on the WebSocketServer — without a handler the error becomes unhandled.\n */\nfunction listenWithRetry(\n server: Server,\n port: number,\n host: string,\n wss?: WebSocketServer,\n maxRetries = 20,\n delay = 500,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let attempts = 0;\n\n // Absorb EADDRINUSE errors re-emitted by the ws library on the WSS\n // during the retry window. Without this, Node.js crashes on unhandled error.\n const wssErrorHandler = () => { /* absorbed — handled via server 'error' */ };\n wss?.on('error', wssErrorHandler);\n\n const tryListen = () => {\n const onError = (err: NodeJS.ErrnoException) => {\n // Clean up the 'listening' callback that server.listen() registered\n server.removeAllListeners('listening');\n\n if (err.code === 'EADDRINUSE' && attempts < maxRetries) {\n attempts++;\n console.log(`[cli] Port ${port} in use, retrying in ${delay}ms (${attempts}/${maxRetries})...`);\n setTimeout(tryListen, delay);\n } else {\n wss?.removeListener('error', wssErrorHandler);\n reject(err);\n }\n };\n\n server.once('error', onError);\n server.listen(port, host, () => {\n server.removeListener('error', onError);\n wss?.removeListener('error', wssErrorHandler);\n resolve();\n });\n };\n\n tryListen();\n });\n}\n\nasync function main() {\n // Avant tout : ce que ce process hérite ici, le serveur, l'agent local et\n // chaque service lancé ensuite en héritent à leur tour. Un raccourci de bureau\n // reçoit le PATH de la session graphique, où ni les gestionnaires de version\n // ni les binaires installés par l'utilisateur (claude, codex) ne figurent.\n const { ensureNodeBinOnPath, ensureUserBinsOnPath, stampConsole } = await import('@runeya/packages-shared/node');\n ensureNodeBinOnPath();\n await ensureUserBinsOnPath();\n\n // Journaux datés du temps écoulé depuis le démarrage. Jamais en --pull-env :\n // sa sortie est lue par un script, pas par un humain.\n if (!isPullEnv) {\n stampConsole();\n // Sortie et crash recopiés sur disque : une instance relancée en\n // `stdio: 'ignore'` (bureau, mise à jour) n'a sinon aucune trace.\n try {\n const { installLogFile } = await import('./log-file.js');\n console.log(`[cli] Journal : ${installLogFile().logPath}`);\n } catch (err) {\n console.warn(`[cli] Journal sur disque indisponible : ${(err as Error).message}`);\n }\n }\n\n // Dynamic import ensures env is parsed AFTER we configure it\n const { createLocalServer, pullEnv, PullEnvError, registerInstance, registerCliLocation } = await import('@runeya/apps-server');\n\n // D'où ce CLI a été lancé, pour que l'interface puisse proposer `install-desktop`\n // sans que le serveur ait à deviner un exécutable.\n registerCliLocation({ execPath: process.execPath, cliEntry: fileURLToPath(import.meta.url) });\n\n // Handle --pull-env mode: resolve env vars and print, then exit\n if (isPullEnv) {\n if (!serviceArg) {\n console.error('Error: --service (-s) is required with --pull-env');\n process.exit(1);\n }\n if (!environmentArg) {\n console.error('Error: --environment (-e) is required with --pull-env');\n process.exit(1);\n }\n try {\n const output = await pullEnv({ service: serviceArg, environment: environmentArg });\n if (output) process.stdout.write(output + '\\n');\n process.exit(0);\n } catch (err: unknown) {\n if (err instanceof PullEnvError) {\n console.error(`Error: ${err.message}`);\n process.exit(err.exitCode);\n }\n throw err;\n }\n }\n\n // --agent mode: standalone agent, no server, no UI\n if (isAgentOnly) {\n const { spawn } = await import('node:child_process');\n const agentBin = join(__dirname, 'agent/index.js');\n const child = spawn(process.execPath, [agentBin], { stdio: 'inherit', windowsHide: true });\n child.on('exit', (code) => process.exit(code ?? 0));\n // SIGINT is already delivered to the child by the terminal (same process group).\n // Intercept it here only to suppress Node's default exit so we stay alive\n // until the child finishes its graceful shutdown.\n process.on('SIGINT', () => { /* wait for child to exit */ });\n process.on('SIGTERM', () => child.kill('SIGTERM'));\n return;\n }\n\n /**\n * Un relais de redémarrage (`appUpdate.restart`) est l'exception au garde-fou\n * ci-dessous : l'instance qui répond encore sur le port est précisément celle\n * qu'on remplace. On la laisse s'éteindre, `listenWithRetry` absorbe\n * l'EADDRINUSE du chevauchement. La marque est consommée ici pour ne pas se\n * propager aux services lancés ensuite, qui héritent de cet environnement.\n */\n const isRestartHandoff = process.env.RUNEYA_RESTART_HANDOFF === '1';\n delete process.env.RUNEYA_RESTART_HANDOFF;\n\n /**\n * A shortcut clicked twice must not fail.\n *\n * Starting a second instance on a port a Runeya already holds ends in\n * EADDRINUSE after ten seconds of retries — and from a launcher there is no\n * terminal to read that in. So the port is asked first: if Runeya answers,\n * the right move is to bring up the window that already exists.\n *\n * Only for launcher starts. From a terminal, `runeya` twice on one port is a\n * mistake worth reporting, not one worth papering over.\n */\n if (isFromDesktop && !isRestartHandoff) {\n const port = resolvePort();\n const { probeRuneya } = await import('./free-port.js');\n const existing = await probeRuneya(port);\n if (existing) {\n const url = `http://localhost:${port}`;\n console.log(`[cli] Runeya v${existing.version} déjà en cours sur le port ${port} — ouverture de ${url}`);\n if (!noOpen) await open(url);\n return;\n }\n }\n\n console.log('[cli] Starting Runeya...');\n\n // Create server (automatically spawns local agent)\n const { app, server, wss, shutdown } = await createLocalServer();\n\n // Serve static files from web frontend (embedded in CLI dist during build)\n const webDistPath = join(__dirname, 'web');\n\n /**\n * Les fichiers d'`assets/` portent un hash de leur contenu dans leur nom : une\n * mise à jour produit un NOUVEAU nom, jamais un contenu différent sous le même\n * nom. Ils peuvent donc être gardés indéfiniment, et `immutable` dit au\n * navigateur de ne même pas revalider.\n *\n * Sans cela, `express.static` répond `max-age=0` : chaque rechargement\n * redemande les 295 fichiers pour s'entendre répondre 304. Ce sont des\n * requêtes vides, mais elles occupent les six connexions qu'un navigateur\n * accorde par hôte — pour tous ses onglets à la fois. Avec plusieurs onglets\n * rechargés ensemble, l'ouverture du WebSocket attendait derrière cette file.\n */\n app.use('/assets', express.static(join(webDistPath, 'assets'), {\n immutable: true,\n maxAge: '1y',\n fallthrough: false,\n }));\n\n // Tout le reste (favicon…) : revalidation à chaque fois, ces noms ne changent\n // pas d'une version à l'autre.\n app.use(express.static(webDistPath, {\n index: false,\n setHeaders: (res) => res.setHeader('Cache-Control', 'no-cache'),\n }));\n\n // Fallback to index.html for client-side routing (Vue Router)\n // Express 5 requires explicit path patterns (not '*')\n //\n // JAMAIS mis en cache : c'est lui qui nomme les assets de la version courante.\n // Un index.html périmé rappellerait les fichiers de l'ancienne version — qui,\n // eux, ne sont plus là.\n //\n // `root` + nom relatif, jamais un chemin absolu : `sendFile` refuse tout\n // segment commençant par un point (`dotfiles: 'ignore'` par défaut) et\n // répond 404. Or l'installation la plus courante — `npx @runeya/runeya` —\n // vit sous `~/.npm/_npx/…`. Avec un chemin absolu, la page d'accueil était\n // donc introuvable sur toute machine lancée par npx (ou depuis `~/.nvm`),\n // alors que `/index.html` répondait : `express.static` ne teste, lui, que la\n // partie relative à sa racine.\n app.use((_req: Request, res: Response) => {\n res.setHeader('Cache-Control', 'no-cache');\n res.sendFile('index.html', { root: webDistPath });\n });\n\n const port = resolvePort();\n const host = process.env.RUNEYA_HOST ?? '127.0.0.1';\n\n await listenWithRetry(server, port, host, wss);\n\n // Announce this instance now that the port is really ours, so the settings\n // panel of any Runeya on this machine can list it.\n await registerInstance();\n\n /**\n * L'adresse d'écoute n'est pas une adresse de visite.\n *\n * `0.0.0.0` (et son équivalent IPv6 `::`) dit « toutes les interfaces » à\n * bind ; ce n'est pas une destination. Sous Linux une connexion vers elle\n * retombe sur la boucle locale, ce qui a longtemps masqué le problème — sous\n * Windows elle n'est pas routable du tout, et les navigateurs la bloquent\n * désormais ailleurs aussi. On écoute donc partout, mais on ouvre localhost.\n */\n const browseHost = host === '0.0.0.0' || host === '::' ? 'localhost' : host;\n const appUrl = `http://${browseHost}:${port}`;\n\n if (!noOpen) {\n await open(appUrl);\n }\n\n console.log('\\n🚀 Runeya is running!');\n if (isLanProxy) {\n console.log('🌐 LAN mode: server and service ports exposed on local network');\n }\n printStartupTable(port, appUrl);\n console.log('[cli] Press Ctrl+C to stop');\n\n // Assignée plus bas : l'arrêt doit pouvoir la retirer, et la poser demande\n // de savoir arrêter — les deux se référencent.\n let tray: { stop: () => Promise<void> } | null = null;\n\n let shuttingDown = false;\n const gracefulShutdown = async (signal: string) => {\n if (shuttingDown) {\n console.log(`[cli] Forced exit (second ${signal})`);\n process.exit(1);\n }\n shuttingDown = true;\n console.log(`\\n[cli] Received ${signal}, shutting down gracefully...`);\n\n // L'icône part en premier : elle annonce une instance vivante, et la\n // laisser pendant l'extinction inviterait à cliquer dans le vide.\n await tray?.stop();\n\n // In dev, exit immediately — ports are freed on process death,\n // and the 'exit' handler in agent-spawner SIGKILLs the agent.\n if (process.env.NODE_ENV === 'development') {\n process.exit(0);\n }\n\n await shutdown();\n console.log('[cli] Shutdown complete');\n process.exit(0);\n };\n\n process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));\n process.on('SIGINT', () => gracefulShutdown('SIGINT'));\n\n if (!noTray) {\n const { startInstanceTray } = await import('./tray.js');\n const {\n scheduleSelfRestart,\n listTrayServices,\n onTrayServicesChanged,\n startProcess,\n stopProcess,\n restartProcess,\n networkInfo,\n setLanMode,\n resolveTrayIcon,\n } = await import('@runeya/apps-server');\n\n tray = await startInstanceTray({\n info: { cwd: process.cwd(), port, version, url: appUrl },\n assetsDir: join(__dirname, 'assets'),\n openUrl: (target) => open(target),\n restart: () => scheduleSelfRestart(),\n stop: () => void gracefulShutdown('tray'),\n\n listServices: listTrayServices,\n startService: (id) => startProcess(id),\n // L'arrêt exige l'agent qui le fait tourner : il est dans la vue, et\n // seul un service en cours propose ce geste.\n stopService: async (id) => {\n const service = (await listTrayServices()).find((s) => s.id === id);\n if (!service?.agentId) throw new Error(`Aucun agent pour ${id}`);\n return stopProcess(id, service.agentId);\n },\n restartService: (id) => restartProcess(id),\n onServicesChanged: onTrayServicesChanged,\n // Le logo de l'organisation du projet ouvert, quand il y en a un : c'est ce\n // qui distingue deux Runeya dans la barre système.\n projectIcon: () => resolveTrayIcon(),\n\n lan: {\n read: () => {\n const info = networkInfo();\n // `lanUrls` est déjà ce qu'on voudrait lire au survol : des adresses\n // joignables, pas des IP brutes. Vide hors mode réseau local.\n return { enabled: info.lanEnabled, addresses: info.lanUrls };\n },\n toggle: (enabled) => setLanMode(enabled),\n },\n\n warn: (message) => console.log(message),\n });\n }\n}\n\nmain().catch(async (err) => {\n console.error('[cli] Fatal error:', err);\n\n // With a terminal, the message above is the report. Without one, it goes\n // nowhere: serve it as a page and open it in the browser instead.\n if (startupFailure) {\n try {\n const { diagnose, renderFailurePage, capturedLines, showStartupFailure } = startupFailure;\n const html = renderFailurePage(\n diagnose(err, resolvePort()),\n capturedLines(),\n {\n 'Version': version,\n 'Dossier de travail': process.cwd(),\n 'Node': process.execPath,\n 'Runeya': fileURLToPath(import.meta.url),\n },\n );\n await showStartupFailure(html);\n } catch (reportErr) {\n console.error('[cli] Impossible d\\'afficher le rapport d\\'erreur:', reportErr);\n }\n }\n\n process.exit(1);\n});\n","/**\n * Resolve the HTTP port Runeya should listen on.\n *\n * Only RUNEYA_HTTP_PORT is read (default 4000) — the generic PORT variable is\n * intentionally ignored so it can stay reserved for the user's own services.\n *\n * Throws if the resolved value is not an integer in [1, 65535].\n */\nexport function resolvePort(): number {\n const raw = process.env.RUNEYA_HTTP_PORT;\n if (raw === undefined) return 4000;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(\n `Invalid port: ${JSON.stringify(raw)}. Must be an integer between 1 and 65535.`,\n );\n }\n return port;\n}\n"],"mappings":"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","names":["resolve","port"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/port-resolution.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { Server } from 'node:http';\nimport type { WebSocketServer } from 'ws';\nimport type { Request, Response } from 'express';\nimport { fileURLToPath, URL as NodeURL } from 'node:url';\nimport { dirname, join, resolve } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport express from 'express';\nimport dotenv from 'dotenv'\nimport open from 'open';\nimport { resolvePort } from './port-resolution.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst { version } = JSON.parse(\n readFileSync(new NodeURL('./package.json', import.meta.url), 'utf8'),\n) as { version: string };\n\nfunction printStartupTable(port: number, appUrl: string): void {\n const rows: [string, string][] = [\n ['Version', version],\n ['Port', String(port)],\n ['Url', appUrl],\n ];\n\n const col0 = Math.max(...rows.map(([k]) => k.length));\n const col1 = Math.max(...rows.map(([, v]) => v.length));\n\n const pad = (s: string, n: number) => s.padEnd(n);\n const hr = (l: string, m: string, r: string, f: string) =>\n l + f.repeat(col0 + 2) + m + f.repeat(col1 + 2) + r;\n\n console.log('');\n console.log(hr('┌', '┬', '┐', '─'));\n for (const [key, value] of rows) {\n console.log(`│ ${pad(key, col0)} │ ${pad(value, col1)} │`);\n }\n console.log(hr('└', '┴', '┘', '─'));\n console.log('');\n}\n\n// Parse CLI flags\nconst args = process.argv.slice(2);\n\n/**\n * Set by the desktop launcher. It means nobody is watching a terminal, so a\n * startup failure has to be reported through a window of its own.\n */\nconst isFromDesktop = args.includes('--from-desktop');\n\n// Capture must start before anything logs, hence this early import.\nconst startupFailure = isFromDesktop ? await import('./startup-failure.js') : null;\nstartupFailure?.captureOutput();\n\nfunction getFlagValue(flags: string[]): string | undefined {\n for (const flag of flags) {\n const idx = args.indexOf(flag);\n if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];\n }\n return undefined;\n}\n\n/**\n * Flags that mirror a RUNEYA_* environment variable. The flag always wins over\n * the variable, and both are equivalent otherwise.\n *\n * Secrets (RUNEYA_ENCRYPTION_KEY, RUNEYA_MASTER_PASSWORD) deliberately have no\n * flag: process arguments are world-readable through `ps` on most systems.\n */\nconst ENV_FLAGS: { flag: string; env: string }[] = [\n { flag: '--host', env: 'RUNEYA_HOST' },\n { flag: '--agent-port', env: 'RUNEYA_AGENT_PORT' },\n { flag: '--agent-host', env: 'RUNEYA_AGENT_HOST' },\n { flag: '--agent-binary-path', env: 'RUNEYA_AGENT_BINARY_PATH' },\n { flag: '--data-dir', env: 'RUNEYA_DATA_DIR' },\n { flag: '--mode', env: 'RUNEYA_MODE' },\n { flag: '--cloud-url', env: 'RUNEYA_CLOUD_URL' },\n { flag: '--cloud-app-url', env: 'RUNEYA_CLOUD_APP_URL' },\n { flag: '--cors-origin', env: 'RUNEYA_CORS_ORIGIN' },\n { flag: '--claude-pty', env: 'RUNEYA_CLAUDE_PTY' },\n];\n\n// Flags taking a value — needed so their value is not mistaken for the target directory\nconst flagsWithValues = new Set([\n '--port',\n '--name',\n '-e', '--environment',\n '-s', '--service',\n ...ENV_FLAGS.map((f) => f.flag),\n]);\n\n// Apply value flags to their env var (flag wins over an already-set variable).\n// Done before dotenv so a .env file can never override an explicit flag.\nfor (const { flag, env: key } of ENV_FLAGS) {\n const value = getFlagValue([flag]);\n if (value !== undefined) process.env[key] = value;\n}\n// --debug is a boolean shortcut for RUNEYA_DEBUG=1\nif (args.includes('--debug')) process.env['RUNEYA_DEBUG'] = '1';\n\n/**\n * `install-desktop` / `uninstall-desktop` — handled before anything else so the\n * sub-command name is never mistaken for a target directory, and so the current\n * working directory is still the user's when we capture it.\n *\n * Bare on a TTY, `install-desktop` opens a dedicated installer page served by a\n * short-lived local server — Runeya itself is not started. Any flag that\n * pre-answers a question installs directly so scripts stay unattended.\n */\nconst subCommand = args[0];\nif (subCommand === 'install-desktop' || subCommand === 'uninstall-desktop') {\n const { installDesktopLauncher, uninstallDesktopLauncher, listDesktopLaunchers, deriveIdentity } =\n await import('./desktop-launcher.js');\n const { shouldOpenInstallPage, printInstallReport, printUninstallReport } =\n await import('./desktop-wizard.js');\n\n const { parsePort } = await import('./free-port.js');\n\n const rest = args.slice(1);\n // Every flag of this sub-command that takes a value: without this, the value\n // itself (a port number, a name) is mistaken for the target directory.\n const subFlagsWithValues = new Set(['--name', '--port', '--agent-port']);\n const positional = rest.find((a, i) => !a.startsWith('-') && !subFlagsWithValues.has(rest[i - 1] ?? ''));\n const nameFlag = getFlagValue(['--name']);\n\n // Ports baked into the shortcut. Null means \"follow Runeya's default\", so a\n // launcher created without them keeps tracking the default if it changes.\n const portFlagRaw = getFlagValue(['--port']);\n const agentPortFlagRaw = getFlagValue(['--agent-port']);\n for (const [flag, raw] of [['--port', portFlagRaw], ['--agent-port', agentPortFlagRaw]] as const) {\n if (raw !== undefined && parsePort(raw) === null) {\n console.error(`Erreur : ${flag} attend un entier entre 1 et 65535 (reçu ${JSON.stringify(raw)}).`);\n process.exit(1);\n }\n }\n const port = parsePort(portFlagRaw);\n const agentPort = parsePort(agentPortFlagRaw);\n\n // The data directory is resolved from the working directory, so a launcher\n // that does not pin one would start Runeya in the desktop's cwd (usually the\n // home directory) and create a stray .runeya there. Pin the cwd by default.\n const workingDir = rest.includes('--no-cwd') ? null : resolve(positional ?? process.cwd());\n const displayName = nameFlag;\n\n const launcherBase = { nodePath: process.execPath, cliEntry: fileURLToPath(import.meta.url), distDir: __dirname };\n\n if (subCommand === 'install-desktop' && shouldOpenInstallPage(rest, positional !== undefined)) {\n const { runInstallerPage } = await import('./desktop-installer-page.js');\n const outcome = await runInstallerPage({\n openUrl: (url) => open(url),\n install: (answers) => installDesktopLauncher({\n ...launcherBase,\n ...answers,\n ...deriveIdentity(answers.name, answers.workingDir),\n }),\n list: listDesktopLaunchers,\n uninstall: (launcher) => uninstallDesktopLauncher(launcher.name, launcher.id),\n current: { nodePath: launcherBase.nodePath, cliEntry: launcherBase.cliEntry },\n });\n if (!outcome) process.exit(130); // annulé depuis la page ou par Ctrl+C\n const { answers, result } = outcome;\n printInstallReport({ ...answers, nodePath: process.execPath, result });\n process.exit(0);\n }\n\n const { name, id } = deriveIdentity(displayName, workingDir);\n\n try {\n if (subCommand === 'install-desktop') {\n const result = await installDesktopLauncher({\n ...launcherBase,\n workingDir,\n name,\n id,\n port,\n agentPort,\n });\n printInstallReport({ name, workingDir, nodePath: process.execPath, port, agentPort, result });\n } else {\n printUninstallReport(name, await uninstallDesktopLauncher(name, id));\n }\n process.exit(0);\n } catch (err) {\n console.error(`Erreur : ${(err as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * `runeya instances` — voir et piloter les Runeya de cette machine depuis un\n * terminal. Traité ici, avec les autres sous-commandes, pour que le mot\n * « instances » ne soit jamais pris pour un répertoire cible.\n *\n * Le pendant de l'icône de barre système, qui ne montre qu'une instance — la\n * sienne — et n'existe pas partout. Celle-ci les montre toutes, partout.\n *\n * Aucun serveur n'est démarré : le registre machine est un dossier de fichiers,\n * et le lire ne demande rien d'autre que de le lire.\n */\nif (subCommand === 'instances') {\n // Importé ici et pas en tête de fichier : charger `@runeya/apps-server` a un\n // coût, et un `runeya` ordinaire n'a pas à le payer pour une sous-commande\n // qu'il n'utilise pas.\n const { listInstances, killInstance, restartInstance } = await import('@runeya/apps-server');\n const { runInstancesCommand } = await import('./instances-command.js');\n\n process.exit(await runInstancesCommand(args.slice(1), {\n listInstances,\n stopInstance: killInstance,\n restartInstance: async (pid: number) => {\n const { pid: newPid, portReleased } = await restartInstance(pid);\n return { pid: newPid, portReleased };\n },\n openUrl: (url: string) => open(url),\n log: (message: string) => console.log(message),\n warn: (message: string) => console.error(message),\n }));\n}\n\n// Resolve target directory (first positional arg, skipping flag values)\nconst targetDir = args.find((a, i) => !a.startsWith('-') && !flagsWithValues.has(args[i - 1] ?? ''));\nif (targetDir) {\n const resolved = resolve(targetDir);\n process.chdir(resolved);\n console.log(`[cli] Working directory: ${resolved}`);\n} else if (process.env['RUNEYA_DATA_DIR']) {\n const resolved = resolve(process.env['RUNEYA_DATA_DIR'], '..');\n process.chdir(resolved);\n console.log(`[cli] Working directory: ${resolved} (from RUNEYA_DATA_DIR)`);\n} else if (process.cwd() === '/') {\n /**\n * On n'écrit jamais à la racine.\n *\n * Un lanceur global — installé sans dossier épinglé — démarre avec le cwd que\n * lui donne le système, et le Finder comme les services systemd donnent `/`.\n * `DATA_DIR` y vaudrait `/.runeya` : sous macOS la racine est le volume\n * système signé, monté en lecture seule, et la première écriture locale\n * échouerait en EROFS — que même root ne peut pas lever sans\n * `/etc/synthetic.conf` et un redémarrage. Sous Linux elle demanderait les\n * droits du superutilisateur pour un dossier qui n'a rien à faire là.\n *\n * Le repli vaut aussi pour les lanceurs DÉJÀ installés, que corriger le stub\n * ne rattraperait pas.\n */\n const home = homedir();\n process.chdir(home);\n console.log(`[cli] Working directory: ${home} (lanceur sans dossier épinglé)`);\n}\n\ndotenv.config({ quiet: true });\n\nif (args.includes('--help') || args.includes('-h')) {\n console.log(`Usage: runeya [path-to-your-stack] [options]\n\nCommands:\n install-desktop [path] Create a desktop/menu launcher for this machine\n Bare on a terminal, this opens a dedicated installer page.\n uninstall-desktop Remove a launcher created by install-desktop\n --name <label> Launcher name (default: \"Runeya — <folder>\")\n --no-cwd Global launcher — do not pin a working directory\n --port <n> Port baked into the launcher (default: Runeya's own)\n --agent-port <n> Agent port baked in (default: launcher port + 1)\n -y, --yes Skip the wizard and take the defaults\n instances [open|stop|restart <pid>]\n List the Runeya running on this machine, and act on\n one. --json for the raw records.\n --from-desktop Set by desktop launchers: report startup failures\n in a browser window instead of a silent exit\n\nOptions:\n --version Show version number\n --pe, --pull-env Print env vars for a service to stdout (requires -e and -s)\n -e, --environment <n> Environment name or id\n -s, --service <name> Service name or id\n --lan Expose server on the local network (shortcut for --host 0.0.0.0)\n --no-open Do not open browser on startup\n --no-tray Do not put this instance in the system tray\n --agent Run as agent only (no server, no UI) — for remote machines\n -h, --help Show this help message\n\nConfiguration:\n Each setting has a flag and an equivalent RUNEYA_ environment variable; the\n flag wins. Variables are prefixed so they cannot collide with those of the\n services you manage. A .env file in the working directory is loaded\n automatically, but never overrides a flag.\n\n Flag Environment variable Default\n --port <n> RUNEYA_HTTP_PORT 4000\n (binds the agent instead when --agent is used)\n --host <address> RUNEYA_HOST 127.0.0.1\n --agent-port <n> RUNEYA_AGENT_PORT server port + 1\n --agent-host <address> RUNEYA_AGENT_HOST 127.0.0.1\n --agent-binary-path <path> RUNEYA_AGENT_BINARY_PATH bundled with the CLI\n --data-dir <path> RUNEYA_DATA_DIR <cwd>/.runeya\n --mode <mode> RUNEYA_MODE local-simple\n 'local-simple' | 'cloud'\n --cloud-url <url> RUNEYA_CLOUD_URL —\n --cloud-app-url <url> RUNEYA_CLOUD_APP_URL —\n --cors-origin <origin> RUNEYA_CORS_ORIGIN —\n --lan RUNEYA_LAN_PROXY false\n --debug RUNEYA_DEBUG false\n --claude-pty <0|1> RUNEYA_CLAUDE_PTY 1\n PTY mode for the Claude Code runner\n\n Secrets have no flag — process arguments are readable by other users via 'ps':\n RUNEYA_ENCRYPTION_KEY 64-char hex key for CI/CD — unlocks secrets without a\n master password AND disables tRPC authentication.\n Never set this on a machine reachable by others.\n RUNEYA_MASTER_PASSWORD Master password used by --pull-env instead of prompting\n\n Standard variables Runeya also honours: NODE_ENV, CI (skips opening the browser).\n`);\n process.exit(0);\n}\n\nif (args.includes('--version')) {\n console.log(version);\n process.exit(0);\n}\n\n/**\n * L'icône de barre système, sauf `--no-tray`.\n *\n * Chaque Runeya pose la sienne au démarrage et la retire en s'arrêtant : une\n * icône par instance en cours. Là où la barre système n'existe pas (serveur\n * sans bureau, arm64, paquet optionnel absent), il n'y a rien à désactiver —\n * l'icône ne se pose pas, et Runeya démarre pareil.\n */\nconst noTray = args.includes('--no-tray');\n\nconst noOpen =\n args.includes('--no-open') ||\n !!process.env['VITEST'] ||\n !!process.env['CI'] ||\n process.env['NODE_ENV'] === 'test';\n\n// --pull-env flag — detect early so we can silence stdout before any log\nconst isPullEnv = args.includes('--pull-env') || args.includes('--pe');\n\n// In pull-env mode, redirect console.log/info to stderr so only KEY=VALUE goes to stdout\nif (isPullEnv) {\n const toStderr = (...a: unknown[]) => process.stderr.write(a.join(' ') + '\\n');\n console.log = toStderr;\n console.info = toStderr;\n}\n\nconst serviceArg = getFlagValue(['-s', '--service']);\nconst environmentArg = getFlagValue(['-e', '--environment']);\n\n// Configure environment BEFORE importing server (env is parsed at module load)\n// --agent mode: run as standalone agent only (no server, no UI)\nconst isAgentOnly = args.includes('--agent');\n\n// --port targets the server, except in agent mode where it binds the agent\nconst portFlag = getFlagValue(['--port']);\nif (portFlag) {\n process.env[isAgentOnly ? 'RUNEYA_AGENT_PORT' : 'RUNEYA_HTTP_PORT'] = portFlag;\n}\n\n// --lan binds to 0.0.0.0. As an explicit flag it outranks RUNEYA_HOST; coming\n// from RUNEYA_LAN_PROXY it only fills in a host nobody else set.\nconst hostFlag = getFlagValue(['--host']);\nconst isLan = args.includes('--lan');\nconst isLanProxy = isLan || process.env.RUNEYA_LAN_PROXY === 'true' || process.env.RUNEYA_LAN_PROXY === '1';\nif (isLanProxy) {\n process.env.RUNEYA_LAN_PROXY = 'true';\n if (!hostFlag && isLan) process.env.RUNEYA_HOST = '0.0.0.0';\n else process.env.RUNEYA_HOST ??= '0.0.0.0';\n}\n\n// In agent mode --host binds the agent rather than the server\nif (isAgentOnly) {\n if (hostFlag) {\n process.env.RUNEYA_AGENT_HOST = hostFlag;\n } else if (isLan) {\n process.env.RUNEYA_AGENT_HOST = '0.0.0.0';\n } else if (isLanProxy) {\n process.env.RUNEYA_AGENT_HOST ??= '0.0.0.0';\n }\n}\n\n// Agent binary shipped inside the CLI dist — overridable via --agent-binary-path\nprocess.env.RUNEYA_AGENT_BINARY_PATH ??= join(__dirname, 'agent/index.js');\n// Expose CLI version to server\nprocess.env.RUNEYA_VERSION = version;\n// Tell the server it was launched from a shortcut, so the UI can say so\nif (isFromDesktop) process.env.RUNEYA_FROM_DESKTOP = '1';\n// Data directory defaults to CWD/.runeya (server env.ts default)\n// so Runeya data lives alongside the target project.\n\n/**\n * Listen with retry for EADDRINUSE — handles the race condition when the CLI\n * restarts the server before the old process has fully released the port.\n *\n * The `wss` parameter is needed because the `ws` library re-emits HTTP server\n * errors on the WebSocketServer — without a handler the error becomes unhandled.\n */\nfunction listenWithRetry(\n server: Server,\n port: number,\n host: string,\n wss?: WebSocketServer,\n maxRetries = 20,\n delay = 500,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let attempts = 0;\n\n // Absorb EADDRINUSE errors re-emitted by the ws library on the WSS\n // during the retry window. Without this, Node.js crashes on unhandled error.\n const wssErrorHandler = () => { /* absorbed — handled via server 'error' */ };\n wss?.on('error', wssErrorHandler);\n\n const tryListen = () => {\n const onError = (err: NodeJS.ErrnoException) => {\n // Clean up the 'listening' callback that server.listen() registered\n server.removeAllListeners('listening');\n\n if (err.code === 'EADDRINUSE' && attempts < maxRetries) {\n attempts++;\n console.log(`[cli] Port ${port} in use, retrying in ${delay}ms (${attempts}/${maxRetries})...`);\n setTimeout(tryListen, delay);\n } else {\n wss?.removeListener('error', wssErrorHandler);\n reject(err);\n }\n };\n\n server.once('error', onError);\n server.listen(port, host, () => {\n server.removeListener('error', onError);\n wss?.removeListener('error', wssErrorHandler);\n resolve();\n });\n };\n\n tryListen();\n });\n}\n\nasync function main() {\n // Avant tout : ce que ce process hérite ici, le serveur, l'agent local et\n // chaque service lancé ensuite en héritent à leur tour. Un raccourci de bureau\n // reçoit le PATH de la session graphique, où ni les gestionnaires de version\n // ni les binaires installés par l'utilisateur (claude, codex) ne figurent.\n const { ensureNodeBinOnPath, ensureUserBinsOnPath, stampConsole } = await import('@runeya/packages-shared/node');\n ensureNodeBinOnPath();\n await ensureUserBinsOnPath();\n\n // Journaux datés du temps écoulé depuis le démarrage. Jamais en --pull-env :\n // sa sortie est lue par un script, pas par un humain.\n if (!isPullEnv) {\n stampConsole();\n // Sortie et crash recopiés sur disque : une instance relancée en\n // `stdio: 'ignore'` (bureau, mise à jour) n'a sinon aucune trace.\n try {\n const { installLogFile } = await import('./log-file.js');\n console.log(`[cli] Journal : ${installLogFile().logPath}`);\n } catch (err) {\n console.warn(`[cli] Journal sur disque indisponible : ${(err as Error).message}`);\n }\n }\n\n // Dynamic import ensures env is parsed AFTER we configure it\n const { createLocalServer, pullEnv, PullEnvError, registerInstance, registerCliLocation } = await import('@runeya/apps-server');\n\n // D'où ce CLI a été lancé, pour que l'interface puisse proposer `install-desktop`\n // sans que le serveur ait à deviner un exécutable.\n registerCliLocation({ execPath: process.execPath, cliEntry: fileURLToPath(import.meta.url) });\n\n // Handle --pull-env mode: resolve env vars and print, then exit\n if (isPullEnv) {\n if (!serviceArg) {\n console.error('Error: --service (-s) is required with --pull-env');\n process.exit(1);\n }\n if (!environmentArg) {\n console.error('Error: --environment (-e) is required with --pull-env');\n process.exit(1);\n }\n try {\n const output = await pullEnv({ service: serviceArg, environment: environmentArg });\n if (output) process.stdout.write(output + '\\n');\n process.exit(0);\n } catch (err: unknown) {\n if (err instanceof PullEnvError) {\n console.error(`Error: ${err.message}`);\n process.exit(err.exitCode);\n }\n throw err;\n }\n }\n\n // --agent mode: standalone agent, no server, no UI\n if (isAgentOnly) {\n const { spawn } = await import('node:child_process');\n const agentBin = join(__dirname, 'agent/index.js');\n const child = spawn(process.execPath, [agentBin], { stdio: 'inherit', windowsHide: true });\n child.on('exit', (code) => process.exit(code ?? 0));\n // SIGINT is already delivered to the child by the terminal (same process group).\n // Intercept it here only to suppress Node's default exit so we stay alive\n // until the child finishes its graceful shutdown.\n process.on('SIGINT', () => { /* wait for child to exit */ });\n process.on('SIGTERM', () => child.kill('SIGTERM'));\n return;\n }\n\n /**\n * Un relais de redémarrage (`appUpdate.restart`) est l'exception au garde-fou\n * ci-dessous : l'instance qui répond encore sur le port est précisément celle\n * qu'on remplace. On la laisse s'éteindre, `listenWithRetry` absorbe\n * l'EADDRINUSE du chevauchement. La marque est consommée ici pour ne pas se\n * propager aux services lancés ensuite, qui héritent de cet environnement.\n */\n const isRestartHandoff = process.env.RUNEYA_RESTART_HANDOFF === '1';\n delete process.env.RUNEYA_RESTART_HANDOFF;\n\n /**\n * A shortcut clicked twice must not fail.\n *\n * Starting a second instance on a port a Runeya already holds ends in\n * EADDRINUSE after ten seconds of retries — and from a launcher there is no\n * terminal to read that in. So the port is asked first: if Runeya answers,\n * the right move is to bring up the window that already exists.\n *\n * Only for launcher starts. From a terminal, `runeya` twice on one port is a\n * mistake worth reporting, not one worth papering over.\n */\n if (isFromDesktop && !isRestartHandoff) {\n const port = resolvePort();\n const { probeRuneya } = await import('./free-port.js');\n const existing = await probeRuneya(port);\n if (existing) {\n const url = `http://localhost:${port}`;\n console.log(`[cli] Runeya v${existing.version} déjà en cours sur le port ${port} — ouverture de ${url}`);\n if (!noOpen) await open(url);\n return;\n }\n }\n\n console.log('[cli] Starting Runeya...');\n\n // Create server (automatically spawns local agent)\n const { app, server, wss, shutdown } = await createLocalServer();\n\n // Serve static files from web frontend (embedded in CLI dist during build)\n const webDistPath = join(__dirname, 'web');\n\n /**\n * Les fichiers d'`assets/` portent un hash de leur contenu dans leur nom : une\n * mise à jour produit un NOUVEAU nom, jamais un contenu différent sous le même\n * nom. Ils peuvent donc être gardés indéfiniment, et `immutable` dit au\n * navigateur de ne même pas revalider.\n *\n * Sans cela, `express.static` répond `max-age=0` : chaque rechargement\n * redemande les 295 fichiers pour s'entendre répondre 304. Ce sont des\n * requêtes vides, mais elles occupent les six connexions qu'un navigateur\n * accorde par hôte — pour tous ses onglets à la fois. Avec plusieurs onglets\n * rechargés ensemble, l'ouverture du WebSocket attendait derrière cette file.\n */\n app.use('/assets', express.static(join(webDistPath, 'assets'), {\n immutable: true,\n maxAge: '1y',\n fallthrough: false,\n }));\n\n // Tout le reste (favicon…) : revalidation à chaque fois, ces noms ne changent\n // pas d'une version à l'autre.\n app.use(express.static(webDistPath, {\n index: false,\n setHeaders: (res) => res.setHeader('Cache-Control', 'no-cache'),\n }));\n\n // Fallback to index.html for client-side routing (Vue Router)\n // Express 5 requires explicit path patterns (not '*')\n //\n // JAMAIS mis en cache : c'est lui qui nomme les assets de la version courante.\n // Un index.html périmé rappellerait les fichiers de l'ancienne version — qui,\n // eux, ne sont plus là.\n //\n // `root` + nom relatif, jamais un chemin absolu : `sendFile` refuse tout\n // segment commençant par un point (`dotfiles: 'ignore'` par défaut) et\n // répond 404. Or l'installation la plus courante — `npx @runeya/runeya` —\n // vit sous `~/.npm/_npx/…`. Avec un chemin absolu, la page d'accueil était\n // donc introuvable sur toute machine lancée par npx (ou depuis `~/.nvm`),\n // alors que `/index.html` répondait : `express.static` ne teste, lui, que la\n // partie relative à sa racine.\n app.use((_req: Request, res: Response) => {\n res.setHeader('Cache-Control', 'no-cache');\n res.sendFile('index.html', { root: webDistPath });\n });\n\n const port = resolvePort();\n const host = process.env.RUNEYA_HOST ?? '127.0.0.1';\n\n await listenWithRetry(server, port, host, wss);\n\n // Announce this instance now that the port is really ours, so the settings\n // panel of any Runeya on this machine can list it.\n await registerInstance();\n\n /**\n * L'adresse d'écoute n'est pas une adresse de visite.\n *\n * `0.0.0.0` (et son équivalent IPv6 `::`) dit « toutes les interfaces » à\n * bind ; ce n'est pas une destination. Sous Linux une connexion vers elle\n * retombe sur la boucle locale, ce qui a longtemps masqué le problème — sous\n * Windows elle n'est pas routable du tout, et les navigateurs la bloquent\n * désormais ailleurs aussi. On écoute donc partout, mais on ouvre localhost.\n */\n const browseHost = host === '0.0.0.0' || host === '::' ? 'localhost' : host;\n const appUrl = `http://${browseHost}:${port}`;\n\n if (!noOpen) {\n await open(appUrl);\n }\n\n console.log('\\n🚀 Runeya is running!');\n if (isLanProxy) {\n console.log('🌐 LAN mode: server and service ports exposed on local network');\n }\n printStartupTable(port, appUrl);\n console.log('[cli] Press Ctrl+C to stop');\n\n // Assignée plus bas : l'arrêt doit pouvoir la retirer, et la poser demande\n // de savoir arrêter — les deux se référencent.\n let tray: { stop: () => Promise<void> } | null = null;\n\n let shuttingDown = false;\n const gracefulShutdown = async (signal: string) => {\n if (shuttingDown) {\n console.log(`[cli] Forced exit (second ${signal})`);\n process.exit(1);\n }\n shuttingDown = true;\n console.log(`\\n[cli] Received ${signal}, shutting down gracefully...`);\n\n // L'icône part en premier : elle annonce une instance vivante, et la\n // laisser pendant l'extinction inviterait à cliquer dans le vide.\n await tray?.stop();\n\n // In dev, exit immediately — ports are freed on process death,\n // and the 'exit' handler in agent-spawner SIGKILLs the agent.\n if (process.env.NODE_ENV === 'development') {\n process.exit(0);\n }\n\n await shutdown();\n console.log('[cli] Shutdown complete');\n process.exit(0);\n };\n\n process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));\n process.on('SIGINT', () => gracefulShutdown('SIGINT'));\n\n if (!noTray) {\n const { startInstanceTray } = await import('./tray.js');\n const {\n scheduleSelfRestart,\n listTrayServices,\n onTrayServicesChanged,\n startProcess,\n stopProcess,\n restartProcess,\n networkInfo,\n setLanMode,\n resolveTrayIcon,\n } = await import('@runeya/apps-server');\n\n tray = await startInstanceTray({\n info: { cwd: process.cwd(), port, version, url: appUrl },\n assetsDir: join(__dirname, 'assets'),\n openUrl: (target) => open(target),\n restart: () => scheduleSelfRestart(),\n stop: () => void gracefulShutdown('tray'),\n\n listServices: listTrayServices,\n startService: (id) => startProcess(id),\n // L'arrêt exige l'agent qui le fait tourner : il est dans la vue, et\n // seul un service en cours propose ce geste.\n stopService: async (id) => {\n const service = (await listTrayServices()).find((s) => s.id === id);\n if (!service?.agentId) throw new Error(`Aucun agent pour ${id}`);\n return stopProcess(id, service.agentId);\n },\n restartService: (id) => restartProcess(id),\n onServicesChanged: onTrayServicesChanged,\n // Le logo de l'organisation du projet ouvert, quand il y en a un : c'est ce\n // qui distingue deux Runeya dans la barre système.\n projectIcon: () => resolveTrayIcon(),\n\n lan: {\n read: () => {\n const info = networkInfo();\n // `lanUrls` est déjà ce qu'on voudrait lire au survol : des adresses\n // joignables, pas des IP brutes. Vide hors mode réseau local.\n return { enabled: info.lanEnabled, addresses: info.lanUrls };\n },\n toggle: (enabled) => setLanMode(enabled),\n },\n\n warn: (message) => console.log(message),\n });\n }\n}\n\nmain().catch(async (err) => {\n console.error('[cli] Fatal error:', err);\n\n // With a terminal, the message above is the report. Without one, it goes\n // nowhere: serve it as a page and open it in the browser instead.\n if (startupFailure) {\n try {\n const { diagnose, renderFailurePage, capturedLines, showStartupFailure } = startupFailure;\n const html = renderFailurePage(\n diagnose(err, resolvePort()),\n capturedLines(),\n {\n 'Version': version,\n 'Dossier de travail': process.cwd(),\n 'Node': process.execPath,\n 'Runeya': fileURLToPath(import.meta.url),\n },\n );\n await showStartupFailure(html);\n } catch (reportErr) {\n console.error('[cli] Impossible d\\'afficher le rapport d\\'erreur:', reportErr);\n }\n }\n\n process.exit(1);\n});\n","/**\n * Resolve the HTTP port Runeya should listen on.\n *\n * Only RUNEYA_HTTP_PORT is read (default 4000) — the generic PORT variable is\n * intentionally ignored so it can stay reserved for the user's own services.\n *\n * Throws if the resolved value is not an integer in [1, 65535].\n */\nexport function resolvePort(): number {\n const raw = process.env.RUNEYA_HTTP_PORT;\n if (raw === undefined) return 4000;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(\n `Invalid port: ${JSON.stringify(raw)}. Must be an integer between 1 and 65535.`,\n );\n }\n return port;\n}\n"],"mappings":"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","names":["answers","resolve","port"]}
@@ -977,6 +977,11 @@ var KanbanCardSchema = z19.object({
977
977
  * carte dans la première colonne, sans se taire sur son origine.
978
978
  */
979
979
  archivedFrom: z19.object({ columnId: z19.string().max(128), columnTitle: z19.string().max(200) }).optional(),
980
+ /**
981
+ * Le tableau d'origine d'une carte vue depuis un tableau où elle est posée
982
+ * en référence. Jamais stocké : le serveur le pose à la lecture. Absent chez elle.
983
+ */
984
+ origin: z19.object({ boardId: z19.string().max(64), boardName: z19.string().max(200) }).optional(),
980
985
  /**
981
986
  * Quand la carte a été supprimée. Rien n'est effacé : la carte reste en base,
982
987
  * et toutes les lectures l'écartent.
@@ -1102,6 +1107,13 @@ var KanbanCardRefSchema = z19.object({
1102
1107
  assignees: z19.array(z19.object({ id: z19.string().max(128), name: z19.string().max(200) })).default([]),
1103
1108
  commentCount: z19.number().int().nonnegative().default(0)
1104
1109
  });
1110
+ var KanbanCardLocationSchema = z19.object({
1111
+ boardId: z19.string().max(64),
1112
+ boardName: z19.string().max(200),
1113
+ columnId: z19.string().max(128),
1114
+ columnTitle: z19.string().max(200),
1115
+ home: z19.boolean()
1116
+ });
1105
1117
  var CloudApiScopeSchema = z20.enum([
1106
1118
  "kanban:read",
1107
1119
  "kanban:write",
@@ -1570,10 +1582,12 @@ var RUNEYA_CAPABILITIES = [
1570
1582
  method: "POST",
1571
1583
  params: [
1572
1584
  { name: "attachmentId", description: "The attachment ID, as listed on the card", required: true },
1573
- { name: "organizationId", description: "Which organization the attachment belongs to. Optional: needed only when the account belongs to several and the error message asks for it.", required: false }
1585
+ { name: "boardId", description: "The board the attachment comes from. Pass it whenever you know it: on a machine linked to several cloud accounts, it is what says which one holds the file.", required: false },
1586
+ { name: "organizationId", description: "Which organization the attachment belongs to. Optional: needed only when neither the board id is known nor the machine has a single account.", required: false }
1574
1587
  ],
1575
1588
  buildInput: (p) => ({
1576
1589
  attachmentId: p.attachmentId,
1590
+ ...p.boardId ? { boardId: p.boardId } : {},
1577
1591
  ...p.organizationId ? { orgId: p.organizationId } : {}
1578
1592
  })
1579
1593
  },
@@ -1670,6 +1684,40 @@ var RUNEYA_CAPABILITIES = [
1670
1684
  ...p.parentBoardId ? { parentBoardId: p.parentBoardId } : {}
1671
1685
  })
1672
1686
  },
1687
+ {
1688
+ name: "kanban_place_card",
1689
+ description: "Place an existing kanban card on another board of the same organization, without copying it \u2014 a reference. The card stays on its origin board; the target board gives it a column of its own. Editing, commenting, archiving or deleting the card from either board acts on the same card; moving it on the target board only moves the reference. Use it to show an issue from an issue list on a team kanban, for instance.",
1690
+ route: "kanban.placeCard",
1691
+ method: "POST",
1692
+ params: [
1693
+ { name: "boardId", description: "The board to place the card on (not its own board)", required: true },
1694
+ { name: "cardId", description: "The card to place", required: true },
1695
+ { name: "column", description: "The column on that board: its ID or its title", required: true }
1696
+ ],
1697
+ buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId, column: p.column })
1698
+ },
1699
+ {
1700
+ name: "kanban_remove_card_placement",
1701
+ description: "Remove a card from a board where it was placed as a reference. The card itself is untouched and stays on its origin board.",
1702
+ route: "kanban.unplaceCard",
1703
+ method: "POST",
1704
+ params: [
1705
+ { name: "boardId", description: "The board the reference is on", required: true },
1706
+ { name: "cardId", description: "The card to remove from that board", required: true }
1707
+ ],
1708
+ buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId })
1709
+ },
1710
+ {
1711
+ name: "kanban_get_card_placements",
1712
+ description: "List where a kanban card is: its origin board (home: true), then every board it is placed on as a reference, with the column on each. kanban_get_board flags a referenced card with an `origin` field naming its origin board.",
1713
+ route: "kanban.getCardPlacements",
1714
+ method: "GET",
1715
+ params: [
1716
+ { name: "boardId", description: "Any board the card is on", required: true },
1717
+ { name: "cardId", description: "The card", required: true }
1718
+ ],
1719
+ buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId })
1720
+ },
1673
1721
  {
1674
1722
  name: "kanban_add_card",
1675
1723
  description: "Add a new card to a kanban column.",