@runeya/runeya 2.0.21 → 2.0.23

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 (321) hide show
  1. package/agent/index.js +103 -9
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-2P4THUVA.js +7 -0
  4. package/{chunk-PUNE7P6S.js → chunk-Y2R42AXQ.js} +848 -561
  5. package/chunk-Y2R42AXQ.js.map +1 -0
  6. package/index.js +92 -43
  7. package/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/{src-K32WI425.js → src-3TOPUVAK.js} +1354 -1083
  10. package/src-3TOPUVAK.js.map +1 -0
  11. package/web/assets/AgentScalarChatInterface.vue-Bk_cXx74.js +13 -0
  12. package/web/assets/AgentTerminalPage-DqXrABlh.js +1 -0
  13. package/web/assets/AiChatPage-B3P7qefE.js +1 -0
  14. package/web/assets/AppLayout-B2e7K0cZ.js +1 -0
  15. package/web/assets/{AuditPage-CUdrFc3C.js → AuditPage-B55qp9MV.js} +1 -1
  16. package/web/assets/{CloudAccountPage-am6AxJRB.js → CloudAccountPage-CGyW79m_.js} +1 -1
  17. package/web/assets/CloudAccountPage-DTity1N0.js +1 -0
  18. package/web/assets/CloudMessagingPage-7RQRbAOB.js +2 -0
  19. package/web/assets/CloudMessagingPage-BUnfNsBz.css +1 -0
  20. package/web/assets/{DashboardPage-pA2caWR9.js → DashboardPage-CwmCbP3t.js} +1 -1
  21. package/web/assets/{EditorIcon-C9E8hRyM.js → EditorIcon-BaldhaS8.js} +1 -1
  22. package/web/assets/{EnvironmentForm-XKpxQ-45.js → EnvironmentForm-Da52SUx4.js} +2 -2
  23. package/web/assets/EnvironmentFormPage-Bab5iOWS.js +1 -0
  24. package/web/assets/EnvironmentListPage-O1fIWq-g.js +1 -0
  25. package/web/assets/EnvironmentSelector-BWzLYssP.js +1 -0
  26. package/web/assets/KanbanBoardPage-BOMHKLHX.css +1 -0
  27. package/web/assets/KanbanBoardPage-BvtIaHqG.js +4 -0
  28. package/web/assets/KanbanListPage-BoNcgR3h.css +1 -0
  29. package/web/assets/KanbanListPage-Cv_QJF00.js +1 -0
  30. package/web/assets/LaunchProjectPage-uB7iIMG1.js +1 -0
  31. package/web/assets/{LaunchServicesPage-CViec72c.js → LaunchServicesPage-1ZN1JYqH.js} +1 -1
  32. package/web/assets/LogViewer-BJxpIK11.js +3 -0
  33. package/web/assets/{ProcessControls-D97-A3dI.js → ProcessControls-C2EYsD-P.js} +1 -1
  34. package/web/assets/ProcessDetailPage-DdOfegjU.js +1 -0
  35. package/web/assets/{ProcessMetrics-CNJ30_ef.js → ProcessMetrics-DpDxpUpO.js} +2 -2
  36. package/web/assets/ProjectDetailPage-DtwRjTew.js +1 -0
  37. package/web/assets/ProjectListPage-D9SUNEFu.js +1 -0
  38. package/web/assets/{RandomJobTestPage-CnY2f-qz.js → RandomJobTestPage-OoAk9WAJ.js} +1 -1
  39. package/web/assets/{ScenarioEditorPage-CBZp7Q34.css → ScenarioEditorPage-GmFIXPDj.css} +1 -1
  40. package/web/assets/ScenarioEditorPage-LhEs4hjS.js +14 -0
  41. package/web/assets/SendToChatDialog-CZWy1K4A.js +1 -0
  42. package/web/assets/ServiceControlsBar-CU6b2KBJ.js +1 -0
  43. package/web/assets/{ServiceDetailPage-DhE1LYrn.css → ServiceDetailPage-BvoD3SB6.css} +1 -1
  44. package/web/assets/ServiceDetailPage-CSaPyV_v.js +2 -0
  45. package/web/assets/ServiceFormPage-D9NFCmmW.js +1 -0
  46. package/web/assets/SettingsPage-BMclTDLy.css +1 -0
  47. package/web/assets/SettingsPage-DXlBFfBq.js +2062 -0
  48. package/web/assets/{SetupPage-CzabsTON.js → SetupPage-dqqf_M_p.js} +1 -1
  49. package/web/assets/UnlockPage-ClEawxOC.css +1 -0
  50. package/web/assets/UnlockPage-DjjpY02d.js +1 -0
  51. package/web/assets/VariablesTable-B4h9eNC_.css +1 -0
  52. package/web/assets/VariablesTable-BFgRnjza.js +1 -0
  53. package/web/assets/{VersionBadge-DUE17MrD.js → VersionBadge-ToW3NGSQ.js} +1 -1
  54. package/web/assets/WikiPage-Djisdsae.js +1 -0
  55. package/web/assets/{WikiView-CUBKfTT-.css → WikiView-CaUVD3f1.css} +1 -1
  56. package/web/assets/WikiView-CiATkY8l.js +1 -0
  57. package/web/assets/{add-scalar-classes-Cfh7_IWy.js → add-scalar-classes-DGwycuRW.js} +3 -3
  58. package/web/assets/auth-errors-DEObS1lG.js +1 -0
  59. package/web/assets/{autocomplete-CrWQ5zMj.js → autocomplete-DP0sGong.js} +1 -1
  60. package/web/assets/{basic-shape-diamond.svg-pjhwN4JT.js → basic-shape-diamond.svg-B0FzUXGN.js} +1 -1
  61. package/web/assets/{basic-shape-hexagon.svg-D0yEDOCC.js → basic-shape-hexagon.svg-BuI9rv44.js} +1 -1
  62. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-DvZ7CdgE.js → basic-shape-primary-circle-ellipse-round.svg-C0WlStV7.js} +1 -1
  63. package/web/assets/{basic-shape-primary-square-rectangle.svg-ChB1hiGR.js → basic-shape-primary-square-rectangle.svg-ByMGtNMn.js} +1 -1
  64. package/web/assets/{basic-shape-shield.svg-Bxrmr3jg.js → basic-shape-shield.svg-h6znjRmZ.js} +1 -1
  65. package/web/assets/{browser-C2YmKQw8.js → browser-DJeF2lho.js} +1 -1
  66. package/web/assets/{browser-CvQ_J2u9.js → browser-oekISqUb.js} +1 -1
  67. package/web/assets/{card-CpyeL2dI.js → card-B7-be48x.js} +1 -1
  68. package/web/assets/{checkbox-DWNV_RaS.js → checkbox-DDSZK_Pi.js} +1 -1
  69. package/web/assets/{chip-8cRcc2L3.js → chip-BVSQ-56q.js} +1 -1
  70. package/web/assets/{client-DNj6VDDb.js → client-DWfGqqLr.js} +1 -1
  71. package/web/assets/cloud-chat.store-BLFChRpl.js +1 -0
  72. package/web/assets/cloud-client-GmxBhiFl.js +1 -0
  73. package/web/assets/{column-DtLr_xcq.js → column-BHRqZfnh.js} +7 -7
  74. package/web/assets/{computer-device-desktop-monitor.svg-CLGySxTI.js → computer-device-desktop-monitor.svg-CL9XulYR.js} +1 -1
  75. package/web/assets/{computer-device-desktop.svg-DJ9n7_Sq.js → computer-device-desktop.svg-CKWeVTzO.js} +1 -1
  76. package/web/assets/{computer-device-laptop.svg-k6zcRWN4.js → computer-device-laptop.svg-wAkdRlEi.js} +1 -1
  77. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-pwKHMzIK.js → computer-device-mobile-phone-android-samsung-back.svg-DOahzxu1.js} +1 -1
  78. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-CR796QT_.js → computer-device-mobile-phone-android-samsung.svg-FBZO3WLS.js} +1 -1
  79. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-CY2r7WXK.js → computer-device-mobile-phone-iphone-x-back.svg-CCLknBWv.js} +1 -1
  80. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-BHISnTWX.js → computer-device-mobile-phone-iphone-x.svg-CWdT8wN4.js} +1 -1
  81. package/web/assets/{computer-device-mobile-tablet-touch.svg-DQqDUvnk.js → computer-device-mobile-tablet-touch.svg-Bx1A6qUQ.js} +1 -1
  82. package/web/assets/{computer-device-mobile-tablet.svg-4Ab5vdIo.js → computer-device-mobile-tablet.svg-JpmP6yqf.js} +1 -1
  83. package/web/assets/{computer-device-network-ethernet-cat6.svg-DJf04S68.js → computer-device-network-ethernet-cat6.svg-D6WFbLbI.js} +1 -1
  84. package/web/assets/{computer-device-network-lan-www.svg-CaETgcOJ.js → computer-device-network-lan-www.svg-U8NMAi5b.js} +1 -1
  85. package/web/assets/{computer-device-network-wifi-connection.svg-PndfmFuy.js → computer-device-network-wifi-connection.svg-FuRBLyH4.js} +1 -1
  86. package/web/assets/{computer-device-network-wifi-router.svg-CBLwEbOV.js → computer-device-network-wifi-router.svg-BMzCP2Aw.js} +1 -1
  87. package/web/assets/{cssMode-RVPpAuHz.js → cssMode--4DE_809.js} +1 -1
  88. package/web/assets/{divider-CYFRNVal.js → divider-hPOrl1Iy.js} +1 -1
  89. package/web/assets/{ecology-science-erlenmeyer-flask.svg-BYXK2uKa.js → ecology-science-erlenmeyer-flask.svg-Bnte5Nm-.js} +1 -1
  90. package/web/assets/{fieldset-BCXBPIiB.js → fieldset-BSwilV-Y.js} +1 -1
  91. package/web/assets/{freemarker2-B6guP0jO.js → freemarker2-DUNAk_0P.js} +1 -1
  92. package/web/assets/{handlebars-BxlL79LN.js → handlebars-CoM2DS_1.js} +1 -1
  93. package/web/assets/{html-Dr0M60O0.js → html-CgEDNZ_R.js} +1 -1
  94. package/web/assets/{htmlMode-B63yAhXq.js → htmlMode-QDDBWVzd.js} +1 -1
  95. package/web/assets/{image-flash-lightning.svg-DVtJFd09.js → image-flash-lightning.svg-ZIxta1IB.js} +1 -1
  96. package/web/assets/{image-picture-flower.svg-BaBdyWt-.js → image-picture-flower.svg-BXxhgW1Q.js} +1 -1
  97. package/web/assets/{index-BTYNCNK0.js → index-Dl-HyfHx.js} +244 -244
  98. package/web/assets/{index-DS4VL_1S.css → index-gKo3WNkm.css} +1 -1
  99. package/web/assets/{interface-alert-exclamation-diamond.svg-BQYncN9j.js → interface-alert-exclamation-diamond.svg-bVfL6jBy.js} +1 -1
  100. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-BvQxARpD.js → interface-alert-exclamation-triangle-warning.svg-BrapXyrc.js} +1 -1
  101. package/web/assets/{interface-alert-information-circle.svg-BbiwUtTt.js → interface-alert-information-circle.svg--W3bUw5n.js} +1 -1
  102. package/web/assets/{interface-award-crown.svg-BJs2BJ9S.js → interface-award-crown.svg-CBbbHFIN.js} +1 -1
  103. package/web/assets/{interface-bookmark.svg-WUL572zM.js → interface-bookmark-tag.svg-C4_v-zTY.js} +1 -1
  104. package/web/assets/{interface-bookmark-tag.svg-huwK9o4X.js → interface-bookmark.svg-CIY69-iu.js} +1 -1
  105. package/web/assets/{interface-calendar-date-one.svg-CEkOALKM.js → interface-calendar-date-one.svg-CD7WxblA.js} +1 -1
  106. package/web/assets/{interface-content-book-open-pages.svg-CDxvdidz.js → interface-content-book-open-pages.svg-BamW-eNv.js} +1 -1
  107. package/web/assets/{interface-content-book-page.svg-ROxK9a-V.js → interface-content-book-page.svg-CpHhLWQE.js} +1 -1
  108. package/web/assets/{interface-content-file.svg-69XGQGxu.js → interface-content-file.svg-BmqLN3iR.js} +1 -1
  109. package/web/assets/interface-content-folder.svg-B2phMZXd.js +1 -0
  110. package/web/assets/{interface-content-folder.svg-BjXgivIz.js → interface-content-folder.svg-ffbcORbT.js} +1 -1
  111. package/web/assets/{interface-copy-clipboard.svg-BAAZ1QLS.js → interface-copy-clipboard.svg-MdbbmqXS.js} +1 -1
  112. package/web/assets/{interface-edit-attachment.svg-BCmyBBsd.js → interface-edit-attachment.svg-DtAVj6tp.js} +1 -1
  113. package/web/assets/{interface-edit-binocular.svg-BI3JPE3j.js → interface-edit-binocular.svg-DfT11Y7N.js} +1 -1
  114. package/web/assets/{interface-edit-magic-wand.svg-DhECH5yw.js → interface-edit-magic-wand.svg-D4mLRHJs.js} +1 -1
  115. package/web/assets/{interface-edit-tool-paint-roller.svg-0-Os9Flg.js → interface-edit-tool-paint-roller.svg-Cxa7Ct4C.js} +1 -1
  116. package/web/assets/{interface-edit-tool-pencil.svg-CM5UI5Rv.js → interface-edit-tool-pencil.svg-IRM3BHKY.js} +1 -1
  117. package/web/assets/{interface-favorite-award.svg-BZNkuFiE.js → interface-favorite-award.svg-CLGZ65SX.js} +1 -1
  118. package/web/assets/{interface-favorite-flag.svg-CeHd37SC.js → interface-favorite-flag.svg-ju-VMCAF.js} +1 -1
  119. package/web/assets/{interface-favorite-heart.svg-D5dJ4sOz.js → interface-favorite-heart.svg-DsWpztN8.js} +1 -1
  120. package/web/assets/{interface-favorite-star.svg-B7YeWzmv.js → interface-favorite-star.svg-CCPUYvBO.js} +1 -1
  121. package/web/assets/{interface-favorite-stars-sparkles.svg-BwqCtLSB.js → interface-favorite-stars-sparkles.svg-nf2ra-Mg.js} +1 -1
  122. package/web/assets/{interface-hierarchy-flowchart.svg-q70-KJ80.js → interface-hierarchy-flowchart.svg-CPrjsUHp.js} +1 -1
  123. package/web/assets/{interface-home-house.svg-CUco1zSk.js → interface-home-house.svg-DyWL4dgs.js} +1 -1
  124. package/web/assets/{interface-hyperlink.svg-D6Z-QbxJ.js → interface-hyperlink.svg-DgjI4lGJ.js} +1 -1
  125. package/web/assets/{interface-lighting-brightness.svg-DDJ8wIbt.js → interface-lighting-brightness.svg-BhomgGV0.js} +1 -1
  126. package/web/assets/{interface-lock-closed.svg-1GuN2t9d.js → interface-lock-closed.svg-CDDGuaUG.js} +1 -1
  127. package/web/assets/{interface-lock-open-unlock.svg-CmSL9GCo.js → interface-lock-open-unlock.svg-DxOzefwC.js} +1 -1
  128. package/web/assets/{interface-login-key.svg-Cnur4uLY.js → interface-login-key.svg-CLs728UT.js} +1 -1
  129. package/web/assets/{interface-search.svg-3TqtroIY.js → interface-search.svg-CSE2bUM-.js} +1 -1
  130. package/web/assets/{interface-setting-cog.svg-B7nRfZ1y.js → interface-setting-cog.svg-SfBQ-E_y.js} +1 -1
  131. package/web/assets/{interface-share-megaphone-bullhorn.svg-DraAk4oI.js → interface-share-megaphone-bullhorn.svg-Cg9QEbsW.js} +1 -1
  132. package/web/assets/{interface-share-rocket.svg-CGRtk6J4.js → interface-share-rocket.svg-V9fWt6R5.js} +1 -1
  133. package/web/assets/{interface-share-satellite.svg-COo3kZqu.js → interface-share-satellite.svg-BoAJlAy3.js} +1 -1
  134. package/web/assets/{interface-share-space-ship.svg-iYhfpKY2.js → interface-share-space-ship.svg-CWoqBiZI.js} +1 -1
  135. package/web/assets/{interface-share.svg-BqVe3lWw.js → interface-share.svg-DW6AG6rC.js} +1 -1
  136. package/web/assets/{interface-signal-square.svg-DmGT2kio.js → interface-signal-square.svg-DktTeL2e.js} +1 -1
  137. package/web/assets/{interface-time-clock-circle.svg-Djt_VZVX.js → interface-time-clock-circle.svg-BBnimglQ.js} +1 -1
  138. package/web/assets/{interface-time-hour-glass.svg-DxZVLqJ6.js → interface-time-hour-glass.svg-WNSRow4o.js} +1 -1
  139. package/web/assets/{interface-users-multiple.svg-n8dIXu9r.js → interface-users-multiple.svg-DoeSSDNX.js} +1 -1
  140. package/web/assets/{interface-weather-moon.svg-CExHl6HQ.js → interface-weather-moon.svg-BzUq31T3.js} +1 -1
  141. package/web/assets/{javascript-6lVkE6gP.js → javascript-CDsgZK6E.js} +1 -1
  142. package/web/assets/{jsonMode-CySg6s8T.js → jsonMode-BOQojnkz.js} +1 -1
  143. package/web/assets/{liquid-DSLZY9bf.js → liquid-CT8_Ig_a.js} +1 -1
  144. package/web/assets/{lspLanguageFeatures-C6Gb5-pg.js → lspLanguageFeatures-BW3wbtds.js} +1 -1
  145. package/web/assets/{mail-chat-bubble-square.svg-BqpIkvR_.js → mail-chat-bubble-square.svg-BB3D-jS5.js} +1 -1
  146. package/web/assets/{mail-send-email-paper-airplane.svg-DUkiRZ1x.js → mail-send-email-paper-airplane.svg-BGUFNdDy.js} +1 -1
  147. package/web/assets/{mail-send-envelope.svg-DjjLaPUo.js → mail-send-envelope.svg-CiEf_X3v.js} +1 -1
  148. package/web/assets/{mdx-DuCKQZ3-.js → mdx-BzJqzXG7.js} +1 -1
  149. package/web/assets/{message-CJ-X_z47.js → message-DQfiz3F1.js} +1 -1
  150. package/web/assets/{minus-RXlNS6-g.js → minus-Bh-ngAnz.js} +1 -1
  151. package/web/assets/modal-B6tgGww3.js +1 -0
  152. package/web/assets/{modal-DfNn2ioJ.js → modal-BH8tCrqE.js} +1 -1
  153. package/web/assets/{money-cashier-receipt.svg-CUzOL6J5.js → money-cashier-receipt.svg-nbLywLCi.js} +1 -1
  154. package/web/assets/{money-currency-dollar-pay.svg-CQY_KC9b.js → money-currency-dollar-pay.svg-HtpV_93j.js} +1 -1
  155. package/web/assets/{money-graph-arrow-increase.svg-BDu5i5aF.js → money-graph-arrow-increase.svg-BG4s2n8-.js} +1 -1
  156. package/web/assets/{money-graph-bar-chart-increase.svg-xBlBZYiv.js → money-graph-bar-chart-increase.svg-7ivMfz5M.js} +1 -1
  157. package/web/assets/{multiselect-BbY8SFyO.js → multiselect-CE72JxFr.js} +1 -1
  158. package/web/assets/{nature-ecology-leaf.svg-C_T6wTIZ.js → nature-ecology-leaf.svg-D60IfAE-.js} +1 -1
  159. package/web/assets/{password-B_pKmF_I.js → password-CZ-A_ePM.js} +1 -1
  160. package/web/assets/{phone-telephone.svg-DzUP7zCx.js → phone-telephone.svg-B0OCaSQr.js} +1 -1
  161. package/web/assets/{plus-DfsTcd_H.js → plus-DZy8IO4Z.js} +1 -1
  162. package/web/assets/process.store-XR7BoPqt.js +1 -0
  163. package/web/assets/{programming-bug.svg-C1NvPH7_.js → programming-bug.svg-DDZ7-1a6.js} +1 -1
  164. package/web/assets/{programming-cloud.svg-hVwd5xzu.js → programming-cloud.svg-BZHqbFE1.js} +1 -1
  165. package/web/assets/{programming-computer-database-server.svg-DXvcm62E.js → programming-computer-database-server.svg-CPK0axOF.js} +1 -1
  166. package/web/assets/{programming-computer-database.svg-w2w_GH3u.js → programming-computer-database.svg-Dat_iKWZ.js} +1 -1
  167. package/web/assets/{programming-module-four-layout.svg-DLcwogv0.js → programming-module-four-layout.svg-DYTRTKmF.js} +1 -1
  168. package/web/assets/{programming-module-three.svg-Cfjjs8tf.js → programming-module-three.svg-CU563xPB.js} +1 -1
  169. package/web/assets/{programming-module.svg-ChyMdD4E.js → programming-module.svg-DdRe2d3E.js} +1 -1
  170. package/web/assets/{programming-script-code.svg-D8N99_E3.js → programming-script-code.svg-D5dDzg1B.js} +1 -1
  171. package/web/assets/{python-BT-UPVMQ.js → python-C0tzbgs4.js} +1 -1
  172. package/web/assets/{radiobutton-C9iiy1zq.js → radiobutton-BYEniO96.js} +1 -1
  173. package/web/assets/{razor-DKUraACi.js → razor-BNCtUdbo.js} +1 -1
  174. package/web/assets/router-BpJBXGio.js +1 -0
  175. package/web/assets/{settings.store-PSHvWAzl.js → settings.store-hW2r6ht2.js} +1 -1
  176. package/web/assets/{shopping-cart.svg-C_kTBKvf.js → shopping-cart.svg-yNDOoxiE.js} +1 -1
  177. package/web/assets/{shopping-gift-present.svg-DIfbUecw.js → shopping-gift-present.svg-ZAFtf911.js} +1 -1
  178. package/web/assets/{shopping-shipping-box-parcel-package.svg-DgeqoYij.js → shopping-shipping-box-parcel-package.svg-C_128hOx.js} +1 -1
  179. package/web/assets/{TiptapEditor-CI1pNNDH.js → src-BTZHXzoT.js} +78 -78
  180. package/web/assets/src-CmzYYWZd.css +1 -0
  181. package/web/assets/src-DtlOQm4K.js +1 -0
  182. package/web/assets/src-ZfecJkW9.css +1 -0
  183. package/web/assets/{tabpanel-tgozju6m.js → tabpanel-BiifLLS6.js} +2 -2
  184. package/web/assets/{tag-new-circle.svg-yzrCIErB.js → tag-new-circle.svg-0OOws8vC.js} +1 -1
  185. package/web/assets/{travel-map-earth-globe.svg-C9CMICQQ.js → travel-map-earth-globe.svg-CvwB8JlN.js} +1 -1
  186. package/web/assets/trpc-D0HTyuMG.js +1 -0
  187. package/web/assets/{tsMode-gblCXC3w.js → tsMode-CadFFP73.js} +1 -1
  188. package/web/assets/{typescript-DeVzlCKf.js → typescript-TsqAe_2a.js} +1 -1
  189. package/web/assets/useAiChatDrawer-2ELNp1KU.js +1 -0
  190. package/web/assets/useCloudOrg-BAtUa48Z.css +1 -0
  191. package/web/assets/useCloudOrg-DuypVndp.js +1 -0
  192. package/web/assets/{xml-UwUjH4rq.js → xml-bgdRCuvM.js} +1 -1
  193. package/web/assets/{yaml-DSs0npSB.js → yaml-BRUv0H85.js} +1 -1
  194. package/web/index.html +2 -2
  195. package/agent-manager-QPZUYLJA.js +0 -8
  196. package/chunk-LZWBXYUE.js +0 -354
  197. package/chunk-LZWBXYUE.js.map +0 -1
  198. package/chunk-PUNE7P6S.js.map +0 -1
  199. package/chunk-Q4N37KNN.js +0 -202
  200. package/chunk-Q4N37KNN.js.map +0 -1
  201. package/kanban-commands-53WWMCHQ.js +0 -14
  202. package/kanban-commands-53WWMCHQ.js.map +0 -1
  203. package/src-K32WI425.js.map +0 -1
  204. package/web/assets/AgentScalarChatInterface.vue-S7bbKRqw.js +0 -13
  205. package/web/assets/AgentTerminalPage-c0fCTCzP.js +0 -1
  206. package/web/assets/AiChatPage-B-kfiUI-.js +0 -1
  207. package/web/assets/AppLayout-COTo6zQf.js +0 -1
  208. package/web/assets/CloudAccountPage-H1QGj9tJ.js +0 -1
  209. package/web/assets/CloudMessagingPage-BEtd8B8B.js +0 -2
  210. package/web/assets/CloudMessagingPage-sgMxCh6j.css +0 -1
  211. package/web/assets/EnvironmentFormPage-DMpiq3Zx.js +0 -1
  212. package/web/assets/EnvironmentListPage-C21qJ3iw.js +0 -1
  213. package/web/assets/EnvironmentSelector-Be9yMkjY.js +0 -1
  214. package/web/assets/KanbanBoardPage-CyUTDl_W.css +0 -1
  215. package/web/assets/KanbanBoardPage-SNCSxTz7.js +0 -4
  216. package/web/assets/KanbanListPage-B3JpbFlv.css +0 -1
  217. package/web/assets/KanbanListPage-D-kgPmxI.js +0 -1
  218. package/web/assets/LaunchProjectPage-BbNDYePE.js +0 -1
  219. package/web/assets/LogViewer-CHyPfODD.js +0 -3
  220. package/web/assets/ProcessDetailPage-BlQJDM-W.js +0 -1
  221. package/web/assets/ProjectDetailPage-DdRYMfcl.js +0 -1
  222. package/web/assets/ProjectListPage-BDLXGuEj.js +0 -1
  223. package/web/assets/ScenarioEditorPage-CFNTrVlY.js +0 -14
  224. package/web/assets/SendToChatDialog-BJjY1MAh.css +0 -1
  225. package/web/assets/SendToChatDialog-CtGhyaOw.js +0 -1
  226. package/web/assets/ServiceControlsBar-m_AQmRiR.js +0 -1
  227. package/web/assets/ServiceDetailPage-RCl6IsBs.js +0 -2
  228. package/web/assets/ServiceFormPage-CIJ-tFub.js +0 -1
  229. package/web/assets/SettingsPage-CPF7w35a.css +0 -1
  230. package/web/assets/SettingsPage-tK-RiJrj.js +0 -2062
  231. package/web/assets/SetupKeyPage-CzB_AMfr.js +0 -1
  232. package/web/assets/SetupKeyPage-DoY1Ua1s.css +0 -1
  233. package/web/assets/TiptapEditor-bdEPMd-W.css +0 -1
  234. package/web/assets/UnlockPage-DWM5symg.js +0 -1
  235. package/web/assets/UnlockPage-EZOi-H7d.css +0 -1
  236. package/web/assets/VariablesTable-Bkv0MOKi.css +0 -1
  237. package/web/assets/VariablesTable-DoNJ4OIn.js +0 -1
  238. package/web/assets/WikiPage-COdO-49s.js +0 -1
  239. package/web/assets/WikiView-BS9HHroX.js +0 -1
  240. package/web/assets/auth-errors-D-uOLmKt.js +0 -1
  241. package/web/assets/cloud-chat.store-DHiYOkkN.js +0 -1
  242. package/web/assets/cloud-client-COpvz58o.js +0 -1
  243. package/web/assets/interface-content-folder.svg-Bijbx45w.js +0 -1
  244. package/web/assets/modal-BvVbc_6u.js +0 -1
  245. package/web/assets/process.store-BLMSPw2l.js +0 -1
  246. package/web/assets/router-DBNATEGs.js +0 -1
  247. package/web/assets/trpc-3qbDCsqO.js +0 -1
  248. package/web/assets/useAiChatDrawer-BtC-0qN-.js +0 -1
  249. /package/{agent-manager-QPZUYLJA.js.map → agent-manager-2P4THUVA.js.map} +0 -0
  250. /package/web/assets/{abap-lJFTLuI6.js → abap-DMmvWZUy.js} +0 -0
  251. /package/web/assets/{apex-Bp0KWav8.js → apex-D89PiTzU.js} +0 -0
  252. /package/web/assets/{azcli-DBOe7Uvy.js → azcli-ChnjTTA5.js} +0 -0
  253. /package/web/assets/{bat-Dk_NT_by.js → bat-uLzQ_FhK.js} +0 -0
  254. /package/web/assets/{bicep-B07Mrlc7.js → bicep-CMOK7Q1v.js} +0 -0
  255. /package/web/assets/{cameligo-DL5eNXGn.js → cameligo-D1AyojFH.js} +0 -0
  256. /package/web/assets/{clojure-BzV2Yqu_.js → clojure-D9JoUgXn.js} +0 -0
  257. /package/web/assets/{coffee-Bjlj5hmf.js → coffee-D-5sSToL.js} +0 -0
  258. /package/web/assets/{cpp-C9oth5qz.js → cpp-BqT_t_y9.js} +0 -0
  259. /package/web/assets/{csharp-HyGd049S.js → csharp-CuToYI91.js} +0 -0
  260. /package/web/assets/{csp-DjCDOBuX.js → csp-CffcxvlO.js} +0 -0
  261. /package/web/assets/{css-DIL8RTJ3.js → css-CUD0biQW.js} +0 -0
  262. /package/web/assets/{cypher-DX1zPXL5.js → cypher-ByyWL_9U.js} +0 -0
  263. /package/web/assets/{dart-BBQZd_ZZ.js → dart-B__ipcUn.js} +0 -0
  264. /package/web/assets/{dockerfile-Cj0CaWYx.js → dockerfile-DS-_8bWw.js} +0 -0
  265. /package/web/assets/{ecl-BjmWCTJg.js → ecl-DWsNCBUj.js} +0 -0
  266. /package/web/assets/{elixir-3uWoEjXX.js → elixir-OfjEy-3g.js} +0 -0
  267. /package/web/assets/{flow9-D4r739e9.js → flow9-BPX22hxS.js} +0 -0
  268. /package/web/assets/{fsharp-B3ycAx5T.js → fsharp-CVJcfIUn.js} +0 -0
  269. /package/web/assets/{go-DFHBjfCl.js → go-CfRHZ3yg.js} +0 -0
  270. /package/web/assets/{graphql-DBjIbM_e.js → graphql-BZaO3ttK.js} +0 -0
  271. /package/web/assets/{hcl-Buu_1Hsp.js → hcl-BF9-qUku.js} +0 -0
  272. /package/web/assets/{ini-CgzZhgz_.js → ini-BUvw6xhr.js} +0 -0
  273. /package/web/assets/{java-BczDO0q5.js → java-BZ5BNd2x.js} +0 -0
  274. /package/web/assets/{julia-B142kn-7.js → julia-DN38cgYT.js} +0 -0
  275. /package/web/assets/{kotlin-42XMP7p1.js → kotlin-CjEJKT8q.js} +0 -0
  276. /package/web/assets/{less-BCIsKRqU.js → less-DMEH_Nwq.js} +0 -0
  277. /package/web/assets/{lexon-DTDOH1dQ.js → lexon-CKQUX8Ov.js} +0 -0
  278. /package/web/assets/{lua-BrH-CeNs.js → lua-CuWjKLJ0.js} +0 -0
  279. /package/web/assets/{m3-D-eUeHOI.js → m3-CEp1bVrM.js} +0 -0
  280. /package/web/assets/{markdown-DwpSjZTn.js → markdown-DxtQ_puq.js} +0 -0
  281. /package/web/assets/{mips-DoGTrjVk.js → mips-Dx7qydpO.js} +0 -0
  282. /package/web/assets/{msdax-CQ8McnjQ.js → msdax-DpNCaSiO.js} +0 -0
  283. /package/web/assets/{mysql-Cj3do0ZO.js → mysql-C3xsBDPP.js} +0 -0
  284. /package/web/assets/{objective-c-BZbR8TG8.js → objective-c-BbaVHCX1.js} +0 -0
  285. /package/web/assets/{pascal-23PRDHRq.js → pascal-sDju2faq.js} +0 -0
  286. /package/web/assets/{pascaligo-CxasKTLJ.js → pascaligo-ChCdOH6b.js} +0 -0
  287. /package/web/assets/{perl-Dhxz2Lhx.js → perl-BCgLFj01.js} +0 -0
  288. /package/web/assets/{pgsql-BIbUy7yD.js → pgsql-DtKby69f.js} +0 -0
  289. /package/web/assets/{php-B5qS9qgM.js → php-NYvR17Qb.js} +0 -0
  290. /package/web/assets/{pla-Bolz9fha.js → pla-gSg-RBG0.js} +0 -0
  291. /package/web/assets/{postiats-Ba6dE5lj.js → postiats-BQxHyDfy.js} +0 -0
  292. /package/web/assets/{powerquery-2jW3ONgo.js → powerquery-DzDmbVZN.js} +0 -0
  293. /package/web/assets/{powershell-CMfBIiJp.js → powershell-yy4Q5BEQ.js} +0 -0
  294. /package/web/assets/{protobuf-BpaquOUj.js → protobuf-B_hIk9q0.js} +0 -0
  295. /package/web/assets/{pug-cBG82Ydz.js → pug-CdeEb1ft.js} +0 -0
  296. /package/web/assets/{qsharp-DtqL8Lmt.js → qsharp-CiSquEgT.js} +0 -0
  297. /package/web/assets/{r-CKLew4sj.js → r-D1Ocz1x4.js} +0 -0
  298. /package/web/assets/{redis-BdDxRU-N.js → redis-D42Y12yz.js} +0 -0
  299. /package/web/assets/{redshift-X4a4MlqW.js → redshift-CS62MYfG.js} +0 -0
  300. /package/web/assets/{restructuredtext-LA4Fv05h.js → restructuredtext-CKFSMEXw.js} +0 -0
  301. /package/web/assets/{ruby-CibE_WVM.js → ruby-C4gTaeU3.js} +0 -0
  302. /package/web/assets/{rust-BK6RsiL0.js → rust-C_f2AiHQ.js} +0 -0
  303. /package/web/assets/{sb-BnCCt0Qj.js → sb-CdNdpP1r.js} +0 -0
  304. /package/web/assets/{scala-oaBDrVqj.js → scala-N4EWaFTL.js} +0 -0
  305. /package/web/assets/{scheme-C-wkLC01.js → scheme-CttIARxx.js} +0 -0
  306. /package/web/assets/{scss-BceVGOJY.js → scss-v0wtbxaE.js} +0 -0
  307. /package/web/assets/{shell-CxhBAFA5.js → shell-BJdEWXbc.js} +0 -0
  308. /package/web/assets/{solidity-D9sXgTfx.js → solidity-DKDa59zZ.js} +0 -0
  309. /package/web/assets/{sophia-C4VBhIEM.js → sophia--Kw-vDpr.js} +0 -0
  310. /package/web/assets/{sparql-DbsK2s9y.js → sparql-BI8jS9Pc.js} +0 -0
  311. /package/web/assets/{sql-CuGfE0wH.js → sql-Dz6z--6O.js} +0 -0
  312. /package/web/assets/{st-rB6Bqb2h.js → st-CT8D05gv.js} +0 -0
  313. /package/web/assets/{swift-BtHGe_4T.js → swift-Ayb3KzWx.js} +0 -0
  314. /package/web/assets/{systemverilog-CgKnzwU4.js → systemverilog-BtWJM9O5.js} +0 -0
  315. /package/web/assets/{tcl-DyqQWf87.js → tcl-BJvUWpbV.js} +0 -0
  316. /package/web/assets/{twig-C980XO5P.js → twig-c3YaBCU6.js} +0 -0
  317. /package/web/assets/{typespec-CVmA80px.js → typespec-gFCWLBoZ.js} +0 -0
  318. /package/web/assets/{uuid-2Kb5PDpq.js → uuid-B3B8HIK9.js} +0 -0
  319. /package/web/assets/{vb-rdNQv5VQ.js → vb-CVtfWXV2.js} +0 -0
  320. /package/web/assets/{w3c-keyname-B2B-WOb1.js → w3c-keyname-nn0G3QC2.js} +0 -0
  321. /package/web/assets/{wgsl-64oVbimL.js → wgsl-C_huaGQy.js} +0 -0
@@ -10,7 +10,6 @@ import {
10
10
  HealthCheckSchema,
11
11
  JSLogParser,
12
12
  JSLogParserSchema,
13
- KanbanColumnSchema,
14
13
  LOG_BUFFER_SIZE,
15
14
  LogSourceConfigSchema,
16
15
  NotificationPreferencesSchema,
@@ -27,24 +26,11 @@ import {
27
26
  SettingsSchema,
28
27
  TraefikConfigSchema,
29
28
  UpdateEnvironmentSchema,
29
+ VariableOverrideSchema,
30
30
  VariableSchema,
31
31
  agentManager,
32
32
  agentSpawner,
33
33
  agentStore,
34
- environmentStore,
35
- getCollection,
36
- nativeParsers,
37
- readEntity,
38
- serviceStore,
39
- traefikManager,
40
- writeEntity
41
- } from "./chunk-PUNE7P6S.js";
42
- import {
43
- generateKanbanPopulatePrompt,
44
- generateKanbanSystemPrompt,
45
- kanbanStore
46
- } from "./chunk-LZWBXYUE.js";
47
- import {
48
34
  clearEncryptionKey,
49
35
  clearPendingLegacyKey,
50
36
  decryptValue,
@@ -53,24 +39,32 @@ import {
53
39
  encryptVariableSlots,
54
40
  ensureEncryptionKey,
55
41
  env,
42
+ environmentStore,
56
43
  extractEncryptedVariableSlots,
44
+ getCollection,
57
45
  getEncryptionKey,
58
46
  getPendingLegacyKey,
59
47
  hasEncryptionKey,
60
48
  hasPendingLegacyKey,
61
49
  isCiMode,
50
+ isEncryptedValue,
62
51
  loadFromEnv,
52
+ nativeParsers,
53
+ readEntity,
54
+ serviceStore,
63
55
  setEncryptionKey,
64
56
  setEncryptionKeyInMemory,
65
57
  setPendingLegacyKey,
66
- stableEncryptValue
67
- } from "./chunk-Q4N37KNN.js";
58
+ stableEncryptValue,
59
+ traefikManager,
60
+ writeEntity
61
+ } from "./chunk-Y2R42AXQ.js";
68
62
 
69
63
  // ../server/src/create-server.ts
70
64
  import express from "express";
71
65
  import cors from "cors";
72
66
  import { createServer } from "http";
73
- import { resolve as resolve4 } from "path";
67
+ import { resolve as resolve5 } from "path";
74
68
  import { WebSocketServer } from "ws";
75
69
  import { createExpressMiddleware } from "@trpc/server/adapters/express";
76
70
  import { applyWSSHandler } from "@trpc/server/adapters/ws";
@@ -108,6 +102,10 @@ async function verifyJwt(token) {
108
102
  import { randomBytes as randomBytes2, randomUUID, timingSafeEqual } from "crypto";
109
103
  import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
110
104
  import { join } from "path";
105
+ function maskApiKey(apiKey) {
106
+ const { key, ...rest } = apiKey;
107
+ return { ...rest, keyPreview: key.slice(-4) };
108
+ }
111
109
  var FILE_PUBLIC = "api-keys.json";
112
110
  var FILE_PRIVATE = "api-keys.private.json";
113
111
  var ApiKeyStore = class {
@@ -122,7 +120,7 @@ var ApiKeyStore = class {
122
120
  async persistFile(isPublic) {
123
121
  await this.ensureDir();
124
122
  const path = this.filePath(isPublic);
125
- const keys = Array.from(this.keys.values()).filter((k) => k.isPublic === isPublic);
123
+ const keys = Array.from(this.keys.values()).filter((k) => k.isPublic === isPublic).map((k) => ({ ...k, key: encryptValue(k.key) }));
126
124
  const tmp = path + ".tmp";
127
125
  await writeFile(tmp, JSON.stringify({ keys }, null, 2), "utf-8");
128
126
  await rename(tmp, path);
@@ -137,7 +135,8 @@ var ApiKeyStore = class {
137
135
  const parsed = ApiKeysFileSchema.safeParse(JSON.parse(raw));
138
136
  if (parsed.success) {
139
137
  for (const k of parsed.data.keys) {
140
- this.keys.set(k.id, k);
138
+ const key = isEncryptedValue(k.key) ? decryptValue(k.key) : k.key;
139
+ this.keys.set(k.id, { ...k, key });
141
140
  }
142
141
  }
143
142
  } catch {
@@ -149,9 +148,18 @@ var ApiKeyStore = class {
149
148
  await this.loadFile(this.filePath(true));
150
149
  await this.loadFile(this.filePath(false));
151
150
  }
151
+ /**
152
+ * Keys as exposed to callers — secrets stripped. Use `reveal` to obtain one
153
+ * deliberately; never hand the whole set of secrets out at once.
154
+ */
152
155
  async list() {
153
156
  await this.load();
154
- return Array.from(this.keys.values());
157
+ return Array.from(this.keys.values()).map(maskApiKey);
158
+ }
159
+ /** The plaintext secret of a single key, or null when unknown. */
160
+ async reveal(id) {
161
+ await this.load();
162
+ return this.keys.get(id)?.key ?? null;
155
163
  }
156
164
  async create(name, scopes, isPublic) {
157
165
  await this.load();
@@ -527,7 +535,7 @@ var ProjectStore = class {
527
535
  await this.load();
528
536
  const parsed = ProjectSchema.safeParse({ ...fields, id });
529
537
  if (!parsed.success) {
530
- console.error(`[project-store] Rejected remote project ${id}:`, parsed.error.message);
538
+ console.error("[project-store] Rejected remote project %s:", id, parsed.error.message);
531
539
  return null;
532
540
  }
533
541
  const incoming = toDiskForm(parsed.data);
@@ -640,10 +648,13 @@ var EnvironmentOverridesStore = class {
640
648
  this.diskEncryptedSlots = extractEncryptedVariableSlots(
641
649
  parsed
642
650
  );
643
- this.data = decryptVariableSlots(
651
+ const decrypted = decryptVariableSlots(
644
652
  parsed,
645
653
  "environment-overrides"
646
654
  );
655
+ this.data = Object.fromEntries(
656
+ Object.entries(decrypted).map(([id, override]) => [id, { value: override.value }])
657
+ );
647
658
  } catch {
648
659
  this.data = {};
649
660
  }
@@ -673,9 +684,9 @@ var EnvironmentOverridesStore = class {
673
684
  await this.load();
674
685
  return this.data[variableId] ?? null;
675
686
  }
676
- async setOverride(variableId, variable) {
687
+ async setOverride(variableId, override) {
677
688
  await this.load();
678
- this.data[variableId] = variable;
689
+ this.data[variableId] = { value: override.value };
679
690
  await this.save();
680
691
  }
681
692
  async removeOverride(variableId) {
@@ -728,10 +739,11 @@ var EnvironmentResolver = class {
728
739
  async resolveEnvironment(environmentId) {
729
740
  const visited = /* @__PURE__ */ new Set();
730
741
  const result = await this.resolveDag(environmentId, visited);
731
- const allOverrides = await environmentOverridesStore.getAll();
732
- for (const [varId, override] of Object.entries(allOverrides)) {
733
- if (result.variables[varId]) {
734
- result.variables[varId] = override;
742
+ const overridesByKey = await this.resolveOverridesByKey(environmentId);
743
+ for (const [varId, variable] of Object.entries(result.variables)) {
744
+ const override = overridesByKey[variable.key];
745
+ if (override) {
746
+ result.variables[varId] = { ...variable, value: override.variable.value };
735
747
  }
736
748
  }
737
749
  const rawEnv = {};
@@ -740,12 +752,13 @@ var EnvironmentResolver = class {
740
752
  rawEnv[variable.key] = slot.pre + slot.value + slot.post;
741
753
  }
742
754
  const { ctx: interpolationCtx, secretKeys: ctxSecretKeys } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);
755
+ const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);
743
756
  const env2 = {};
744
757
  const envSecrets = [];
745
758
  for (const [_varId, variable] of Object.entries(result.variables)) {
746
759
  const { value, isSecret } = resolveVariable(variable, interpolationCtx);
747
760
  env2[variable.key] = value;
748
- if (isSecret || this.interpolatesSecretKey(variable.value.value, ctxSecretKeys)) {
761
+ if (isSecret || secretKeys.has(variable.key)) {
749
762
  envSecrets.push(variable.key);
750
763
  }
751
764
  }
@@ -760,10 +773,11 @@ var EnvironmentResolver = class {
760
773
  if (!environmentId) return {};
761
774
  const visited = /* @__PURE__ */ new Set();
762
775
  const result = await this.resolveDag(environmentId, visited);
763
- const allOverrides = await environmentOverridesStore.getAll();
764
- for (const [varId, override] of Object.entries(allOverrides)) {
765
- if (result.variables[varId]) {
766
- result.variables[varId] = override;
776
+ const overridesByKey = await this.resolveOverridesByKey(environmentId);
777
+ for (const [varId, variable] of Object.entries(result.variables)) {
778
+ const override = overridesByKey[variable.key];
779
+ if (override) {
780
+ result.variables[varId] = { ...variable, value: override.variable.value };
767
781
  }
768
782
  }
769
783
  const rawEnv = {};
@@ -789,13 +803,15 @@ var EnvironmentResolver = class {
789
803
  async resolveEnvironmentWithSources(environmentId) {
790
804
  const visited = /* @__PURE__ */ new Set();
791
805
  const result = await this.resolveDag(environmentId, visited);
792
- const allOverrides = await environmentOverridesStore.getAll();
793
- for (const [varId, override] of Object.entries(allOverrides)) {
794
- if (result.variables[varId]) {
795
- result.variables[varId] = override;
806
+ const overridesByKey = await this.resolveOverridesByKey(environmentId);
807
+ for (const [varId, variable] of Object.entries(result.variables)) {
808
+ const override = overridesByKey[variable.key];
809
+ if (override) {
810
+ result.variables[varId] = { ...variable, value: override.variable.value };
796
811
  result.sources[varId] = {
797
812
  ...result.sources[varId],
798
- overridden: true
813
+ overridden: true,
814
+ ...override.source ? { overriddenBy: override.source } : {}
799
815
  };
800
816
  }
801
817
  }
@@ -805,12 +821,13 @@ var EnvironmentResolver = class {
805
821
  rawEnv[variable.key] = slot.pre + slot.value + slot.post;
806
822
  }
807
823
  const { ctx: interpolationCtx, secretKeys: ctxSecretKeys, keySources } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);
824
+ const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);
808
825
  const env2 = {};
809
826
  const envSecrets = [];
810
827
  for (const [_varId, variable] of Object.entries(result.variables)) {
811
828
  const { value, isSecret } = resolveVariable(variable, interpolationCtx);
812
829
  env2[variable.key] = value;
813
- if (isSecret || this.interpolatesSecretKey(variable.value.value, ctxSecretKeys)) {
830
+ if (isSecret || secretKeys.has(variable.key)) {
814
831
  envSecrets.push(variable.key);
815
832
  }
816
833
  }
@@ -835,6 +852,37 @@ var EnvironmentResolver = class {
835
852
  };
836
853
  }
837
854
  /** Check if a raw value string interpolates any key that is secret in the context */
855
+ /**
856
+ * Toutes les clés dont la valeur est, directement ou par interpolation, un
857
+ * secret.
858
+ *
859
+ * Le contexte d'interpolation ne connaît que les secrets *hérités* : les
860
+ * secrets définis dans le même environnement n'y figurent pas, si bien qu'un
861
+ * `AUTH='Bearer {{environment.TOKEN}}'` voisin de son propre `TOKEN` secret
862
+ * ressortait non marqué — et donc non masqué par toute la chaîne en aval.
863
+ *
864
+ * On part des secrets hérités plus ceux déclarés ici, puis on propage
865
+ * jusqu'au point fixe : une valeur qui référence une clé secrète devient
866
+ * secrète à son tour, y compris en chaîne (A secret → B cite A → C cite B).
867
+ */
868
+ collectSecretKeys(variables, parentSecretKeys) {
869
+ const secretKeys = new Set(parentSecretKeys);
870
+ for (const variable of Object.values(variables)) {
871
+ if (variable.value.isSecret) secretKeys.add(variable.key);
872
+ }
873
+ let changed = true;
874
+ while (changed) {
875
+ changed = false;
876
+ for (const variable of Object.values(variables)) {
877
+ if (secretKeys.has(variable.key)) continue;
878
+ if (this.interpolatesSecretKey(variable.value.value, secretKeys)) {
879
+ secretKeys.add(variable.key);
880
+ changed = true;
881
+ }
882
+ }
883
+ }
884
+ return secretKeys;
885
+ }
838
886
  interpolatesSecretKey(rawValue, ctxSecretKeys) {
839
887
  const refs = [...rawValue.matchAll(/\{\{environment\.([^}]+)\}\}/g)].map((m) => m[1]);
840
888
  return refs.some((ref) => ctxSecretKeys.has(ref));
@@ -905,7 +953,49 @@ var EnvironmentResolver = class {
905
953
  if (additionalIds.length === 0) return environment.parentIds;
906
954
  return [...environment.parentIds, ...additionalIds];
907
955
  }
908
- async resolveDag(environmentId, visited) {
956
+ /**
957
+ * Project an environment's variables onto their overrides: keep only the
958
+ * variables that have one, and swap in the override's value slot. The key
959
+ * always comes from the definition.
960
+ *
961
+ * Feeding this projection back through `resolveDag` resolves the override
962
+ * chain with the exact same inheritance and dedup-by-key rules as the value
963
+ * chain — so an override set on a parent env is no longer swallowed when a
964
+ * child (or a service env) redeclares the same key.
965
+ */
966
+ projectOverrides(variables, overrides) {
967
+ const projected = {};
968
+ for (const [varId, variable] of Object.entries(variables)) {
969
+ const override = overrides[varId];
970
+ if (override) {
971
+ projected[varId] = { key: variable.key, value: override.value };
972
+ }
973
+ }
974
+ return projected;
975
+ }
976
+ /**
977
+ * Resolve the override chain independently of the value chain and return the
978
+ * winning override per variable key (empty when nothing is overridden).
979
+ */
980
+ async resolveOverridesByKey(environmentId) {
981
+ const allOverrides = await environmentOverridesStore.getAll();
982
+ if (Object.keys(allOverrides).length === 0) return {};
983
+ const result = await this.resolveDag(
984
+ environmentId,
985
+ /* @__PURE__ */ new Set(),
986
+ (variables) => this.projectOverrides(variables, allOverrides)
987
+ );
988
+ const byKey = {};
989
+ for (const [varId, variable] of Object.entries(result.variables)) {
990
+ const source = result.sources[varId];
991
+ byKey[variable.key] = {
992
+ variable,
993
+ ...source ? { source: { environmentId: source.environmentId, environmentName: source.environmentName } } : {}
994
+ };
995
+ }
996
+ return byKey;
997
+ }
998
+ async resolveDag(environmentId, visited, project) {
909
999
  if (visited.has(environmentId)) {
910
1000
  throw new Error(`Circular dependency detected in environment inheritance for ${environmentId}`);
911
1001
  }
@@ -918,7 +1008,7 @@ var EnvironmentResolver = class {
918
1008
  const parentResults = [];
919
1009
  for (const parentId of effectiveParentIds) {
920
1010
  const parentVisited = new Set(visited);
921
- const parentResult = await this.resolveDag(parentId, parentVisited);
1011
+ const parentResult = await this.resolveDag(parentId, parentVisited, project);
922
1012
  parentResults.push(parentResult);
923
1013
  }
924
1014
  const mergedVariables = {};
@@ -967,7 +1057,11 @@ var EnvironmentResolver = class {
967
1057
  }
968
1058
  }
969
1059
  }
970
- for (const [varId, variable] of Object.entries(environment.variables)) {
1060
+ for (const key of this.collectSecretKeys(mergedVariables, parentSecretKeys)) {
1061
+ parentSecretKeys.add(key);
1062
+ }
1063
+ const ownVariables = project ? project(environment.variables) : environment.variables;
1064
+ for (const [varId, variable] of Object.entries(ownVariables)) {
971
1065
  let wasInherited = varId in mergedVariables;
972
1066
  for (const [existingId, existingVar] of Object.entries(mergedVariables)) {
973
1067
  if (existingVar.key === variable.key && existingId !== varId) {
@@ -1763,13 +1857,13 @@ var agentRouter = router({
1763
1857
  yield { agentId: agent.config.id, status: agent.status };
1764
1858
  }
1765
1859
  const queue = [];
1766
- let resolve5 = null;
1860
+ let resolve6 = null;
1767
1861
  const handler = (event) => {
1768
1862
  if (queue.length >= SUBSCRIPTION_QUEUE_MAX) queue.shift();
1769
1863
  queue.push(event);
1770
- if (resolve5) {
1771
- resolve5();
1772
- resolve5 = null;
1864
+ if (resolve6) {
1865
+ resolve6();
1866
+ resolve6 = null;
1773
1867
  }
1774
1868
  };
1775
1869
  agentManager.on("status", handler);
@@ -1779,7 +1873,7 @@ var agentRouter = router({
1779
1873
  yield queue.shift();
1780
1874
  } else {
1781
1875
  await new Promise((r) => {
1782
- resolve5 = r;
1876
+ resolve6 = r;
1783
1877
  });
1784
1878
  }
1785
1879
  }
@@ -1795,14 +1889,14 @@ import { TRPCError as TRPCError5 } from "@trpc/server";
1795
1889
  var SUBSCRIPTION_QUEUE_MAX2 = LOG_BUFFER_SIZE;
1796
1890
  async function* forwardEvents(eventName, filter) {
1797
1891
  const queue = [];
1798
- let resolve5 = null;
1892
+ let resolve6 = null;
1799
1893
  const handler = (data) => {
1800
1894
  if (filter && !filter(data)) return;
1801
1895
  if (queue.length >= SUBSCRIPTION_QUEUE_MAX2) queue.shift();
1802
1896
  queue.push(data);
1803
- if (resolve5) {
1804
- resolve5();
1805
- resolve5 = null;
1897
+ if (resolve6) {
1898
+ resolve6();
1899
+ resolve6 = null;
1806
1900
  }
1807
1901
  };
1808
1902
  agentManager.on(eventName, handler);
@@ -1812,7 +1906,7 @@ async function* forwardEvents(eventName, filter) {
1812
1906
  yield queue.shift();
1813
1907
  } else {
1814
1908
  await new Promise((r) => {
1815
- resolve5 = r;
1909
+ resolve6 = r;
1816
1910
  });
1817
1911
  }
1818
1912
  }
@@ -1822,7 +1916,7 @@ async function* forwardEvents(eventName, filter) {
1822
1916
  }
1823
1917
  async function* forwardLogBatches(agentId, processId) {
1824
1918
  const queue = [];
1825
- let resolve5 = null;
1919
+ let resolve6 = null;
1826
1920
  const handler = (data) => {
1827
1921
  if (data.agentId !== agentId) return;
1828
1922
  const batch = data.batch;
@@ -1831,9 +1925,9 @@ async function* forwardLogBatches(agentId, processId) {
1831
1925
  if (filtered.length === 0) return;
1832
1926
  if (queue.length >= SUBSCRIPTION_QUEUE_MAX2) queue.shift();
1833
1927
  queue.push(filtered);
1834
- if (resolve5) {
1835
- resolve5();
1836
- resolve5 = null;
1928
+ if (resolve6) {
1929
+ resolve6();
1930
+ resolve6 = null;
1837
1931
  }
1838
1932
  };
1839
1933
  agentManager.on("process:log", handler);
@@ -1843,7 +1937,7 @@ async function* forwardLogBatches(agentId, processId) {
1843
1937
  yield queue.shift();
1844
1938
  } else {
1845
1939
  await new Promise((r) => {
1846
- resolve5 = r;
1940
+ resolve6 = r;
1847
1941
  });
1848
1942
  }
1849
1943
  }
@@ -2315,7 +2409,9 @@ var environmentRouter = router({
2315
2409
  // --- Overrides (flat, by variableId) ---
2316
2410
  setOverride: protectedProcedure.input(z8.object({
2317
2411
  variableId: z8.string().max(128),
2318
- variable: VariableSchema
2412
+ // Only the value slot: the env var name comes from the variable
2413
+ // definition (a `key` sent by an older client is stripped here).
2414
+ variable: VariableOverrideSchema
2319
2415
  })).mutation(async ({ input }) => {
2320
2416
  await environmentOverridesStore.setOverride(input.variableId, input.variable);
2321
2417
  return { success: true };
@@ -2341,6 +2437,72 @@ var environmentRouter = router({
2341
2437
  if (!result) throw new TRPCError6({ code: "INTERNAL_SERVER_ERROR", message: "Failed to update variable" });
2342
2438
  return result;
2343
2439
  }),
2440
+ /**
2441
+ * List everywhere a variable key is used inside a project: environments that
2442
+ * define it, and variables that reference it through {{environment.KEY}}.
2443
+ * Used by the UI to warn before editing a shared variable from a resolved view.
2444
+ */
2445
+ keyUsages: protectedProcedure.input(z8.object({
2446
+ projectId: z8.string().max(128),
2447
+ key: z8.string().min(1).max(256),
2448
+ /** Environment holding the variable about to be edited — enables inherited-impact detection */
2449
+ environmentId: z8.string().max(128).optional()
2450
+ })).query(async ({ input }) => {
2451
+ const environments = await environmentStore.list(input.projectId);
2452
+ const services = await serviceStore.list();
2453
+ const serviceName = (id) => services.find((s) => s.id === id)?.name;
2454
+ const escaped = input.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2455
+ const refPattern = new RegExp(`\\{\\{environment\\.${escaped}\\}\\}`);
2456
+ const definedIn = [];
2457
+ const referencedBy = [];
2458
+ for (const env2 of environments) {
2459
+ const name = serviceName(env2.reference);
2460
+ for (const variable of Object.values(env2.variables ?? {})) {
2461
+ if (variable.key === input.key) {
2462
+ definedIn.push({
2463
+ environmentId: env2.id,
2464
+ environmentName: env2.name,
2465
+ scope: env2.scope ?? "global",
2466
+ ...name ? { serviceName: name } : {}
2467
+ });
2468
+ continue;
2469
+ }
2470
+ const slot = variable.value;
2471
+ if (refPattern.test(`${slot.pre}${slot.value}${slot.post}`)) {
2472
+ referencedBy.push({
2473
+ environmentId: env2.id,
2474
+ environmentName: env2.name,
2475
+ scope: env2.scope ?? "global",
2476
+ ...name ? { serviceName: name } : {},
2477
+ key: variable.key
2478
+ });
2479
+ }
2480
+ }
2481
+ }
2482
+ const inheritedBy = [];
2483
+ if (input.environmentId) {
2484
+ for (const env2 of environments) {
2485
+ if (env2.id === input.environmentId) continue;
2486
+ try {
2487
+ const resolved = await environmentResolver.resolveEnvironmentWithSources(env2.id);
2488
+ const inherits = Object.entries(resolved.variables).some(
2489
+ ([varId, variable]) => variable.key === input.key && resolved.sources[varId]?.environmentId === input.environmentId
2490
+ );
2491
+ if (inherits) {
2492
+ const name = serviceName(env2.reference);
2493
+ inheritedBy.push({
2494
+ environmentId: env2.id,
2495
+ environmentName: env2.name,
2496
+ scope: env2.scope ?? "global",
2497
+ ...name ? { serviceName: name } : {}
2498
+ });
2499
+ }
2500
+ } catch {
2501
+ }
2502
+ }
2503
+ }
2504
+ return { definedIn, referencedBy, inheritedBy };
2505
+ }),
2344
2506
  removeVariable: protectedProcedure.input(z8.object({
2345
2507
  environmentId: z8.string().max(128),
2346
2508
  variableId: z8.string().max(128)
@@ -2355,10 +2517,11 @@ var environmentRouter = router({
2355
2517
 
2356
2518
  // ../server/src/trpc/routers/settings.ts
2357
2519
  import { promises as dns } from "dns";
2520
+ import { resolve } from "path";
2358
2521
  import { z as z9 } from "zod";
2359
2522
  import { TRPCError as TRPCError7 } from "@trpc/server";
2360
2523
  var NotificationPreferencesPatchSchema = NotificationPreferencesSchema.partial().strict();
2361
- function maskApiKey(plaintext) {
2524
+ function maskApiKey2(plaintext) {
2362
2525
  if (plaintext.length <= 8) return "****";
2363
2526
  return plaintext.slice(0, 4) + "****" + plaintext.slice(-4);
2364
2527
  }
@@ -2370,6 +2533,14 @@ var settingsRouter = router({
2370
2533
  const settings = await settingsManager.getSettings();
2371
2534
  return settings;
2372
2535
  }),
2536
+ /**
2537
+ * Paths the server is running from (working directory + data directory).
2538
+ * Read-only, purely informational for the settings UI.
2539
+ */
2540
+ getPaths: protectedProcedure.query(() => ({
2541
+ cwd: process.cwd(),
2542
+ dataDir: resolve(env.DATA_DIR)
2543
+ })),
2373
2544
  /**
2374
2545
  * Update settings (mode, cloudUrl)
2375
2546
  */
@@ -2519,7 +2690,7 @@ var settingsRouter = router({
2519
2690
  let maskedKey = "(aucune)";
2520
2691
  if (p.encryptedApiKey) {
2521
2692
  try {
2522
- maskedKey = maskApiKey(decryptValue(p.encryptedApiKey));
2693
+ maskedKey = maskApiKey2(decryptValue(p.encryptedApiKey));
2523
2694
  } catch {
2524
2695
  maskedKey = "(cl\xE9 invalide)";
2525
2696
  }
@@ -2559,7 +2730,7 @@ var settingsRouter = router({
2559
2730
  ...priv,
2560
2731
  aiProviders: [...priv.aiProviders ?? [], newProvider]
2561
2732
  });
2562
- return { ...newProvider, maskedKey: plainKey ? maskApiKey(plainKey) : "(aucune)", encryptedApiKey: void 0 };
2733
+ return { ...newProvider, maskedKey: plainKey ? maskApiKey2(plainKey) : "(aucune)", encryptedApiKey: void 0 };
2563
2734
  }),
2564
2735
  /**
2565
2736
  * Update AI provider (apiKey optional — omit to keep existing)
@@ -2591,7 +2762,7 @@ var settingsRouter = router({
2591
2762
  let maskedKey = "(aucune)";
2592
2763
  if (updated.encryptedApiKey) {
2593
2764
  try {
2594
- maskedKey = maskApiKey(decryptValue(updated.encryptedApiKey));
2765
+ maskedKey = maskApiKey2(decryptValue(updated.encryptedApiKey));
2595
2766
  } catch {
2596
2767
  }
2597
2768
  }
@@ -2729,7 +2900,7 @@ var settingsRouter = router({
2729
2900
  }
2730
2901
  } catch {
2731
2902
  }
2732
- await new Promise((resolve5) => setTimeout(resolve5, 500));
2903
+ await new Promise((resolve6) => setTimeout(resolve6, 500));
2733
2904
  }
2734
2905
  }),
2735
2906
  /**
@@ -2916,7 +3087,7 @@ import { z as z11 } from "zod";
2916
3087
  import { TRPCError as TRPCError8 } from "@trpc/server";
2917
3088
  import { execFile as execFile2, spawn } from "child_process";
2918
3089
  import { stat as stat2 } from "fs/promises";
2919
- import { resolve, join as join7 } from "path";
3090
+ import { resolve as resolve2, join as join7 } from "path";
2920
3091
  import { homedir as homedir2 } from "os";
2921
3092
 
2922
3093
  // ../server/src/services/user-config-manager.ts
@@ -3008,9 +3179,9 @@ async function getServiceCwd(serviceId) {
3008
3179
  return resolveCwd(rawCwd);
3009
3180
  }
3010
3181
  async function isCommandAvailable(command) {
3011
- return new Promise((resolve5) => {
3182
+ return new Promise((resolve6) => {
3012
3183
  execFile2("which", [command], { timeout: 5e3 }, (err) => {
3013
- resolve5(!err);
3184
+ resolve6(!err);
3014
3185
  });
3015
3186
  });
3016
3187
  }
@@ -3064,7 +3235,7 @@ var editorRouter = router({
3064
3235
  throw new TRPCError8({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
3065
3236
  }
3066
3237
  const expanded = input.path.replace(/^~(\/|$)/, `${process.env["HOME"] ?? "/tmp"}$1`).replace(/\$\{(\w+)\}|\$(\w+)/g, (_, a, b) => process.env[a || b] ?? "");
3067
- const resolvedPath = resolve(expanded || ".");
3238
+ const resolvedPath = resolve2(expanded || ".");
3068
3239
  try {
3069
3240
  await stat2(resolvedPath);
3070
3241
  } catch (err) {
@@ -3130,10 +3301,19 @@ var apiKeysRouter = router({
3130
3301
  create: protectedProcedure.input(KeyInputSchema).mutation(
3131
3302
  ({ input }) => apiKeyStore.create(input.name, input.scopes, input.isPublic)
3132
3303
  ),
3304
+ /**
3305
+ * Renvoie le secret d'une seule clé, sur demande explicite. `list` ne le
3306
+ * contient pas : une clé ne doit jamais être re-servie en masse.
3307
+ */
3308
+ reveal: protectedProcedure.input(z12.object({ id: z12.string().max(128) })).query(async ({ input }) => {
3309
+ const key = await apiKeyStore.reveal(input.id);
3310
+ if (!key) throw new TRPCError9({ code: "NOT_FOUND", message: "API key not found" });
3311
+ return { key };
3312
+ }),
3133
3313
  update: protectedProcedure.input(KeyInputSchema.extend({ id: z12.string().max(128) })).mutation(async ({ input }) => {
3134
3314
  const updated = await apiKeyStore.update(input.id, input.name, input.scopes, input.isPublic);
3135
3315
  if (!updated) throw new TRPCError9({ code: "NOT_FOUND", message: "API key not found" });
3136
- return updated;
3316
+ return maskApiKey(updated);
3137
3317
  }),
3138
3318
  remove: protectedProcedure.input(z12.object({ id: z12.string().max(128) })).mutation(async ({ input }) => {
3139
3319
  const removed = await apiKeyStore.remove(input.id);
@@ -3160,7 +3340,7 @@ var apiKeysRouter = router({
3160
3340
  });
3161
3341
 
3162
3342
  // ../server/src/trpc/routers/chat.ts
3163
- import { z as z14 } from "zod";
3343
+ import { z as z15 } from "zod";
3164
3344
  import Anthropic2 from "@anthropic-ai/sdk";
3165
3345
  import OpenAI2 from "openai";
3166
3346
  import { execFile as execFile3 } from "child_process";
@@ -3794,6 +3974,18 @@ async function startClaudeTranscriptWatcher() {
3794
3974
  }
3795
3975
  }
3796
3976
  }
3977
+ function stopClaudeTranscriptWatcher() {
3978
+ for (const w of watchers) {
3979
+ try {
3980
+ w.close();
3981
+ } catch {
3982
+ }
3983
+ }
3984
+ watchers.length = 0;
3985
+ for (const t2 of pendingResyncs.values()) clearTimeout(t2);
3986
+ pendingResyncs.clear();
3987
+ started = false;
3988
+ }
3797
3989
 
3798
3990
  // ../server/src/services/codex-sessions.ts
3799
3991
  import { readdir as readdir3 } from "fs/promises";
@@ -3827,7 +4019,7 @@ async function codexSessionExists(sessionId) {
3827
4019
  }
3828
4020
 
3829
4021
  // ../server/src/trpc/routers/chat.ts
3830
- import { TRPCError as TRPCError10 } from "@trpc/server";
4022
+ import { TRPCError as TRPCError11 } from "@trpc/server";
3831
4023
 
3832
4024
  // ../server/src/trpc/shared-schemas.ts
3833
4025
  import { z as z13 } from "zod";
@@ -4185,7 +4377,7 @@ function parse(text) {
4185
4377
  return result;
4186
4378
  }
4187
4379
  function getClaudeUsage() {
4188
- return new Promise((resolve5, reject) => {
4380
+ return new Promise((resolve6, reject) => {
4189
4381
  const env2 = { ...process.env };
4190
4382
  delete env2["CLAUDECODE"];
4191
4383
  let claudeBin = "claude";
@@ -4216,7 +4408,7 @@ function getClaudeUsage() {
4216
4408
  clearTimeout(hardTimeout);
4217
4409
  term?.kill();
4218
4410
  const clean = buffer.replaceAll(/\x1b\[[0-9;]*X/g, "\n").replaceAll(/\x1b\[[0-9;]*[mGKHFABCDJsu]/g, "").replaceAll(/\x1b[^a-zA-Z]*[a-zA-Z]/g, "").replaceAll("\r", "");
4219
- resolve5(parse(clean));
4411
+ resolve6(parse(clean));
4220
4412
  }
4221
4413
  const hardTimeout = setTimeout(() => {
4222
4414
  if (!done) finish();
@@ -4350,7 +4542,7 @@ function parseCodexStatusText(text) {
4350
4542
  return entries;
4351
4543
  }
4352
4544
  function getCodexUsageEntries() {
4353
- return new Promise((resolve5, reject) => {
4545
+ return new Promise((resolve6, reject) => {
4354
4546
  let term = null;
4355
4547
  try {
4356
4548
  term = pty2.spawn("codex", [], {
@@ -4380,7 +4572,7 @@ function getCodexUsageEntries() {
4380
4572
  const debugClean = stripAnsi(buffer).slice(-6e3);
4381
4573
  process.stdout.write("[codex-usage] raw tail:\n" + debugClean + "\n");
4382
4574
  }
4383
- resolve5(parseCodexStatusText(buffer));
4575
+ resolve6(parseCodexStatusText(buffer));
4384
4576
  };
4385
4577
  const hardTimeout = setTimeout(() => {
4386
4578
  finish();
@@ -4494,10 +4686,10 @@ var ActiveSessionRegistry = class {
4494
4686
  }
4495
4687
  }
4496
4688
  pushEvent(conversationId, event) {
4497
- if (process.env.DEBUG) console.log("[chat]: event - ", event.type);
4689
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: event - ", event.type);
4498
4690
  const session = this.sessions.get(conversationId);
4499
4691
  if (!session) return;
4500
- if (process.env.DEBUG) console.log("[chat]: event - ", event.type, "session", session.conversationId);
4692
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: event - ", event.type, "session", session.conversationId);
4501
4693
  if (event.type === "delta") {
4502
4694
  session.fullContent += event.text;
4503
4695
  const last = session.parts[session.parts.length - 1];
@@ -4535,15 +4727,15 @@ var ActiveSessionRegistry = class {
4535
4727
  if (session.buffer.length >= MAX_BUFFER) {
4536
4728
  session.buffer.shift();
4537
4729
  }
4538
- if (process.env.DEBUG) console.log("[chat]: event - ", event.type, "push buffer");
4730
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: event - ", event.type, "push buffer");
4539
4731
  session.buffer.push(event);
4540
4732
  session.totalProduced++;
4541
- if (process.env.DEBUG) console.log("[chat]: event - ", event.type, "notify", session.notify.size);
4733
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: event - ", event.type, "notify", session.notify.size);
4542
4734
  for (const cb of session.notify) cb();
4543
4735
  session.notify.clear();
4544
4736
  }
4545
4737
  markDone(conversationId) {
4546
- if (process.env.DEBUG) console.log("[chat]: markdone....", conversationId);
4738
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: markdone....", conversationId);
4547
4739
  const session = this.sessions.get(conversationId);
4548
4740
  if (!session) return;
4549
4741
  session.status = "done";
@@ -4775,15 +4967,15 @@ import { join as join12 } from "path";
4775
4967
 
4776
4968
  // ../server/src/services/run-store-utils.ts
4777
4969
  import { chmod as chmod7, mkdir as mkdir9, rename as rename8, writeFile as writeFile8 } from "fs/promises";
4778
- import { dirname as dirname2, resolve as resolve2, sep } from "path";
4970
+ import { dirname as dirname2, resolve as resolve3, sep } from "path";
4779
4971
  import { readFile as readFile9 } from "fs/promises";
4780
4972
  var SAFE_PATH_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
4781
4973
  function resolveConversationRunDir(rootDirName, conversationId) {
4782
4974
  if (!SAFE_PATH_SEGMENT_RE.test(conversationId)) {
4783
4975
  throw new Error("Invalid conversationId");
4784
4976
  }
4785
- const base = resolve2(env.DATA_DIR, rootDirName);
4786
- const dir = resolve2(base, conversationId);
4977
+ const base = resolve3(env.DATA_DIR, rootDirName);
4978
+ const dir = resolve3(base, conversationId);
4787
4979
  if (!dir.startsWith(base + sep)) {
4788
4980
  throw new Error("Invalid conversationId");
4789
4981
  }
@@ -5028,7 +5220,7 @@ async function fetchProviderModels(provider, plaintextKey) {
5028
5220
  import { randomUUID as randomUUID4 } from "crypto";
5029
5221
  import { mkdir as mkdir11, writeFile as writeFile9, readFile as readFile11, unlink as unlink2, readdir as readdir4 } from "fs/promises";
5030
5222
  import { existsSync } from "fs";
5031
- import { join as join13, extname, resolve as resolve3, sep as sep2 } from "path";
5223
+ import { join as join13, extname, resolve as resolve4, sep as sep2 } from "path";
5032
5224
  var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
5033
5225
  "image/png",
5034
5226
  "image/jpeg",
@@ -5066,8 +5258,8 @@ var ImageStorageService = class {
5066
5258
  const safeExt = ext.replace(/[^a-z.]/g, "");
5067
5259
  const fileName = `${imageId}${safeExt}`;
5068
5260
  const filePath = join13(this.dir, fileName);
5069
- const resolvedPath = resolve3(filePath);
5070
- const resolvedDir = resolve3(this.dir);
5261
+ const resolvedPath = resolve4(filePath);
5262
+ const resolvedDir = resolve4(this.dir);
5071
5263
  if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep2)) {
5072
5264
  throw new Error("Invalid file path");
5073
5265
  }
@@ -5076,15 +5268,25 @@ var ImageStorageService = class {
5076
5268
  await writeFile9(`${filePath}.meta.json`, JSON.stringify(meta), { encoding: "utf-8", mode: 384 });
5077
5269
  return meta;
5078
5270
  }
5079
- async getImagePath(imageId) {
5271
+ /**
5272
+ * Resolve an image on disk.
5273
+ *
5274
+ * `expectedContextId` scopes the lookup: when given, an image stored under a
5275
+ * different context (or under none at all) is reported as missing. Callers
5276
+ * that serve an image through a context-scoped URL MUST pass it — the id
5277
+ * alone is not an authorization, and without the check any imageId could be
5278
+ * fetched under any conversation.
5279
+ */
5280
+ async getImagePath(imageId, expectedContextId) {
5080
5281
  if (!UUID_RE.test(imageId)) return null;
5081
5282
  try {
5082
5283
  const files = await readdir4(this.dir);
5083
5284
  const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
5084
5285
  if (!metaFile) return null;
5085
5286
  const raw = JSON.parse(await readFile11(join13(this.dir, metaFile), "utf-8"));
5287
+ if (expectedContextId !== void 0 && raw.contextId !== expectedContextId) return null;
5086
5288
  if (!existsSync(raw.filePath)) return null;
5087
- return { filePath: raw.filePath, mimeType: raw.mimeType };
5289
+ return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };
5088
5290
  } catch {
5089
5291
  return null;
5090
5292
  }
@@ -5119,8 +5321,8 @@ var ImageStorageService = class {
5119
5321
  })
5120
5322
  );
5121
5323
  }
5122
- async readImageAsBase64(imageId) {
5123
- const result = await this.getImagePath(imageId);
5324
+ async readImageAsBase64(imageId, expectedContextId) {
5325
+ const result = await this.getImagePath(imageId, expectedContextId);
5124
5326
  if (!result) return null;
5125
5327
  const buffer = await readFile11(result.filePath);
5126
5328
  return { base64: buffer.toString("base64"), mimeType: result.mimeType };
@@ -5148,8 +5350,8 @@ var ImageStorageService = class {
5148
5350
  var _singleton;
5149
5351
  function getSingleton() {
5150
5352
  if (!_singleton) {
5151
- const dataDir = process.env["DATA_DIR"] ?? ".runeya";
5152
- _singleton = new ImageStorageService(resolve3(dataDir, "images"));
5353
+ const dataDir = process.env["RUNEYA_DATA_DIR"] ?? ".runeya";
5354
+ _singleton = new ImageStorageService(resolve4(dataDir, "images"));
5153
5355
  }
5154
5356
  return _singleton;
5155
5357
  }
@@ -5186,10 +5388,10 @@ function mimeToExt(mime) {
5186
5388
  }
5187
5389
 
5188
5390
  // ../server/src/services/message-images.ts
5189
- async function resolveImagesToBase64(images) {
5391
+ async function resolveImagesToBase64(images, contextId) {
5190
5392
  const results = await Promise.all(
5191
5393
  images.map(async (img) => {
5192
- const resolved = await imageStorageService.readImageAsBase64(img.imageId);
5394
+ const resolved = await imageStorageService.readImageAsBase64(img.imageId, contextId);
5193
5395
  if (!resolved) {
5194
5396
  console.warn("[message-images] Image not found (imageId redacted from log)");
5195
5397
  return null;
@@ -5200,111 +5402,495 @@ async function resolveImagesToBase64(images) {
5200
5402
  return results.filter((r) => r !== null);
5201
5403
  }
5202
5404
 
5203
- // ../server/src/services/ai-runners/claude-code-runner.ts
5204
- import { spawn as spawn2 } from "child_process";
5205
-
5206
- // ../server/src/services/ai-runners/scenario-tokens.ts
5207
- function parseEndToken(output) {
5208
- const tail = output.trimEnd().slice(-2e3);
5209
- const branchMatch = tail.match(/<pass\s+branch="(\d+)"\s*\/>/);
5210
- if (branchMatch) {
5211
- return { status: "pass", message: "", branchChoice: Number.parseInt(branchMatch[1], 10) };
5212
- }
5213
- if (/<pass\s*\/>/.test(tail)) {
5214
- return { status: "pass", message: "" };
5215
- }
5216
- const errorMatch = tail.match(/<error>([\s\S]*?)<\/error>/);
5217
- if (errorMatch) {
5218
- return { status: "error", message: errorMatch[1].trim() };
5219
- }
5220
- const askMatch = tail.match(/<ask_user>([\s\S]*?)<\/ask_user>/);
5221
- if (askMatch) {
5222
- return { status: "ask_user", message: askMatch[1].trim() };
5223
- }
5224
- return { status: "none", message: "" };
5225
- }
5226
- var TOKEN_INSTRUCTIONS = `
5227
- ## Instructions de signalisation (OBLIGATOIRE)
5228
-
5229
- \xC0 la fin de CHAQUE r\xE9ponse, tu DOIS terminer par exactement l'un de ces tags XML :
5230
-
5231
- - Si tu as termin\xE9 avec succ\xE8s (chemin par d\xE9faut) : \`<pass />\`
5232
- - Si le n\u0153ud propose plusieurs branches de sortie num\xE9rot\xE9es : \`<pass branch="1" />\`, \`<pass branch="2" />\`, etc. (choisis la branche qui correspond le mieux au r\xE9sultat)
5233
- - Si tu as rencontr\xE9 une erreur bloquante : \`<error>description courte</error>\`
5234
- - Si tu es bloqu\xE9 par une information que personne dans l'\xE9quipe ne peut fournir (secret, d\xE9cision business, credential externe) : \`<ask_user>ta question</ask_user>\` \u2014 NE PAS utiliser pour demander une confirmation ou un accord ("es-tu pr\xEAt ?", "puis-je continuer ?")
5235
-
5236
- Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9ponse.
5237
- `.trim();
5238
-
5239
- // ../server/src/services/ai-runners/claude-pty-runner.ts
5405
+ // ../server/src/services/kanban-commands.ts
5240
5406
  import { randomUUID as randomUUID5 } from "crypto";
5241
5407
 
5242
- // ../server/src/services/ai-runners/claude-pty-tail.ts
5243
- import { open } from "fs/promises";
5244
- import { watch as watch2 } from "fs";
5245
- async function waitForFile(sessionId, signal, timeoutMs) {
5246
- const start = Date.now();
5247
- while (Date.now() - start < timeoutMs) {
5248
- if (signal.aborted) throw new Error("Aborted");
5249
- const path = await findClaudeSessionFile(sessionId);
5250
- if (path) return path;
5251
- await new Promise((r) => setTimeout(r, 150));
5408
+ // ../server/src/trpc/routers/cloud-auth.ts
5409
+ import { z as z14 } from "zod";
5410
+ import { TRPCError as TRPCError10 } from "@trpc/server";
5411
+
5412
+ // ../server/src/services/cloud-session-store.ts
5413
+ import { readFile as readFile12, writeFile as writeFile10, rename as rename9, mkdir as mkdir12, chmod as chmod8, unlink as unlink3 } from "fs/promises";
5414
+ import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "crypto";
5415
+ import { join as join14 } from "path";
5416
+ var FILENAME4 = "cloud-session.json";
5417
+ var STATE_TTL_MS = 5 * 60 * 1e3;
5418
+ var CloudSessionStore = class {
5419
+ session = null;
5420
+ loaded = false;
5421
+ /** Pending login states (CSRF binding between `start` and the callback). */
5422
+ pending = /* @__PURE__ */ new Map();
5423
+ getFilePath() {
5424
+ return join14(env.DATA_DIR, FILENAME4);
5252
5425
  }
5253
- throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);
5254
- }
5255
- function tailTranscript(opts) {
5256
- const { sessionId, signal, onEntry, fileAppearTimeoutMs = 3e4 } = opts;
5257
- let stopped = false;
5258
- let watcher = null;
5259
- let fh = null;
5260
- let offset = 0;
5261
- let pendingBuf = "";
5262
- let reading = false;
5263
- let queuedRead = false;
5264
- const cleanup = async () => {
5265
- stopped = true;
5266
- if (watcher) {
5267
- try {
5268
- watcher.close();
5269
- } catch {
5270
- }
5271
- watcher = null;
5272
- }
5273
- if (fh) {
5274
- try {
5275
- await fh.close();
5276
- } catch {
5277
- }
5278
- fh = null;
5426
+ async load() {
5427
+ if (this.loaded) return;
5428
+ try {
5429
+ const raw = await readFile12(this.getFilePath(), "utf-8");
5430
+ this.session = JSON.parse(raw);
5431
+ } catch {
5279
5432
  }
5280
- };
5281
- const drainBuffer = () => {
5282
- let idx;
5283
- while ((idx = pendingBuf.indexOf("\n")) >= 0) {
5284
- const line = pendingBuf.slice(0, idx).trim();
5285
- pendingBuf = pendingBuf.slice(idx + 1);
5286
- if (!line) continue;
5287
- try {
5288
- const entry = JSON.parse(line);
5289
- onEntry(entry);
5290
- } catch {
5433
+ this.loaded = true;
5434
+ }
5435
+ /**
5436
+ * Mint a state value binding a login attempt to this server. Returned to the
5437
+ * cloud page and echoed back on the callback, so a redirect we never
5438
+ * initiated cannot install a token.
5439
+ */
5440
+ createState(returnTo) {
5441
+ this.sweepStates();
5442
+ const state = randomBytes3(32).toString("hex");
5443
+ this.pending.set(state, { expiresAt: Date.now() + STATE_TTL_MS, returnTo });
5444
+ return state;
5445
+ }
5446
+ /**
5447
+ * Consume a state — single use, constant-time compare, TTL enforced. Returns
5448
+ * the URL to send the browser back to, or null when the state is unknown or
5449
+ * expired (i.e. a callback this server never initiated).
5450
+ */
5451
+ consumeState(candidate) {
5452
+ this.sweepStates();
5453
+ for (const [state, entry] of this.pending) {
5454
+ const a = Buffer.from(state);
5455
+ const b = Buffer.from(candidate);
5456
+ if (a.length === b.length && timingSafeEqual2(a, b)) {
5457
+ this.pending.delete(state);
5458
+ return entry.expiresAt > Date.now() ? entry.returnTo : null;
5291
5459
  }
5292
5460
  }
5293
- };
5294
- const readMore = async () => {
5295
- if (!fh || stopped) return;
5296
- if (reading) {
5297
- queuedRead = true;
5298
- return;
5461
+ return null;
5462
+ }
5463
+ sweepStates() {
5464
+ const now = Date.now();
5465
+ for (const [state, entry] of this.pending) {
5466
+ if (entry.expiresAt <= now) this.pending.delete(state);
5299
5467
  }
5300
- reading = true;
5468
+ }
5469
+ async save() {
5470
+ await mkdir12(env.DATA_DIR, { recursive: true });
5471
+ const filePath = this.getFilePath();
5472
+ const tmpPath = filePath + ".tmp";
5473
+ await writeFile10(tmpPath, JSON.stringify(this.session, null, 2), "utf-8");
5474
+ await rename9(tmpPath, filePath);
5475
+ await chmod8(filePath, 384);
5476
+ }
5477
+ /** Store the session token obtained from the one-time-token exchange. */
5478
+ async set(cloudUrl, token) {
5479
+ await this.load();
5480
+ this.session = { cloudUrl, encryptedToken: encryptValue(token) };
5481
+ await this.save();
5482
+ }
5483
+ /** The decrypted session token, or null when not logged in. */
5484
+ async getToken() {
5485
+ await this.load();
5486
+ if (!this.session) return null;
5301
5487
  try {
5302
- while (!stopped) {
5303
- const st = await fh.stat();
5304
- if (st.size <= offset) break;
5305
- const len = st.size - offset;
5306
- const buf = Buffer.alloc(len);
5307
- const { bytesRead } = await fh.read(buf, 0, len, offset);
5488
+ return decryptValue(this.session.encryptedToken);
5489
+ } catch {
5490
+ return null;
5491
+ }
5492
+ }
5493
+ async isLinked() {
5494
+ return await this.getToken() !== null;
5495
+ }
5496
+ /** Forget the session (sign-out). */
5497
+ async clear() {
5498
+ await this.load();
5499
+ this.session = null;
5500
+ this.pending.clear();
5501
+ try {
5502
+ await unlink3(this.getFilePath());
5503
+ } catch {
5504
+ }
5505
+ }
5506
+ };
5507
+ var cloudSessionStore = new CloudSessionStore();
5508
+
5509
+ // ../server/src/trpc/routers/cloud-auth.ts
5510
+ var DEFAULT_CLOUD_API_URL = "https://api.runeya.dev";
5511
+ var DEFAULT_CLOUD_APP_URL = "https://runeya.dev";
5512
+ function getCloudApiUrl() {
5513
+ return (env.CLOUD_URL ?? DEFAULT_CLOUD_API_URL).replace(/\/+$/, "");
5514
+ }
5515
+ function getCloudAppUrl() {
5516
+ return (env.CLOUD_APP_URL ?? DEFAULT_CLOUD_APP_URL).replace(/\/+$/, "");
5517
+ }
5518
+ function isLocalOrigin(url) {
5519
+ try {
5520
+ const { protocol, hostname } = new URL(url);
5521
+ return (protocol === "http:" || protocol === "https:") && (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]");
5522
+ } catch {
5523
+ return false;
5524
+ }
5525
+ }
5526
+ async function cloudFetch(path, init = {}) {
5527
+ const token = await cloudSessionStore.getToken();
5528
+ if (!token) {
5529
+ throw new TRPCError10({ code: "UNAUTHORIZED", message: "Not linked to the Runeya cloud." });
5530
+ }
5531
+ return fetch(`${getCloudApiUrl()}${path}`, {
5532
+ method: init.method ?? "GET",
5533
+ headers: {
5534
+ Authorization: `Bearer ${token}`,
5535
+ ...init.body !== void 0 ? { "Content-Type": "application/json" } : {}
5536
+ },
5537
+ ...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
5538
+ signal: AbortSignal.timeout(15e3)
5539
+ });
5540
+ }
5541
+ var PROXY_PREFIXES = ["/organization/", "/project/", "/chat/", "/kanban/", "/user/prefs"];
5542
+ function isAllowedProxyPath(path) {
5543
+ if (!path.startsWith("/") || path.includes("..")) return false;
5544
+ const [pathname] = path.split("?");
5545
+ return PROXY_PREFIXES.some((p) => pathname === p || pathname.startsWith(p));
5546
+ }
5547
+ var cloudAuthRouter = router({
5548
+ /** Build the cloud authorization URL to open in the browser. */
5549
+ startLogin: protectedProcedure.input(z14.object({ returnTo: z14.string().url().max(2048) })).mutation(async ({ input }) => {
5550
+ if (!isLocalOrigin(input.returnTo)) {
5551
+ throw new TRPCError10({ code: "BAD_REQUEST", message: "returnTo must be a local origin." });
5552
+ }
5553
+ const state = cloudSessionStore.createState(input.returnTo);
5554
+ const callback = `http://127.0.0.1:${env.PORT}/api/cloud/callback`;
5555
+ const url = `${getCloudAppUrl()}/link-local?callback=${encodeURIComponent(callback)}&state=${encodeURIComponent(state)}`;
5556
+ return { url };
5557
+ }),
5558
+ /** Whether this machine holds a cloud session. */
5559
+ status: protectedProcedure.query(async () => {
5560
+ return { linked: await cloudSessionStore.isLinked() };
5561
+ }),
5562
+ /** The cloud session (user + org), fetched with the stored token. */
5563
+ session: protectedProcedure.query(async () => {
5564
+ if (!await cloudSessionStore.isLinked()) return null;
5565
+ const res = await cloudFetch("/api/auth/get-session");
5566
+ if (!res.ok) return null;
5567
+ return await res.json();
5568
+ }),
5569
+ /**
5570
+ * Forward a cloud API call, authenticated with this machine's token.
5571
+ *
5572
+ * This is what replaces the browser's direct, cookie-authenticated calls to
5573
+ * the cloud: the web UI asks its own server, which holds the credential.
5574
+ */
5575
+ proxy: protectedProcedure.input(
5576
+ z14.object({
5577
+ path: z14.string().min(1).max(512),
5578
+ method: z14.enum(["GET", "POST"]).default("GET"),
5579
+ query: z14.record(z14.string().max(128), z14.string().max(2048)).optional(),
5580
+ body: z14.unknown().optional()
5581
+ })
5582
+ ).mutation(async ({ input }) => {
5583
+ if (!isAllowedProxyPath(input.path)) {
5584
+ throw new TRPCError10({ code: "FORBIDDEN", message: `Path not allowed: ${input.path}` });
5585
+ }
5586
+ const qs = input.query ? `?${new URLSearchParams(input.query).toString()}` : "";
5587
+ const res = await cloudFetch(`/api/auth${input.path}${qs}`, {
5588
+ method: input.method,
5589
+ ...input.method === "POST" ? { body: input.body ?? {} } : {}
5590
+ });
5591
+ const text = await res.text();
5592
+ let data = null;
5593
+ try {
5594
+ data = text ? JSON.parse(text) : null;
5595
+ } catch {
5596
+ data = text;
5597
+ }
5598
+ if (!res.ok) return { data: null, error: { status: res.status, ...data } };
5599
+ return { data, error: null };
5600
+ }),
5601
+ /** Drop the cloud session, both here and on the cloud. */
5602
+ signOut: protectedProcedure.mutation(async () => {
5603
+ if (await cloudSessionStore.isLinked()) {
5604
+ try {
5605
+ await cloudFetch("/api/auth/sign-out", { method: "POST", body: {} });
5606
+ } catch {
5607
+ }
5608
+ }
5609
+ await cloudSessionStore.clear();
5610
+ return { success: true };
5611
+ })
5612
+ });
5613
+
5614
+ // ../server/src/services/kanban-store.ts
5615
+ async function callCloud(path, init = {}) {
5616
+ try {
5617
+ const res = await cloudFetch(`/api/auth${path}`, init);
5618
+ if (!res.ok) {
5619
+ console.error("[kanban-store] cloud call failed: %s %s", res.status, path);
5620
+ return { ok: false, data: null };
5621
+ }
5622
+ return { ok: true, data: await res.json() };
5623
+ } catch (err) {
5624
+ console.error("[kanban-store] cloud call errored:", err);
5625
+ return { ok: false, data: null };
5626
+ }
5627
+ }
5628
+ async function resolveOrganizationId() {
5629
+ const session = await callCloud("/get-session");
5630
+ const active = session.data?.session?.activeOrganizationId;
5631
+ if (active) return active;
5632
+ const orgs = await callCloud("/organization/list");
5633
+ return orgs.data?.[0]?.id ?? null;
5634
+ }
5635
+ var KanbanStore = class {
5636
+ async list(includeArchived = false) {
5637
+ const organizationId = await resolveOrganizationId();
5638
+ if (!organizationId) return [];
5639
+ const query = `?organizationId=${encodeURIComponent(organizationId)}` + (includeArchived ? "&includeArchived=true" : "");
5640
+ const res = await callCloud(`/kanban/board/list${query}`);
5641
+ return res.data?.boards ?? [];
5642
+ }
5643
+ async get(id) {
5644
+ const res = await callCloud(`/kanban/board/get?id=${encodeURIComponent(id)}`);
5645
+ return res.data?.board ?? null;
5646
+ }
5647
+ async create(input) {
5648
+ const organizationId = await resolveOrganizationId();
5649
+ if (!organizationId) return null;
5650
+ const res = await callCloud("/kanban/board/create", {
5651
+ method: "POST",
5652
+ body: { organizationId, name: input.name, description: input.description ?? "" }
5653
+ });
5654
+ return res.data?.board ?? null;
5655
+ }
5656
+ async update(input) {
5657
+ const res = await callCloud("/kanban/board/update", {
5658
+ method: "POST",
5659
+ body: input
5660
+ });
5661
+ return res.data?.board ?? null;
5662
+ }
5663
+ async delete(id) {
5664
+ const res = await callCloud("/kanban/board/delete", {
5665
+ method: "POST",
5666
+ body: { id }
5667
+ });
5668
+ return res.data?.success ?? false;
5669
+ }
5670
+ };
5671
+ var kanbanStore = new KanbanStore();
5672
+
5673
+ // ../server/src/services/kanban-commands.ts
5674
+ function generateKanbanSystemPrompt(board, cardId) {
5675
+ let currentColTitle = "";
5676
+ let card = null;
5677
+ for (const col of board.columns) {
5678
+ const found = col.cards.find((c) => c.id === cardId);
5679
+ if (found) {
5680
+ currentColTitle = col.title;
5681
+ card = found;
5682
+ break;
5683
+ }
5684
+ }
5685
+ const sortedColumns = [...board.columns].sort((a, b) => a.order - b.order);
5686
+ const lines = [];
5687
+ lines.push("## Kanban Board Context");
5688
+ lines.push("");
5689
+ lines.push(`Tu travailles dans le contexte d'une carte Kanban.`);
5690
+ lines.push(`- **Board** : ${board.name}`);
5691
+ if (card) {
5692
+ lines.push(`- **Carte** : ${card.title}`);
5693
+ lines.push(`- **Colonne actuelle** : \`${currentColTitle}\``);
5694
+ }
5695
+ lines.push("");
5696
+ lines.push("### Colonnes disponibles (noms exacts \xE0 utiliser) :");
5697
+ for (const col of sortedColumns) {
5698
+ const cardCount = col.cards.length;
5699
+ const isCurrent = card && col.cards.some((c) => c.id === cardId);
5700
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})${isCurrent ? " \u2190 **colonne actuelle**" : ""}`);
5701
+ }
5702
+ lines.push("");
5703
+ if (card) {
5704
+ const comments = (card.history ?? []).filter((e) => e.type === "comment");
5705
+ if (comments.length > 0) {
5706
+ lines.push("### Commentaires existants sur cette carte :");
5707
+ lines.push("");
5708
+ for (const c of comments.slice(-20)) {
5709
+ const who = c.author === "ai" ? "AI" : "Utilisateur";
5710
+ const date = new Date(c.timestamp).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
5711
+ lines.push(`- **${who}** (${date}) : ${c.detail}`);
5712
+ }
5713
+ lines.push("");
5714
+ }
5715
+ }
5716
+ lines.push("### Outils Kanban disponibles");
5717
+ lines.push("");
5718
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
5719
+ if (card) lines.push(`- **cardId** (carte actuelle) : \`${card.id}\``);
5720
+ lines.push("");
5721
+ lines.push("Tu disposes des outils suivants pour interagir avec le board :");
5722
+ lines.push("- `kanban_move_card(boardId, cardId, targetColumn)` \u2014 D\xE9placer la carte vers une autre colonne");
5723
+ lines.push("- `kanban_add_comment(boardId, cardId, comment)` \u2014 Ajouter un commentaire sur la carte");
5724
+ lines.push("");
5725
+ lines.push("### OBLIGATOIRE \u2014 D\xE9cision Kanban");
5726
+ lines.push("");
5727
+ lines.push("\xC0 chaque r\xE9ponse, tu DOIS obligatoirement faire les deux actions suivantes :");
5728
+ lines.push("");
5729
+ lines.push("**1. \xC9valuer et d\xE9placer la carte si n\xE9cessaire** via `kanban_move_card` :");
5730
+ lines.push(" - **Au d\xE9but** si tu sais d\xE9j\xE0 que tu vas travailler activement (ex. tu passes en `In progress`).");
5731
+ lines.push(" - **\xC0 la fin** pour une d\xE9cision forte apr\xE8s analyse compl\xE8te (ex. tu conclus que c'est `Done`).");
5732
+ lines.push(" - Si tu ne fais qu'analyser ou poser des questions \u2192 **ne pas d\xE9placer**.");
5733
+ lines.push("");
5734
+ lines.push(" > \u26A0\uFE0F **CRITIQUE** : En fin de r\xE9ponse, tu DOIS TOUJOURS \xE9valuer si la colonne actuelle est encore correcte.");
5735
+ lines.push("");
5736
+ lines.push("**2. Appeler `kanban_add_comment`** r\xE9sumant ce que tu as fait ou constat\xE9 (**\xE0 la fin** de ta r\xE9ponse).");
5737
+ lines.push("");
5738
+ lines.push("> \u{1F6AB} **INTERDIT** : Terminer une r\xE9ponse sans appeler `kanban_add_comment`. C'est non n\xE9gociable.");
5739
+ lines.push("");
5740
+ return lines.join("\n");
5741
+ }
5742
+ function generateKanbanPopulatePrompt(board) {
5743
+ const lines = [];
5744
+ lines.push("## Kanban Board \u2014 Mode Populate");
5745
+ lines.push("");
5746
+ lines.push(`Tu es en mode **populate** sur le tableau Kanban **"${board.name}"**.`);
5747
+ lines.push(`Tu es un outil de gestion de projet. Quand l'utilisateur dit "cr\xE9er une card", "ajouter une t\xE2che", "cr\xE9er une carte", ou toute demande similaire, il parle EXCLUSIVEMENT d'une carte Kanban \xE0 ajouter au tableau \u2014 PAS d'un composant UI, PAS de code HTML ou CSS, PAS d'un composant React/Vue/Angular.`);
5748
+ lines.push("");
5749
+ lines.push("Ton UNIQUE r\xF4le est de cr\xE9er des cartes Kanban pertinentes en r\xE9ponse \xE0 la demande de l'utilisateur.");
5750
+ lines.push("Tu ne dois PAS r\xE9pondre \xE0 la question, ni donner d'explication, ni \xE9crire du code, ni montrer de composant.");
5751
+ lines.push("Tu dois UNIQUEMENT produire des tags XML `<kanban_add_card>` pour cr\xE9er des cartes dans le tableau.");
5752
+ if (board.description) {
5753
+ lines.push("");
5754
+ lines.push(`Description du board : ${board.description}`);
5755
+ }
5756
+ lines.push("");
5757
+ const sortedCols = [...board.columns].sort((a, b) => a.order - b.order);
5758
+ lines.push("### Colonnes existantes (noms exacts \xE0 utiliser) :");
5759
+ for (const col of sortedCols) {
5760
+ const cardCount = col.cards.length;
5761
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})`);
5762
+ for (const card of col.cards) {
5763
+ lines.push(` - ${card.title}`);
5764
+ }
5765
+ }
5766
+ lines.push("");
5767
+ lines.push("### Outil disponible");
5768
+ lines.push("");
5769
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
5770
+ lines.push("");
5771
+ lines.push("Pour chaque carte \xE0 cr\xE9er, appelle l'outil `kanban_add_card(boardId, column, title, content)` :");
5772
+ lines.push("- `column` : nom exact de la colonne cible");
5773
+ lines.push("- `title` : titre concis de la carte");
5774
+ lines.push("- `content` : contenu markdown d\xE9taill\xE9 et actionnable");
5775
+ lines.push("");
5776
+ lines.push("### R\xE8gles STRICTES");
5777
+ lines.push("");
5778
+ lines.push("1. **NE R\xC9PONDS PAS** \xE0 la question de l'utilisateur. Ne donne aucune explication, aucun texte libre.");
5779
+ lines.push("2. **Appelle uniquement** `kanban_add_card`, rien d'autre.");
5780
+ lines.push("3. Tu ne peux PAS cr\xE9er de nouvelles colonnes. Dispatch les cartes dans les colonnes existantes list\xE9es ci-dessus.");
5781
+ lines.push("4. Utilise les noms **exacts** des colonnes.");
5782
+ lines.push("5. Chaque carte doit avoir un titre concis et un contenu markdown d\xE9taill\xE9 et actionnable.");
5783
+ lines.push("6. D\xE9compose la demande de l'utilisateur en t\xE2ches concr\xE8tes, chacune \xE9tant une carte.");
5784
+ lines.push("7. Dispatch chaque carte dans la colonne la plus appropri\xE9e selon sa nature.");
5785
+ lines.push("");
5786
+ return lines.join("\n");
5787
+ }
5788
+
5789
+ // ../server/src/services/ai-runners/claude-code-runner.ts
5790
+ import { spawn as spawn2 } from "child_process";
5791
+
5792
+ // ../server/src/services/ai-runners/scenario-tokens.ts
5793
+ function parseEndToken(output) {
5794
+ const tail = output.trimEnd().slice(-2e3);
5795
+ const branchMatch = tail.match(/<pass\s+branch="(\d+)"\s*\/>/);
5796
+ if (branchMatch) {
5797
+ return { status: "pass", message: "", branchChoice: Number.parseInt(branchMatch[1], 10) };
5798
+ }
5799
+ if (/<pass\s*\/>/.test(tail)) {
5800
+ return { status: "pass", message: "" };
5801
+ }
5802
+ const errorMatch = tail.match(/<error>([\s\S]*?)<\/error>/);
5803
+ if (errorMatch) {
5804
+ return { status: "error", message: errorMatch[1].trim() };
5805
+ }
5806
+ const askMatch = tail.match(/<ask_user>([\s\S]*?)<\/ask_user>/);
5807
+ if (askMatch) {
5808
+ return { status: "ask_user", message: askMatch[1].trim() };
5809
+ }
5810
+ return { status: "none", message: "" };
5811
+ }
5812
+ var TOKEN_INSTRUCTIONS = `
5813
+ ## Instructions de signalisation (OBLIGATOIRE)
5814
+
5815
+ \xC0 la fin de CHAQUE r\xE9ponse, tu DOIS terminer par exactement l'un de ces tags XML :
5816
+
5817
+ - Si tu as termin\xE9 avec succ\xE8s (chemin par d\xE9faut) : \`<pass />\`
5818
+ - Si le n\u0153ud propose plusieurs branches de sortie num\xE9rot\xE9es : \`<pass branch="1" />\`, \`<pass branch="2" />\`, etc. (choisis la branche qui correspond le mieux au r\xE9sultat)
5819
+ - Si tu as rencontr\xE9 une erreur bloquante : \`<error>description courte</error>\`
5820
+ - Si tu es bloqu\xE9 par une information que personne dans l'\xE9quipe ne peut fournir (secret, d\xE9cision business, credential externe) : \`<ask_user>ta question</ask_user>\` \u2014 NE PAS utiliser pour demander une confirmation ou un accord ("es-tu pr\xEAt ?", "puis-je continuer ?")
5821
+
5822
+ Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9ponse.
5823
+ `.trim();
5824
+
5825
+ // ../server/src/services/ai-runners/claude-pty-runner.ts
5826
+ import { randomUUID as randomUUID6 } from "crypto";
5827
+
5828
+ // ../server/src/services/ai-runners/claude-pty-tail.ts
5829
+ import { open } from "fs/promises";
5830
+ import { watch as watch2 } from "fs";
5831
+ async function waitForFile(sessionId, signal, timeoutMs) {
5832
+ const start = Date.now();
5833
+ while (Date.now() - start < timeoutMs) {
5834
+ if (signal.aborted) throw new Error("Aborted");
5835
+ const path = await findClaudeSessionFile(sessionId);
5836
+ if (path) return path;
5837
+ await new Promise((r) => setTimeout(r, 150));
5838
+ }
5839
+ throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);
5840
+ }
5841
+ function tailTranscript(opts) {
5842
+ const { sessionId, signal, onEntry, fileAppearTimeoutMs = 3e4 } = opts;
5843
+ let stopped = false;
5844
+ let watcher = null;
5845
+ let fh = null;
5846
+ let offset = 0;
5847
+ let pendingBuf = "";
5848
+ let reading = false;
5849
+ let queuedRead = false;
5850
+ const cleanup = async () => {
5851
+ stopped = true;
5852
+ if (watcher) {
5853
+ try {
5854
+ watcher.close();
5855
+ } catch {
5856
+ }
5857
+ watcher = null;
5858
+ }
5859
+ if (fh) {
5860
+ try {
5861
+ await fh.close();
5862
+ } catch {
5863
+ }
5864
+ fh = null;
5865
+ }
5866
+ };
5867
+ const drainBuffer = () => {
5868
+ let idx;
5869
+ while ((idx = pendingBuf.indexOf("\n")) >= 0) {
5870
+ const line = pendingBuf.slice(0, idx).trim();
5871
+ pendingBuf = pendingBuf.slice(idx + 1);
5872
+ if (!line) continue;
5873
+ try {
5874
+ const entry = JSON.parse(line);
5875
+ onEntry(entry);
5876
+ } catch {
5877
+ }
5878
+ }
5879
+ };
5880
+ const readMore = async () => {
5881
+ if (!fh || stopped) return;
5882
+ if (reading) {
5883
+ queuedRead = true;
5884
+ return;
5885
+ }
5886
+ reading = true;
5887
+ try {
5888
+ while (!stopped) {
5889
+ const st = await fh.stat();
5890
+ if (st.size <= offset) break;
5891
+ const len = st.size - offset;
5892
+ const buf = Buffer.alloc(len);
5893
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
5308
5894
  if (bytesRead <= 0) break;
5309
5895
  offset += bytesRead;
5310
5896
  pendingBuf += buf.slice(0, bytesRead).toString("utf-8");
@@ -5337,12 +5923,12 @@ function tailTranscript(opts) {
5337
5923
  const poll = setInterval(() => {
5338
5924
  void readMore();
5339
5925
  }, 500);
5340
- await new Promise((resolve5) => {
5926
+ await new Promise((resolve6) => {
5341
5927
  const tick = setInterval(() => {
5342
5928
  if (stopped) {
5343
5929
  clearInterval(tick);
5344
5930
  clearInterval(poll);
5345
- resolve5();
5931
+ resolve6();
5346
5932
  }
5347
5933
  }, 100);
5348
5934
  });
@@ -5361,7 +5947,7 @@ function tailTranscript(opts) {
5361
5947
 
5362
5948
  // ../server/src/services/ai-runners/claude-pty-runner.ts
5363
5949
  async function getAgentManager() {
5364
- const { agentManager: agentManager2 } = await import("./agent-manager-QPZUYLJA.js");
5950
+ const { agentManager: agentManager2 } = await import("./agent-manager-2P4THUVA.js");
5365
5951
  return agentManager2;
5366
5952
  }
5367
5953
  function buildPtyArgs(params, sessionId, opts) {
@@ -5431,7 +6017,7 @@ function dispatchEntry(entry, state, cbs) {
5431
6017
  async function runClaudeViaPty(params) {
5432
6018
  const { agentId, signal, skipTokenInstructions } = params;
5433
6019
  if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
5434
- const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID5();
6020
+ const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID6();
5435
6021
  const args = buildPtyArgs(params, sessionId, { excludeMcp: true });
5436
6022
  const agentMgr = await getAgentManager();
5437
6023
  const displayArgs = args.map((a) => {
@@ -5440,7 +6026,7 @@ async function runClaudeViaPty(params) {
5440
6026
  return a;
5441
6027
  });
5442
6028
  params.onCommand?.(`claude ${displayArgs.join(" ")}`);
5443
- return new Promise((resolve5, reject) => {
6029
+ return new Promise((resolve6, reject) => {
5444
6030
  let settled = false;
5445
6031
  let spawnId = null;
5446
6032
  const state = { assistantText: "", tools: /* @__PURE__ */ new Map() };
@@ -5463,7 +6049,7 @@ async function runClaudeViaPty(params) {
5463
6049
  settled = true;
5464
6050
  cleanup();
5465
6051
  void killSpawn();
5466
- resolve5(result);
6052
+ resolve6(result);
5467
6053
  };
5468
6054
  const fail = (err) => {
5469
6055
  if (settled) return;
@@ -5536,10 +6122,6 @@ async function runClaudeViaPty(params) {
5536
6122
  }
5537
6123
 
5538
6124
  // ../server/src/services/ai-runners/claude-code-runner.ts
5539
- async function getAgentManager2() {
5540
- const { agentManager: agentManager2 } = await import("./agent-manager-QPZUYLJA.js");
5541
- return agentManager2;
5542
- }
5543
6125
  function createClaudeStreamState() {
5544
6126
  return {
5545
6127
  fullContent: "",
@@ -5762,116 +6344,16 @@ function processClaudeStdoutLines(lines, streamState, params, ctx) {
5762
6344
  }
5763
6345
  var ClaudeCodeRunner = class {
5764
6346
  run(params) {
5765
- const ptyEnabled = params.usePty ?? process.env["RUNEYA_CLAUDE_PTY"] === "1";
5766
- if (ptyEnabled && params.agentId && !(params.images && params.images.length > 0)) {
5767
- return runClaudeViaPty(params);
5768
- }
5769
6347
  if (params.agentId) {
5770
- return this.runViaAgent(params);
6348
+ return runClaudeViaPty(params);
5771
6349
  }
5772
6350
  return this.runLocal(params);
5773
6351
  }
5774
- // ── Run via agent (remote spawn, local parsing) ─────────────────────────
5775
- async runViaAgent(params) {
5776
- const { agentId, signal, skipTokenInstructions } = params;
5777
- const { args, env: env2, stdinData, sessionArg } = buildClaudeSpawnConfig(params, { excludeMcp: true });
5778
- const agentMgr = await getAgentManager2();
5779
- const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
5780
- console.log(`[claude-code-runner] [agent:${agentId}] arg size: ${totalArgSize} bytes, session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
5781
- const displayArgs = args.map((a) => {
5782
- if (a === params.prompt) return "<prompt>";
5783
- if (a.includes(TOKEN_INSTRUCTIONS)) return "<system-context>";
5784
- return a;
5785
- });
5786
- params.onCommand?.(`claude ${displayArgs.join(" ")}`);
5787
- return new Promise((resolve5, reject) => {
5788
- const streamState = createClaudeStreamState();
5789
- const ctx = { settled: false, earlyResult: null, lastResultError: "", rawStdoutLines: [] };
5790
- let stdoutBuffer = "";
5791
- let stderrBuffer = "";
5792
- let spawnId = null;
5793
- let unsubscribe = null;
5794
- const onAbort = () => {
5795
- if (spawnId && agentId) {
5796
- agentMgr.agentMutation(agentId, "ai.kill", { spawnId }).catch(() => {
5797
- });
5798
- }
5799
- };
5800
- signal.addEventListener("abort", onAbort, { once: true });
5801
- const cleanup = () => {
5802
- signal.removeEventListener("abort", onAbort);
5803
- if (unsubscribe) {
5804
- unsubscribe();
5805
- unsubscribe = null;
5806
- }
5807
- };
5808
- agentMgr.agentMutation(agentId, "ai.spawn", {
5809
- cli: "claude",
5810
- args,
5811
- env: env2,
5812
- stdinData,
5813
- enableMcp: !!params.mcpConfig,
5814
- mcpApiBaseUrl: params.apiBaseUrl || void 0
5815
- }).then(({ spawnId: id }) => {
5816
- spawnId = id;
5817
- if (signal.aborted) {
5818
- cleanup();
5819
- reject(new Error("Aborted"));
5820
- return;
5821
- }
5822
- unsubscribe = agentMgr.agentSubscribe(
5823
- agentId,
5824
- "ai.stream",
5825
- { spawnId, fromOffset: 0 },
5826
- (data) => {
5827
- const item = data;
5828
- const chunk = item.chunk;
5829
- if (chunk.type === "stdout") {
5830
- stdoutBuffer += chunk.data;
5831
- const lines = stdoutBuffer.split("\n");
5832
- stdoutBuffer = lines.pop() ?? "";
5833
- processClaudeStdoutLines(lines, streamState, params, ctx);
5834
- }
5835
- if (chunk.type === "stderr") {
5836
- stderrBuffer += chunk.data;
5837
- }
5838
- if (chunk.type === "exit") {
5839
- const code = chunk.code ?? 0;
5840
- cleanup();
5841
- if (ctx.earlyResult) {
5842
- resolve5({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
5843
- return;
5844
- }
5845
- if (signal.aborted) {
5846
- reject(new Error("Aborted"));
5847
- return;
5848
- }
5849
- if (code !== 0 && !streamState.fullContent) {
5850
- const stderr = stderrBuffer.trim().slice(-2e3);
5851
- const rawStdout = ctx.rawStdoutLines.slice(-10).join("\n").trim();
5852
- console.error(`[claude-code-runner] [agent] exit code ${code} | stderr: ${stderr || "(empty)"} | last stdout lines: ${rawStdout || "(empty)"}`);
5853
- const detail = ctx.lastResultError || stderr || rawStdout;
5854
- reject(new Error(`Claude Code exited with code ${code}${detail ? `: ${detail}` : ""}`));
5855
- return;
5856
- }
5857
- const { status, message, branchChoice } = skipTokenInstructions ? { status: "none", message: "", branchChoice: void 0 } : parseEndToken(streamState.fullContent);
5858
- resolve5({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });
5859
- }
5860
- },
5861
- () => {
5862
- }
5863
- );
5864
- }).catch((err) => {
5865
- cleanup();
5866
- reject(new Error(`Failed to spawn claude on agent: ${err.message}`));
5867
- });
5868
- });
5869
- }
5870
6352
  // ── Run locally (original implementation) ───────────────────────────────
5871
6353
  runLocal(params) {
5872
6354
  const { signal, skipTokenInstructions } = params;
5873
6355
  const { args, env: env2, stdinData, sessionArg, hasImages: _hasImages } = buildClaudeSpawnConfig(params);
5874
- return new Promise((resolve5, reject) => {
6356
+ return new Promise((resolve6, reject) => {
5875
6357
  const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
5876
6358
  console.log(`[claude-code-runner] arg size: ${totalArgSize} bytes (${(totalArgSize / 1024 / 1024).toFixed(2)} MB), session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
5877
6359
  const displayArgs = args.map((a) => {
@@ -5913,7 +6395,7 @@ var ClaudeCodeRunner = class {
5913
6395
  });
5914
6396
  proc.on("close", (code) => {
5915
6397
  if (ctx.earlyResult) {
5916
- resolve5({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
6398
+ resolve6({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
5917
6399
  return;
5918
6400
  }
5919
6401
  signal.removeEventListener("abort", onAbort);
@@ -5930,7 +6412,7 @@ var ClaudeCodeRunner = class {
5930
6412
  return;
5931
6413
  }
5932
6414
  const { status, message, branchChoice } = skipTokenInstructions ? { status: "none", message: "", branchChoice: void 0 } : parseEndToken(streamState.fullContent);
5933
- resolve5({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });
6415
+ resolve6({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });
5934
6416
  });
5935
6417
  proc.on("error", (err) => {
5936
6418
  if (ctx.settled) return;
@@ -5986,12 +6468,12 @@ var CodexAppServerClient = class extends EventEmitter3 {
5986
6468
  if (forceId !== void 0 && forceId >= this.nextId) {
5987
6469
  this.nextId = forceId + 1;
5988
6470
  }
5989
- return new Promise((resolve5, reject) => {
6471
+ return new Promise((resolve6, reject) => {
5990
6472
  const timer = setTimeout(() => {
5991
6473
  this.pending.delete(id);
5992
6474
  reject(new Error(`Request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`));
5993
6475
  }, this.requestTimeoutMs);
5994
- this.pending.set(id, { resolve: resolve5, reject, timer });
6476
+ this.pending.set(id, { resolve: resolve6, reject, timer });
5995
6477
  this.writeLine({ jsonrpc: "2.0", method, id, params: params ?? {} });
5996
6478
  });
5997
6479
  }
@@ -6023,8 +6505,8 @@ var CodexAppServerClient = class extends EventEmitter3 {
6023
6505
  this.rl = null;
6024
6506
  const proc = this.proc;
6025
6507
  this.proc = null;
6026
- return new Promise((resolve5) => {
6027
- proc.on("exit", () => resolve5());
6508
+ return new Promise((resolve6) => {
6509
+ proc.on("exit", () => resolve6());
6028
6510
  proc.kill("SIGTERM");
6029
6511
  const forceTimer = setTimeout(() => {
6030
6512
  proc.kill("SIGKILL");
@@ -6117,8 +6599,8 @@ async function shutdownCodexAppServer() {
6117
6599
  await client.shutdown();
6118
6600
  }
6119
6601
  }
6120
- async function getAgentManager3() {
6121
- const { agentManager: agentManager2 } = await import("./agent-manager-QPZUYLJA.js");
6602
+ async function getAgentManager2() {
6603
+ const { agentManager: agentManager2 } = await import("./agent-manager-2P4THUVA.js");
6122
6604
  return agentManager2;
6123
6605
  }
6124
6606
  function parseRuneyaCurlCommand(command) {
@@ -6187,7 +6669,7 @@ var CodexRunner = class {
6187
6669
  // ── Run via agent (app-server JSON-RPC via agent tRPC routes) ───────────
6188
6670
  async runViaAgent(params) {
6189
6671
  const { agentId, prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken, injectedEnv } = params;
6190
- const agentMgr = await getAgentManager3();
6672
+ const agentMgr = await getAgentManager2();
6191
6673
  if (isResume && params.sessionId && !this.lastThreadId) {
6192
6674
  this.lastThreadId = params.sessionId;
6193
6675
  }
@@ -6233,7 +6715,7 @@ ${prompt}`;
6233
6715
  }
6234
6716
  input.push({ type: "text", text: fullPrompt });
6235
6717
  const turnRef = { id: null };
6236
- return new Promise((resolve5, reject) => {
6718
+ return new Promise((resolve6, reject) => {
6237
6719
  let settled = false;
6238
6720
  let fullContent = "";
6239
6721
  let thinkingEmitted = false;
@@ -6282,7 +6764,7 @@ ${prompt}`;
6282
6764
  params.onDelta(delta);
6283
6765
  const { status, message, branchChoice } = parseEndToken(fullContent);
6284
6766
  if (status !== "none") {
6285
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6767
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6286
6768
  }
6287
6769
  }
6288
6770
  } else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
@@ -6356,7 +6838,7 @@ ${prompt}`;
6356
6838
  return;
6357
6839
  }
6358
6840
  const { status, message, branchChoice } = parseEndToken(fullContent);
6359
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6841
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6360
6842
  } else if (method === "turn/errored") {
6361
6843
  const error = p["error"];
6362
6844
  const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
@@ -6431,7 +6913,7 @@ ${prompt}`;
6431
6913
  sandboxPolicy: { type: "dangerFullAccess" }
6432
6914
  });
6433
6915
  const turnId = turnRes.turn.id;
6434
- return new Promise((resolve5, reject) => {
6916
+ return new Promise((resolve6, reject) => {
6435
6917
  let settled = false;
6436
6918
  let fullContent = "";
6437
6919
  let thinkingEmitted = false;
@@ -6457,7 +6939,7 @@ ${prompt}`;
6457
6939
  params.onDelta(delta);
6458
6940
  const { status, message, branchChoice } = parseEndToken(fullContent);
6459
6941
  if (status !== "none") {
6460
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6942
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6461
6943
  }
6462
6944
  }
6463
6945
  } else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
@@ -6531,7 +7013,7 @@ ${prompt}`;
6531
7013
  return;
6532
7014
  }
6533
7015
  const { status, message, branchChoice } = parseEndToken(fullContent);
6534
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
7016
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6535
7017
  } else if (method === "turn/errored") {
6536
7018
  const error = p["error"];
6537
7019
  const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
@@ -6552,18 +7034,18 @@ ${prompt}`;
6552
7034
 
6553
7035
  // ../server/src/trpc/routers/chat.ts
6554
7036
  var execFileAsync2 = promisify2(execFile3);
6555
- var MessageSchema = z14.object({
6556
- role: z14.enum(["user", "assistant"]),
6557
- content: z14.string().max(1e5)
7037
+ var MessageSchema = z15.object({
7038
+ role: z15.enum(["user", "assistant"]),
7039
+ content: z15.string().max(1e5)
6558
7040
  });
6559
- var ImagePartSchema = z14.object({
6560
- type: z14.literal("image"),
6561
- imageId: z14.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
6562
- url: z14.string().max(500),
6563
- mimeType: z14.string().max(100),
6564
- altText: z14.string().max(500).optional()
7041
+ var ImagePartSchema = z15.object({
7042
+ type: z15.literal("image"),
7043
+ imageId: z15.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
7044
+ url: z15.string().max(500),
7045
+ mimeType: z15.string().max(100),
7046
+ altText: z15.string().max(500).optional()
6565
7047
  });
6566
- var ImagePartsSchema = z14.array(ImagePartSchema).max(5).optional();
7048
+ var ImagePartsSchema = z15.array(ImagePartSchema).max(5).optional();
6567
7049
  var LANGUAGE_INSTRUCTION = "Always respond in the same language the user writes in.";
6568
7050
  async function buildSystemPrompt(apiKey) {
6569
7051
  const allServices = await serviceStore.list();
@@ -6720,7 +7202,7 @@ async function* runOpenAI(client, model, initialMessages, apiKey, authToken, sig
6720
7202
  yield { type: "done", text: "" };
6721
7203
  }
6722
7204
  async function* drainSession(session, signal, isReconnect) {
6723
- if (process.env.DEBUG) console.log("[chat]: drainSession");
7205
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: drainSession");
6724
7206
  if (isReconnect) {
6725
7207
  for (const part of session.parts) {
6726
7208
  if (part.type === "thinking") {
@@ -6873,7 +7355,7 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
6873
7355
  if (result) capturedFinalResult = result;
6874
7356
  }
6875
7357
  }).then(() => {
6876
- if (process.env.DEBUG) console.log("[chat]: claude end...");
7358
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: claude end...");
6877
7359
  if (activeSessionRegistry.get(convId) !== session) return;
6878
7360
  if (capturedFinalResult && capturedFinalResult.length > session.fullContent.length) {
6879
7361
  const missing = capturedFinalResult.slice(session.fullContent.length);
@@ -6882,7 +7364,7 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
6882
7364
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
6883
7365
  activeSessionRegistry.markDone(convId);
6884
7366
  }).catch((err) => {
6885
- if (process.env.DEBUG) console.error("[chat]: error...", err?.message, err);
7367
+ if (process.env.RUNEYA_DEBUG) console.error("[chat]: error...", err?.message, err);
6886
7368
  if (activeSessionRegistry.get(convId) !== session) return;
6887
7369
  activeSessionRegistry.pushEvent(convId, { type: "error", message: `Claude Code error: ${err.message}` });
6888
7370
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
@@ -6944,24 +7426,24 @@ async function startCodexSession(convId, prompt, opts = {}) {
6944
7426
  });
6945
7427
  return { session, done };
6946
7428
  }
6947
- var MessagePartSchema = z14.discriminatedUnion("type", [
6948
- z14.object({ type: z14.literal("thinking"), text: z14.string().max(1e5) }),
6949
- z14.object({ type: z14.literal("text"), text: z14.string().max(1e5) }),
6950
- z14.object({
6951
- type: z14.literal("tool"),
7429
+ var MessagePartSchema = z15.discriminatedUnion("type", [
7430
+ z15.object({ type: z15.literal("thinking"), text: z15.string().max(1e5) }),
7431
+ z15.object({ type: z15.literal("text"), text: z15.string().max(1e5) }),
7432
+ z15.object({
7433
+ type: z15.literal("tool"),
6952
7434
  id: Id128Schema,
6953
7435
  name: Id128Schema,
6954
- input: z14.record(z14.string(), z14.unknown()).optional(),
6955
- rawInput: z14.string().max(1e4).optional(),
6956
- result: z14.unknown().optional(),
6957
- isError: z14.boolean().optional(),
6958
- pending: z14.boolean()
7436
+ input: z15.record(z15.string(), z15.unknown()).optional(),
7437
+ rawInput: z15.string().max(1e4).optional(),
7438
+ result: z15.unknown().optional(),
7439
+ isError: z15.boolean().optional(),
7440
+ pending: z15.boolean()
6959
7441
  })
6960
7442
  ]);
6961
- var StoredMessageSchema = z14.object({
6962
- role: z14.enum(["user", "assistant"]),
6963
- content: z14.string().max(1e5),
6964
- parts: z14.array(MessagePartSchema).max(200).optional()
7443
+ var StoredMessageSchema = z15.object({
7444
+ role: z15.enum(["user", "assistant"]),
7445
+ content: z15.string().max(1e5),
7446
+ parts: z15.array(MessagePartSchema).max(200).optional()
6965
7447
  });
6966
7448
  var chatRouter = router({
6967
7449
  conversations: router({
@@ -6969,13 +7451,13 @@ var chatRouter = router({
6969
7451
  const initial = await conversationStore.list();
6970
7452
  yield { type: "initial", conversations: initial };
6971
7453
  const queue = [];
6972
- let resolve5 = null;
7454
+ let resolve6 = null;
6973
7455
  const handler = (event) => {
6974
7456
  if (queue.length >= 500) queue.shift();
6975
7457
  queue.push(event);
6976
- if (resolve5) {
6977
- resolve5();
6978
- resolve5 = null;
7458
+ if (resolve6) {
7459
+ resolve6();
7460
+ resolve6 = null;
6979
7461
  }
6980
7462
  };
6981
7463
  conversationStore.onChanged(handler);
@@ -6985,7 +7467,7 @@ var chatRouter = router({
6985
7467
  yield queue.shift();
6986
7468
  } else {
6987
7469
  await new Promise((r) => {
6988
- resolve5 = r;
7470
+ resolve6 = r;
6989
7471
  });
6990
7472
  }
6991
7473
  }
@@ -6994,25 +7476,25 @@ var chatRouter = router({
6994
7476
  }
6995
7477
  }),
6996
7478
  list: protectedProcedure.query(() => conversationStore.list()),
6997
- get: protectedProcedure.input(z14.object({ id: Id128Schema })).query(async ({ input }) => {
7479
+ get: protectedProcedure.input(z15.object({ id: Id128Schema })).query(async ({ input }) => {
6998
7480
  const conv = await conversationStore.get(input.id);
6999
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7481
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7000
7482
  return conv;
7001
7483
  }),
7002
- create: protectedProcedure.input(z14.object({
7484
+ create: protectedProcedure.input(z15.object({
7003
7485
  title: ShortString200Schema.optional(),
7004
- isCli: z14.boolean().optional(),
7005
- runner: z14.enum(["claude-code", "codex"]).optional(),
7486
+ isCli: z15.boolean().optional(),
7487
+ runner: z15.enum(["claude-code", "codex"]).optional(),
7006
7488
  providerId: Id128Schema.optional(),
7007
7489
  scenarioId: Id128Schema.optional(),
7008
- scenarioInput: z14.string().max(1e5).optional(),
7490
+ scenarioInput: z15.string().max(1e5).optional(),
7009
7491
  kanbanBoardId: Id128Schema.optional(),
7010
7492
  kanbanCardId: Id128Schema.optional()
7011
7493
  })).mutation(async ({ input }) => {
7012
7494
  let scenario = null;
7013
7495
  if (input.scenarioId) {
7014
7496
  scenario = await scenarioStore.get(input.scenarioId);
7015
- if (!scenario) throw new TRPCError10({ code: "NOT_FOUND", message: "Scenario not found" });
7497
+ if (!scenario) throw new TRPCError11({ code: "NOT_FOUND", message: "Scenario not found" });
7016
7498
  }
7017
7499
  const conv = await conversationStore.create(
7018
7500
  input.title,
@@ -7033,22 +7515,22 @@ var chatRouter = router({
7033
7515
  }
7034
7516
  return conv;
7035
7517
  }),
7036
- update: protectedProcedure.input(z14.object({
7518
+ update: protectedProcedure.input(z15.object({
7037
7519
  id: Id128Schema,
7038
7520
  title: ShortString200Schema.optional(),
7039
- messages: z14.array(StoredMessageSchema).max(500).optional(),
7040
- contextUsage: z14.object({
7041
- inputTokens: z14.number().int().nonnegative(),
7042
- contextWindow: z14.number().int().positive()
7521
+ messages: z15.array(StoredMessageSchema).max(500).optional(),
7522
+ contextUsage: z15.object({
7523
+ inputTokens: z15.number().int().nonnegative(),
7524
+ contextWindow: z15.number().int().positive()
7043
7525
  }).optional()
7044
7526
  })).mutation(async ({ input }) => {
7045
7527
  const conv = await conversationStore.update(input.id, { title: input.title, messages: input.messages, contextUsage: input.contextUsage });
7046
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7528
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7047
7529
  return conv;
7048
7530
  }),
7049
- delete: protectedProcedure.input(z14.object({ id: Id128Schema })).mutation(async ({ input }) => {
7531
+ delete: protectedProcedure.input(z15.object({ id: Id128Schema })).mutation(async ({ input }) => {
7050
7532
  const existing = await conversationStore.get(input.id);
7051
- if (!existing) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7533
+ if (!existing) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7052
7534
  for (const agent of agentManager.listAgents()) {
7053
7535
  const agentId = agent.config.id;
7054
7536
  agentManager.agentQuery(
@@ -7068,13 +7550,13 @@ var chatRouter = router({
7068
7550
  (err) => console.error("[chat.delete] Image cleanup error:", err)
7069
7551
  );
7070
7552
  }),
7071
- archive: protectedProcedure.input(z14.object({ id: Id128Schema, archived: z14.boolean() })).mutation(async ({ input }) => {
7553
+ archive: protectedProcedure.input(z15.object({ id: Id128Schema, archived: z15.boolean() })).mutation(async ({ input }) => {
7072
7554
  const conv = await conversationStore.update(input.id, { archived: input.archived });
7073
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7555
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7074
7556
  return conv;
7075
7557
  })
7076
7558
  }),
7077
- listModels: protectedProcedure.input(z14.object({ providerId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7559
+ listModels: protectedProcedure.input(z15.object({ providerId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7078
7560
  const active = await getActiveProvider(input?.providerId);
7079
7561
  if (!active) return [];
7080
7562
  try {
@@ -7083,7 +7565,7 @@ var chatRouter = router({
7083
7565
  return [];
7084
7566
  }
7085
7567
  }),
7086
- checkCliAvailability: protectedProcedure.input(z14.object({ agentId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7568
+ checkCliAvailability: protectedProcedure.input(z15.object({ agentId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7087
7569
  if (input?.agentId) {
7088
7570
  try {
7089
7571
  const res = await agentManager.agentQuery(
@@ -7101,9 +7583,13 @@ var chatRouter = router({
7101
7583
  ]);
7102
7584
  return { available: claudeOk, codexAvailable: codexOk };
7103
7585
  }),
7104
- enhanceScenarioPrompt: protectedProcedure.input(z14.object({ text: z14.string().max(1e4), providerId: Id128Schema.optional() })).mutation(async ({ input }) => {
7586
+ enhanceScenarioPrompt: protectedProcedure.input(z15.object({
7587
+ text: z15.string().max(1e4),
7588
+ providerId: Id128Schema.optional(),
7589
+ model: z15.string().max(200).optional()
7590
+ })).mutation(async ({ input }) => {
7105
7591
  const active = await getActiveProvider(input.providerId);
7106
- if (!active) throw new TRPCError10({ code: "BAD_REQUEST", message: "No AI provider configured" });
7592
+ if (!active) throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
7107
7593
  const systemPrompt = `You are an expert at writing clear, concise user request briefs for AI-powered workflows.
7108
7594
  When given a rough user request, rewrite it to be:
7109
7595
  - Clear and well-structured
@@ -7112,7 +7598,7 @@ When given a rough user request, rewrite it to be:
7112
7598
  - Concise but complete (no fluff)
7113
7599
  Return ONLY the improved text, no explanation, no preamble.`;
7114
7600
  const { provider, plaintextKey } = active;
7115
- const model = provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7601
+ const model = input.model || provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7116
7602
  if (provider.provider === "anthropic") {
7117
7603
  const client = new Anthropic2({ apiKey: plaintextKey });
7118
7604
  const res = await client.messages.create({
@@ -7154,39 +7640,39 @@ Return ONLY the improved text, no explanation, no preamble.`;
7154
7640
  }
7155
7641
  }),
7156
7642
  getStreamingStatus: protectedProcedure.query(() => activeSessionRegistry.listActive()),
7157
- stopSession: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
7158
- if (process.env.DEBUG) console.log("[chat]: stopSession");
7643
+ stopSession: protectedProcedure.input(z15.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
7644
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: stopSession");
7159
7645
  const killed = activeSessionRegistry.kill(input.conversationId);
7160
- if (!killed) throw new TRPCError10({ code: "NOT_FOUND", message: "No active session for this conversation" });
7646
+ if (!killed) throw new TRPCError11({ code: "NOT_FOUND", message: "No active session for this conversation" });
7161
7647
  return { ok: true };
7162
7648
  }),
7163
- streamViaClaudeCode: protectedProcedure.input(z14.object({
7649
+ streamViaClaudeCode: protectedProcedure.input(z15.object({
7164
7650
  conversationId: ConversationIdSchema,
7165
- prompt: z14.string().max(1e5),
7166
- contextNote: z14.string().max(2e5).optional(),
7651
+ prompt: z15.string().max(1e5),
7652
+ contextNote: z15.string().max(2e5).optional(),
7167
7653
  model: ShortString200Schema.optional(),
7168
- effort: z14.enum(["low", "medium", "high"]).optional(),
7169
- reconnect: z14.boolean().optional(),
7654
+ effort: z15.enum(["low", "medium", "high"]).optional(),
7655
+ reconnect: z15.boolean().optional(),
7170
7656
  agentId: Id128Schema.optional(),
7171
- environmentId: z14.string().max(128).optional(),
7172
- imageIds: z14.array(z14.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7657
+ environmentId: z15.string().max(128).optional(),
7658
+ imageIds: z15.array(z15.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7173
7659
  imageParts: ImagePartsSchema
7174
7660
  })).subscription(async function* ({ input, ctx, signal }) {
7175
- if (process.env.DEBUG) console.log("[chat]: Reconnect");
7661
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: Reconnect");
7176
7662
  const existingSession = activeSessionRegistry.get(input.conversationId);
7177
7663
  if (input.reconnect && existingSession) {
7178
7664
  yield* drainSession(existingSession, signal, true);
7179
7665
  return;
7180
7666
  }
7181
- if (!input.prompt) throw new TRPCError10({ code: "BAD_REQUEST", message: "Prompt is required for new messages" });
7667
+ if (!input.prompt) throw new TRPCError11({ code: "BAD_REQUEST", message: "Prompt is required for new messages" });
7182
7668
  const conv = await conversationStore.get(input.conversationId);
7183
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7669
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7184
7670
  console.log(`[kanban-debug] streamViaClaudeCode start \u2014 convId=${conv.id} kanbanBoardId=${conv.kanbanBoardId ?? "null"}`);
7185
7671
  const isResume = conv.messages.length > 0;
7186
7672
  const userParts = input.imageParts && input.imageParts.length > 0 ? [...input.prompt.trim() ? [{ type: "text", text: input.prompt }] : [], ...input.imageParts] : void 0;
7187
7673
  conv.messages.push({ role: "user", content: input.prompt, parts: userParts, contextNote: input.contextNote });
7188
7674
  conv.messages.push({ role: "assistant", content: "" });
7189
- if (process.env.DEBUG) console.log("[chat]: update");
7675
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: update");
7190
7676
  conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => {
7191
7677
  });
7192
7678
  const cliPrompt = input.contextNote ? `${input.contextNote}
@@ -7215,7 +7701,7 @@ ${input.prompt}` : input.prompt;
7215
7701
  const imagePaths = [];
7216
7702
  if (input.imageIds && input.imageIds.length > 0) {
7217
7703
  for (const id of input.imageIds) {
7218
- const result = await imageStorageService.getImagePath(id);
7704
+ const result = await imageStorageService.getImagePath(id, input.conversationId);
7219
7705
  if (result) imagePaths.push(result.filePath);
7220
7706
  }
7221
7707
  }
@@ -7228,14 +7714,14 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7228
7714
  try {
7229
7715
  session = activeSessionRegistry.registerProviderApiSession(convId, ac);
7230
7716
  } catch {
7231
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7717
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7232
7718
  }
7233
7719
  let capturedFinalResult = "";
7234
7720
  const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();
7235
7721
  if (resolvedAgentId) {
7236
7722
  session.agentId = resolvedAgentId;
7237
7723
  }
7238
- if (process.env.DEBUG) console.log("[chat]: claude run...");
7724
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: claude run...");
7239
7725
  const injectedEnv = await resolveInjectedEnv(input.environmentId);
7240
7726
  new ClaudeCodeRunner().run({
7241
7727
  agentId: resolvedAgentId ?? void 0,
@@ -7276,18 +7762,18 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7276
7762
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
7277
7763
  activeSessionRegistry.markDone(convId);
7278
7764
  });
7279
- if (process.env.DEBUG) console.log("[chat]: drainSession...");
7765
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: drainSession...");
7280
7766
  yield* drainSession(session, signal, false);
7281
7767
  }),
7282
- streamViaCodex: protectedProcedure.input(z14.object({
7768
+ streamViaCodex: protectedProcedure.input(z15.object({
7283
7769
  conversationId: ConversationIdSchema,
7284
- prompt: z14.string().max(1e5),
7285
- contextNote: z14.string().max(2e5).optional(),
7770
+ prompt: z15.string().max(1e5),
7771
+ contextNote: z15.string().max(2e5).optional(),
7286
7772
  model: ShortString200Schema.optional(),
7287
- reconnect: z14.boolean().optional(),
7773
+ reconnect: z15.boolean().optional(),
7288
7774
  agentId: Id128Schema.optional(),
7289
- environmentId: z14.string().max(128).optional(),
7290
- imageIds: z14.array(z14.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7775
+ environmentId: z15.string().max(128).optional(),
7776
+ imageIds: z15.array(z15.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7291
7777
  imageParts: ImagePartsSchema
7292
7778
  })).subscription(async function* ({ input, ctx, signal }) {
7293
7779
  const existingSession = activeSessionRegistry.get(input.conversationId);
@@ -7295,9 +7781,9 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7295
7781
  yield* drainSession(existingSession, signal, true);
7296
7782
  return;
7297
7783
  }
7298
- if (!input.prompt) throw new TRPCError10({ code: "BAD_REQUEST", message: "Prompt is required" });
7784
+ if (!input.prompt) throw new TRPCError11({ code: "BAD_REQUEST", message: "Prompt is required" });
7299
7785
  const conv = await conversationStore.get(input.conversationId);
7300
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7786
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7301
7787
  const isResume = !!conv.codexThreadId;
7302
7788
  const codexUserParts = input.imageParts && input.imageParts.length > 0 ? [...input.prompt.trim() ? [{ type: "text", text: input.prompt }] : [], ...input.imageParts] : void 0;
7303
7789
  conv.messages.push({ role: "user", content: input.prompt, parts: codexUserParts, contextNote: input.contextNote });
@@ -7317,14 +7803,14 @@ ${input.prompt}` : input.prompt;
7317
7803
  systemContext += "\n\n" + (conv.kanbanCardId ? generateKanbanSystemPrompt(kanbanBoard, conv.kanbanCardId) : generateKanbanPopulatePrompt(kanbanBoard));
7318
7804
  }
7319
7805
  }
7320
- const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" }))) : [];
7806
+ const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" })), input.conversationId) : [];
7321
7807
  const convId = input.conversationId;
7322
7808
  const ac = new AbortController();
7323
7809
  let session;
7324
7810
  try {
7325
7811
  session = activeSessionRegistry.registerProviderApiSession(convId, ac);
7326
7812
  } catch {
7327
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7813
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7328
7814
  }
7329
7815
  const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();
7330
7816
  if (resolvedAgentId) {
@@ -7364,13 +7850,13 @@ ${input.prompt}` : input.prompt;
7364
7850
  });
7365
7851
  yield* drainSession(session, signal, false);
7366
7852
  }),
7367
- send: protectedProcedure.input(z14.object({
7853
+ send: protectedProcedure.input(z15.object({
7368
7854
  conversationId: ConversationIdSchema,
7369
- messages: z14.array(MessageSchema).min(0).max(100),
7855
+ messages: z15.array(MessageSchema).min(0).max(100),
7370
7856
  model: ShortString200Schema.optional(),
7371
- contextNote: z14.string().max(2e5).optional(),
7372
- reconnect: z14.boolean().optional(),
7373
- imageIds: z14.array(z14.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7857
+ contextNote: z15.string().max(2e5).optional(),
7858
+ reconnect: z15.boolean().optional(),
7859
+ imageIds: z15.array(z15.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),
7374
7860
  imageParts: ImagePartsSchema
7375
7861
  })).subscription(async function* ({ input, ctx, signal }) {
7376
7862
  const existingSession = activeSessionRegistry.get(input.conversationId);
@@ -7379,12 +7865,12 @@ ${input.prompt}` : input.prompt;
7379
7865
  return;
7380
7866
  }
7381
7867
  if (input.messages.length === 0) {
7382
- throw new TRPCError10({ code: "BAD_REQUEST", message: "Messages are required for new messages" });
7868
+ throw new TRPCError11({ code: "BAD_REQUEST", message: "Messages are required for new messages" });
7383
7869
  }
7384
7870
  const conv = await conversationStore.get(input.conversationId);
7385
7871
  const active = await getActiveProvider(conv?.providerId);
7386
7872
  if (!active) {
7387
- throw new TRPCError10({
7873
+ throw new TRPCError11({
7388
7874
  code: "PRECONDITION_FAILED",
7389
7875
  message: "No AI provider configured for this conversation. Please select one when starting a new chat."
7390
7876
  });
@@ -7410,12 +7896,12 @@ ${input.prompt}` : input.prompt;
7410
7896
  system += "\n\n" + (conv.kanbanCardId ? generateKanbanSystemPrompt(kanbanBoard, conv.kanbanCardId) : generateKanbanPopulatePrompt(kanbanBoard));
7411
7897
  }
7412
7898
  }
7413
- const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" }))) : [];
7899
+ const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" })), input.conversationId) : [];
7414
7900
  let session;
7415
7901
  try {
7416
7902
  session = activeSessionRegistry.registerDirectProviderApiSession(input.conversationId);
7417
7903
  } catch {
7418
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7904
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7419
7905
  }
7420
7906
  const apiSignal = session.abortController.signal;
7421
7907
  const runStream = async () => {
@@ -7487,25 +7973,25 @@ ${input.prompt}` : input.prompt;
7487
7973
  // ── PTY passthrough (interactive Claude Code session) ────────────────────
7488
7974
  pty: router({
7489
7975
  /** Start a PTY session running `claude --resume <sessionId>` (or fresh). */
7490
- start: protectedProcedure.input(z14.object({
7491
- agentId: z14.string().max(128).optional(),
7492
- sessionId: z14.string().max(128).optional(),
7493
- runner: z14.enum(["claude-code", "codex"]).default("claude-code"),
7494
- resume: z14.boolean().default(false),
7495
- model: z14.string().max(128).optional(),
7496
- cwd: z14.string().max(4096).optional(),
7497
- cols: z14.number().int().min(1).max(1e3).default(120),
7498
- rows: z14.number().int().min(1).max(1e3).default(30),
7499
- enableMcp: z14.boolean().default(false),
7500
- apiBaseUrl: z14.string().max(512).optional(),
7501
- environmentId: z14.string().max(128).optional(),
7502
- attachedContexts: z14.array(z14.object({
7503
- label: z14.string().max(200),
7504
- text: z14.string().max(1e6)
7976
+ start: protectedProcedure.input(z15.object({
7977
+ agentId: z15.string().max(128).optional(),
7978
+ sessionId: z15.string().max(128).optional(),
7979
+ runner: z15.enum(["claude-code", "codex"]).default("claude-code"),
7980
+ resume: z15.boolean().default(false),
7981
+ model: z15.string().max(128).optional(),
7982
+ cwd: z15.string().max(4096).optional(),
7983
+ cols: z15.number().int().min(1).max(1e3).default(120),
7984
+ rows: z15.number().int().min(1).max(1e3).default(30),
7985
+ enableMcp: z15.boolean().default(false),
7986
+ apiBaseUrl: z15.string().max(512).optional(),
7987
+ environmentId: z15.string().max(128).optional(),
7988
+ attachedContexts: z15.array(z15.object({
7989
+ label: z15.string().max(200),
7990
+ text: z15.string().max(1e6)
7505
7991
  })).max(20).optional()
7506
7992
  })).mutation(async ({ input, ctx }) => {
7507
7993
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7508
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7994
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7509
7995
  if (input.sessionId) {
7510
7996
  try {
7511
7997
  const existing = await agentManager.agentQuery(
@@ -7641,15 +8127,15 @@ Context:
7641
8127
  return { spawnId: res.spawnId, agentId, reattached: false };
7642
8128
  }),
7643
8129
  /** Subscribe to PTY output stream (raw text with ANSI). */
7644
- stream: protectedProcedure.input(z14.object({
7645
- agentId: z14.string().max(128).optional(),
7646
- spawnId: z14.string().max(128),
7647
- fromOffset: z14.number().int().min(0).default(0)
8130
+ stream: protectedProcedure.input(z15.object({
8131
+ agentId: z15.string().max(128).optional(),
8132
+ spawnId: z15.string().max(128),
8133
+ fromOffset: z15.number().int().min(0).default(0)
7648
8134
  })).subscription(async function* ({ input, signal }) {
7649
8135
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7650
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8136
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7651
8137
  const queue = [];
7652
- let resolve5 = null;
8138
+ let resolve6 = null;
7653
8139
  let stopped = false;
7654
8140
  const unsub = agentManager.agentSubscribe(
7655
8141
  agentId,
@@ -7657,16 +8143,16 @@ Context:
7657
8143
  { spawnId: input.spawnId, fromOffset: input.fromOffset },
7658
8144
  (data) => {
7659
8145
  queue.push(data);
7660
- if (resolve5) {
7661
- resolve5();
7662
- resolve5 = null;
8146
+ if (resolve6) {
8147
+ resolve6();
8148
+ resolve6 = null;
7663
8149
  }
7664
8150
  },
7665
8151
  () => {
7666
8152
  stopped = true;
7667
- if (resolve5) {
7668
- resolve5();
7669
- resolve5 = null;
8153
+ if (resolve6) {
8154
+ resolve6();
8155
+ resolve6 = null;
7670
8156
  }
7671
8157
  }
7672
8158
  );
@@ -7682,7 +8168,7 @@ Context:
7682
8168
  }
7683
8169
  if (stopped) break;
7684
8170
  await new Promise((r) => {
7685
- resolve5 = r;
8171
+ resolve6 = r;
7686
8172
  });
7687
8173
  }
7688
8174
  } finally {
@@ -7690,27 +8176,27 @@ Context:
7690
8176
  }
7691
8177
  }),
7692
8178
  /** Write input to a PTY session (keystrokes from xterm). */
7693
- write: protectedProcedure.input(z14.object({
7694
- agentId: z14.string().max(128).optional(),
7695
- spawnId: z14.string().max(128),
7696
- data: z14.string().max(1e6)
8179
+ write: protectedProcedure.input(z15.object({
8180
+ agentId: z15.string().max(128).optional(),
8181
+ spawnId: z15.string().max(128),
8182
+ data: z15.string().max(1e6)
7697
8183
  })).mutation(async ({ input }) => {
7698
8184
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7699
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8185
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7700
8186
  return agentManager.agentMutation(agentId, "ai.writePty", {
7701
8187
  spawnId: input.spawnId,
7702
8188
  data: input.data
7703
8189
  });
7704
8190
  }),
7705
8191
  /** Resize a PTY session. */
7706
- resize: protectedProcedure.input(z14.object({
7707
- agentId: z14.string().max(128).optional(),
7708
- spawnId: z14.string().max(128),
7709
- cols: z14.number().int().min(1).max(1e3),
7710
- rows: z14.number().int().min(1).max(1e3)
8192
+ resize: protectedProcedure.input(z15.object({
8193
+ agentId: z15.string().max(128).optional(),
8194
+ spawnId: z15.string().max(128),
8195
+ cols: z15.number().int().min(1).max(1e3),
8196
+ rows: z15.number().int().min(1).max(1e3)
7711
8197
  })).mutation(async ({ input }) => {
7712
8198
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7713
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8199
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7714
8200
  return agentManager.agentMutation(agentId, "ai.resizePty", {
7715
8201
  spawnId: input.spawnId,
7716
8202
  cols: input.cols,
@@ -7723,14 +8209,14 @@ Context:
7723
8209
  * picks it up. Returns the tmp path and which clipboard method succeeded
7724
8210
  * (null if none — caller should inject `@<path>` instead of Ctrl+V).
7725
8211
  */
7726
- pasteImage: protectedProcedure.input(z14.object({
7727
- agentId: z14.string().max(128).optional(),
7728
- sessionId: z14.string().max(128).optional(),
7729
- mimeType: z14.string().max(64),
7730
- base64: z14.string().max(15e6)
8212
+ pasteImage: protectedProcedure.input(z15.object({
8213
+ agentId: z15.string().max(128).optional(),
8214
+ sessionId: z15.string().max(128).optional(),
8215
+ mimeType: z15.string().max(64),
8216
+ base64: z15.string().max(15e6)
7731
8217
  })).mutation(async ({ input }) => {
7732
8218
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7733
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8219
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7734
8220
  const res = await agentManager.agentMutation(agentId, "ai.pasteImage", {
7735
8221
  sessionId: input.sessionId,
7736
8222
  mimeType: input.mimeType,
@@ -7745,7 +8231,7 @@ Context:
7745
8231
  * Read the transcript Claude Code stores on disk for a given session.
7746
8232
  * Returns parsed JSONL entries (user / assistant messages, tool uses, etc.).
7747
8233
  */
7748
- transcript: protectedProcedure.input(z14.object({ sessionId: z14.string().max(128) })).query(async ({ input }) => {
8234
+ transcript: protectedProcedure.input(z15.object({ sessionId: z15.string().max(128) })).query(async ({ input }) => {
7749
8235
  return readClaudeTranscript(input.sessionId);
7750
8236
  }),
7751
8237
  /**
@@ -7753,9 +8239,9 @@ Context:
7753
8239
  * when was the transcript JSONL last touched? Used by the non-PTY view
7754
8240
  * to show a "Claude is thinking…" indicator while output streams.
7755
8241
  */
7756
- activity: protectedProcedure.input(z14.object({
7757
- agentId: z14.string().max(128).optional(),
7758
- sessionId: z14.string().max(128)
8242
+ activity: protectedProcedure.input(z15.object({
8243
+ agentId: z15.string().max(128).optional(),
8244
+ sessionId: z15.string().max(128)
7759
8245
  })).query(async ({ input }) => {
7760
8246
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7761
8247
  let running = false;
@@ -7781,10 +8267,10 @@ Context:
7781
8267
  * them on the conversation, so the non-PTY chat view stays in sync.
7782
8268
  * Uses the conversation id as session id (PTY sessions are spawned that way).
7783
8269
  */
7784
- syncTranscript: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
8270
+ syncTranscript: protectedProcedure.input(z15.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
7785
8271
  const conv = await conversationStore.get(input.conversationId);
7786
8272
  if (!conv) {
7787
- throw new TRPCError10({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
8273
+ throw new TRPCError11({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
7788
8274
  }
7789
8275
  const synced = await syncConversationFromClaudeTranscript(conv.id);
7790
8276
  const after = await conversationStore.get(conv.id);
@@ -7795,17 +8281,17 @@ Context:
7795
8281
  * with the given sessionId. Used to benchmark the PTY → tRPC → xterm.js
7796
8282
  * pipeline. Open the conversation in PTY mode to attach to it.
7797
8283
  */
7798
- debugStress: protectedProcedure.input(z14.object({
7799
- sessionId: z14.string().max(128),
7800
- agentId: z14.string().max(128).optional(),
7801
- bytesPerSec: z14.number().int().min(100).max(1e7).default(5e4),
7802
- chunkSize: z14.number().int().min(1).max(65536).default(256),
7803
- durationMs: z14.number().int().min(0).max(36e5).default(0),
7804
- cols: z14.number().int().min(1).max(1e3).default(120),
7805
- rows: z14.number().int().min(1).max(1e3).default(30)
8284
+ debugStress: protectedProcedure.input(z15.object({
8285
+ sessionId: z15.string().max(128),
8286
+ agentId: z15.string().max(128).optional(),
8287
+ bytesPerSec: z15.number().int().min(100).max(1e7).default(5e4),
8288
+ chunkSize: z15.number().int().min(1).max(65536).default(256),
8289
+ durationMs: z15.number().int().min(0).max(36e5).default(0),
8290
+ cols: z15.number().int().min(1).max(1e3).default(120),
8291
+ rows: z15.number().int().min(1).max(1e3).default(30)
7806
8292
  })).mutation(async ({ input }) => {
7807
8293
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7808
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8294
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7809
8295
  const stressSessionId = `${input.sessionId}-stress-${Date.now().toString(36)}`;
7810
8296
  const { existsSync: existsSync3 } = await import("fs");
7811
8297
  const candidates = [
@@ -7822,7 +8308,7 @@ Context:
7822
8308
  }
7823
8309
  }
7824
8310
  if (!scriptPath) {
7825
- throw new TRPCError10({
8311
+ throw new TRPCError11({
7826
8312
  code: "INTERNAL_SERVER_ERROR",
7827
8313
  message: `pty-stress-gen.mjs not found near ${fileURLToPath(import.meta.url)}`
7828
8314
  });
@@ -7847,12 +8333,12 @@ Context:
7847
8333
  return { spawnId: res.spawnId, agentId, stressSessionId };
7848
8334
  }),
7849
8335
  /** Kill a PTY session. */
7850
- kill: protectedProcedure.input(z14.object({
7851
- agentId: z14.string().max(128).optional(),
7852
- spawnId: z14.string().max(128)
8336
+ kill: protectedProcedure.input(z15.object({
8337
+ agentId: z15.string().max(128).optional(),
8338
+ spawnId: z15.string().max(128)
7853
8339
  })).mutation(async ({ input }) => {
7854
8340
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7855
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8341
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7856
8342
  return agentManager.agentMutation(agentId, "ai.kill", {
7857
8343
  spawnId: input.spawnId
7858
8344
  });
@@ -7861,125 +8347,125 @@ Context:
7861
8347
  });
7862
8348
 
7863
8349
  // ../server/src/trpc/routers/git.ts
7864
- import { z as z15 } from "zod";
7865
- import { TRPCError as TRPCError11 } from "@trpc/server";
8350
+ import { z as z16 } from "zod";
8351
+ import { TRPCError as TRPCError12 } from "@trpc/server";
7866
8352
  import Anthropic3 from "@anthropic-ai/sdk";
7867
8353
  import OpenAI3 from "openai";
7868
- var SERVICE_ID_SCHEMA = z15.string().max(128);
7869
- var SAFE_PATH_SCHEMA = z15.string().min(1).max(4096).refine((p) => !p.startsWith("/") && !p.includes("../"), {
8354
+ var SERVICE_ID_SCHEMA = z16.string().max(128);
8355
+ var SAFE_PATH_SCHEMA = z16.string().min(1).max(4096).refine((p) => !p.startsWith("/") && !p.includes("../"), {
7870
8356
  message: "Path must be relative and not traverse up"
7871
8357
  });
7872
- var REMOTE_SCHEMA = z15.string().min(1).max(128).regex(/^[a-zA-Z0-9_\-.]+$/);
7873
- var BRANCH_SCHEMA = z15.string().min(1).max(256).regex(/^[a-zA-Z0-9._\-/]+$/);
8358
+ var REMOTE_SCHEMA = z16.string().min(1).max(128).regex(/^[a-zA-Z0-9_\-.]+$/);
8359
+ var BRANCH_SCHEMA = z16.string().min(1).max(256).regex(/^[a-zA-Z0-9._\-/]+$/);
7874
8360
  async function resolveServiceContext(serviceId) {
7875
8361
  const service = await serviceStore.get(serviceId);
7876
8362
  if (!service) {
7877
- throw new TRPCError11({ code: "NOT_FOUND", message: "Service not found" });
8363
+ throw new TRPCError12({ code: "NOT_FOUND", message: "Service not found" });
7878
8364
  }
7879
8365
  const agentId = await resolveEffectiveAgentId(service);
7880
8366
  if (!agentId) {
7881
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent configured for this service" });
8367
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent configured for this service" });
7882
8368
  }
7883
8369
  return { agentId, cwd: service.cwd || "." };
7884
8370
  }
7885
8371
  var gitTargetShape = {
7886
8372
  serviceId: SERVICE_ID_SCHEMA.optional(),
7887
- agentId: z15.string().max(128).optional()
8373
+ agentId: z16.string().max(128).optional()
7888
8374
  };
7889
8375
  async function resolveGitContext(input) {
7890
8376
  if (input.serviceId) return resolveServiceContext(input.serviceId);
7891
8377
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7892
8378
  if (!agentId) {
7893
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent available" });
8379
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent available" });
7894
8380
  }
7895
8381
  return { agentId, cwd: "." };
7896
8382
  }
7897
8383
  var gitRouter = router({
7898
8384
  /** Get full git status */
7899
- status: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8385
+ status: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
7900
8386
  const { agentId, cwd } = await resolveGitContext(input);
7901
8387
  return agentManager.agentQuery(agentId, "git.status", { cwd });
7902
8388
  }),
7903
8389
  /** Get commit log */
7904
- log: protectedProcedure.input(z15.object({ ...gitTargetShape, limit: z15.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
8390
+ log: protectedProcedure.input(z16.object({ ...gitTargetShape, limit: z16.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
7905
8391
  const { agentId, cwd } = await resolveGitContext(input);
7906
8392
  return agentManager.agentQuery(agentId, "git.log", { cwd, limit: input.limit });
7907
8393
  }),
7908
8394
  /** List local and remote branches */
7909
- branches: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8395
+ branches: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
7910
8396
  const { agentId, cwd } = await resolveGitContext(input);
7911
8397
  return agentManager.agentQuery(agentId, "git.branches", { cwd });
7912
8398
  }),
7913
8399
  /** Checkout a branch */
7914
- checkout: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z15.boolean().default(false) })).mutation(async ({ input }) => {
8400
+ checkout: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z16.boolean().default(false) })).mutation(async ({ input }) => {
7915
8401
  const { agentId, cwd } = await resolveGitContext(input);
7916
8402
  return agentManager.agentMutation(agentId, "git.checkout", { cwd, branch: input.branch, create: input.create });
7917
8403
  }),
7918
8404
  /** Stage files */
7919
- stage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
8405
+ stage: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7920
8406
  const { agentId, cwd } = await resolveGitContext(input);
7921
8407
  return agentManager.agentMutation(agentId, "git.stage", { cwd, paths: input.paths });
7922
8408
  }),
7923
8409
  /** Stage all files */
7924
- stageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
8410
+ stageAll: protectedProcedure.input(z16.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7925
8411
  const { agentId, cwd } = await resolveGitContext(input);
7926
8412
  return agentManager.agentMutation(agentId, "git.stageAll", { cwd });
7927
8413
  }),
7928
8414
  /** Unstage files */
7929
- unstage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
8415
+ unstage: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7930
8416
  const { agentId, cwd } = await resolveGitContext(input);
7931
8417
  return agentManager.agentMutation(agentId, "git.unstage", { cwd, paths: input.paths });
7932
8418
  }),
7933
8419
  /** Unstage all files */
7934
- unstageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
8420
+ unstageAll: protectedProcedure.input(z16.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7935
8421
  const { agentId, cwd } = await resolveGitContext(input);
7936
8422
  return agentManager.agentMutation(agentId, "git.unstageAll", { cwd });
7937
8423
  }),
7938
8424
  /** Commit staged changes */
7939
- commit: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().min(1).max(4096) })).mutation(async ({ input }) => {
8425
+ commit: protectedProcedure.input(z16.object({ ...gitTargetShape, message: z16.string().min(1).max(4096) })).mutation(async ({ input }) => {
7940
8426
  const { agentId, cwd } = await resolveGitContext(input);
7941
8427
  return agentManager.agentMutation(agentId, "git.commit", { cwd, message: input.message });
7942
8428
  }),
7943
8429
  /** Push to remote */
7944
- push: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z15.boolean().default(false) })).mutation(async ({ input }) => {
8430
+ push: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z16.boolean().default(false) })).mutation(async ({ input }) => {
7945
8431
  const { agentId, cwd } = await resolveGitContext(input);
7946
8432
  return agentManager.agentMutation(agentId, "git.push", { cwd, remote: input.remote, setUpstream: input.setUpstream });
7947
8433
  }),
7948
8434
  /** Pull from remote */
7949
- pull: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
8435
+ pull: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7950
8436
  const { agentId, cwd } = await resolveGitContext(input);
7951
8437
  return agentManager.agentMutation(agentId, "git.pull", { cwd, remote: input.remote });
7952
8438
  }),
7953
8439
  /** Fetch from remote */
7954
- fetch: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
8440
+ fetch: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7955
8441
  const { agentId, cwd } = await resolveGitContext(input);
7956
8442
  return agentManager.agentMutation(agentId, "git.fetch", { cwd, remote: input.remote });
7957
8443
  }),
7958
8444
  /** Get diff for a file or the whole index */
7959
- diff: protectedProcedure.input(z15.object({
8445
+ diff: protectedProcedure.input(z16.object({
7960
8446
  ...gitTargetShape,
7961
- path: z15.string().max(4096).optional(),
7962
- staged: z15.boolean().default(false),
7963
- untracked: z15.boolean().default(false)
8447
+ path: z16.string().max(4096).optional(),
8448
+ staged: z16.boolean().default(false),
8449
+ untracked: z16.boolean().default(false)
7964
8450
  })).query(async ({ input }) => {
7965
8451
  const { agentId, cwd } = await resolveGitContext(input);
7966
8452
  return agentManager.agentQuery(agentId, "git.diff", { cwd, path: input.path, staged: input.staged, untracked: input.untracked });
7967
8453
  }),
7968
8454
  /** Discard changes to working tree files */
7969
- discard: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
8455
+ discard: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7970
8456
  const { agentId, cwd } = await resolveGitContext(input);
7971
8457
  return agentManager.agentMutation(agentId, "git.discard", { cwd, paths: input.paths });
7972
8458
  }),
7973
8459
  /** Generate a commit message from staged diffs using AI (diff fetched from agent, AI runs on server) */
7974
- generateCommitMessage: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
8460
+ generateCommitMessage: protectedProcedure.input(z16.object({ ...gitTargetShape, providerId: OptionalProviderIdSchema, model: z16.string().max(200).optional() })).mutation(async ({ input }) => {
7975
8461
  const { agentId, cwd } = await resolveGitContext(input);
7976
8462
  const { diff } = await agentManager.agentQuery(agentId, "git.stagedDiff", { cwd });
7977
8463
  if (!diff.trim()) {
7978
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
8464
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
7979
8465
  }
7980
- const active = await getActiveProvider();
8466
+ const active = await getActiveProvider(input.providerId);
7981
8467
  if (!active) {
7982
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
8468
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No AI provider configured" });
7983
8469
  }
7984
8470
  const systemPrompt = `You are a git commit message generator. Given a git diff, write a concise and meaningful conventional commit message (e.g. feat:, fix:, refactor:, chore:, docs:, style:, test:).
7985
8471
  Rules:
@@ -7988,7 +8474,7 @@ Rules:
7988
8474
  - Write in English
7989
8475
  - Return ONLY the commit message, nothing else`;
7990
8476
  const { provider, plaintextKey } = active;
7991
- const model = provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
8477
+ const model = input.model || provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7992
8478
  if (provider.provider === "anthropic") {
7993
8479
  const client = new Anthropic3({ apiKey: plaintextKey });
7994
8480
  const res = await client.messages.create({
@@ -8012,48 +8498,48 @@ Rules:
8012
8498
  });
8013
8499
  return { message: res.choices[0]?.message?.content?.trim() ?? "" };
8014
8500
  }
8015
- throw new TRPCError11({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
8501
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
8016
8502
  }),
8017
8503
  /** Merge a branch into the current branch */
8018
- merge: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, noFf: z15.boolean().default(false) })).mutation(async ({ input }) => {
8504
+ merge: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, noFf: z16.boolean().default(false) })).mutation(async ({ input }) => {
8019
8505
  const { agentId, cwd } = await resolveGitContext(input);
8020
8506
  return agentManager.agentMutation(agentId, "git.merge", { cwd, branch: input.branch, noFf: input.noFf });
8021
8507
  }),
8022
8508
  /** Rebase current branch onto another branch */
8023
- rebase: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
8509
+ rebase: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
8024
8510
  const { agentId, cwd } = await resolveGitContext(input);
8025
8511
  return agentManager.agentMutation(agentId, "git.rebase", { cwd, branch: input.branch });
8026
8512
  }),
8027
8513
  /** List stash entries */
8028
- stashList: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8514
+ stashList: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
8029
8515
  const { agentId, cwd } = await resolveGitContext(input);
8030
8516
  return agentManager.agentQuery(agentId, "git.stashList", { cwd });
8031
8517
  }),
8032
8518
  /** Stash current changes */
8033
- stashPush: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().max(4096).optional(), includeUntracked: z15.boolean().default(true) })).mutation(async ({ input }) => {
8519
+ stashPush: protectedProcedure.input(z16.object({ ...gitTargetShape, message: z16.string().max(4096).optional(), includeUntracked: z16.boolean().default(true) })).mutation(async ({ input }) => {
8034
8520
  const { agentId, cwd } = await resolveGitContext(input);
8035
8521
  return agentManager.agentMutation(agentId, "git.stashPush", { cwd, message: input.message, includeUntracked: input.includeUntracked });
8036
8522
  }),
8037
8523
  /** Pop a stash entry */
8038
- stashPop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8524
+ stashPop: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8039
8525
  const { agentId, cwd } = await resolveGitContext(input);
8040
8526
  return agentManager.agentMutation(agentId, "git.stashPop", { cwd, index: input.index });
8041
8527
  }),
8042
8528
  /** Apply a stash entry without dropping it */
8043
- stashApply: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8529
+ stashApply: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8044
8530
  const { agentId, cwd } = await resolveGitContext(input);
8045
8531
  return agentManager.agentMutation(agentId, "git.stashApply", { cwd, index: input.index });
8046
8532
  }),
8047
8533
  /** Drop a stash entry */
8048
- stashDrop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8534
+ stashDrop: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8049
8535
  const { agentId, cwd } = await resolveGitContext(input);
8050
8536
  return agentManager.agentMutation(agentId, "git.stashDrop", { cwd, index: input.index });
8051
8537
  }),
8052
8538
  /** Watch .git/ for changes and stream updated status */
8053
- onStatusChange: protectedProcedure.input(z15.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
8539
+ onStatusChange: protectedProcedure.input(z16.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
8054
8540
  const { agentId, cwd } = await resolveGitContext(input);
8055
8541
  const queue = [];
8056
- let resolve5 = null;
8542
+ let resolve6 = null;
8057
8543
  let done = false;
8058
8544
  let unsubscribe;
8059
8545
  try {
@@ -8063,21 +8549,21 @@ Rules:
8063
8549
  { cwd },
8064
8550
  (data) => {
8065
8551
  queue.push(data);
8066
- if (resolve5) {
8067
- resolve5();
8068
- resolve5 = null;
8552
+ if (resolve6) {
8553
+ resolve6();
8554
+ resolve6 = null;
8069
8555
  }
8070
8556
  },
8071
8557
  () => {
8072
8558
  done = true;
8073
- if (resolve5) {
8074
- resolve5();
8075
- resolve5 = null;
8559
+ if (resolve6) {
8560
+ resolve6();
8561
+ resolve6 = null;
8076
8562
  }
8077
8563
  }
8078
8564
  );
8079
8565
  } catch (err) {
8080
- throw new TRPCError11({
8566
+ throw new TRPCError12({
8081
8567
  code: "BAD_REQUEST",
8082
8568
  message: `Cannot subscribe to git status: ${err.message}`
8083
8569
  });
@@ -8088,7 +8574,7 @@ Rules:
8088
8574
  yield queue.shift();
8089
8575
  } else {
8090
8576
  await new Promise((r) => {
8091
- resolve5 = r;
8577
+ resolve6 = r;
8092
8578
  });
8093
8579
  }
8094
8580
  }
@@ -8097,49 +8583,49 @@ Rules:
8097
8583
  }
8098
8584
  }),
8099
8585
  /** List remotes */
8100
- remotes: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8586
+ remotes: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
8101
8587
  const { agentId, cwd } = await resolveGitContext(input);
8102
8588
  return agentManager.agentQuery(agentId, "git.remotes", { cwd });
8103
8589
  })
8104
8590
  });
8105
8591
 
8106
8592
  // ../server/src/trpc/routers/scenario.ts
8107
- import { randomUUID as randomUUID7 } from "crypto";
8108
- import { TRPCError as TRPCError14 } from "@trpc/server";
8109
- import { z as z16 } from "zod";
8593
+ import { randomUUID as randomUUID8 } from "crypto";
8594
+ import { TRPCError as TRPCError15 } from "@trpc/server";
8595
+ import { z as z17 } from "zod";
8110
8596
 
8111
8597
  // ../server/src/trpc/crud-procedure-utils.ts
8112
- import { TRPCError as TRPCError12 } from "@trpc/server";
8598
+ import { TRPCError as TRPCError13 } from "@trpc/server";
8113
8599
  async function getOrNotFound(getter, notFoundMessage) {
8114
8600
  const entity = await getter();
8115
- if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8601
+ if (!entity) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8116
8602
  return entity;
8117
8603
  }
8118
8604
  async function createOrBadRequest(creator) {
8119
8605
  try {
8120
8606
  return await creator();
8121
8607
  } catch (err) {
8122
- throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
8608
+ throw new TRPCError13({ code: "BAD_REQUEST", message: err.message });
8123
8609
  }
8124
8610
  }
8125
8611
  async function updateOrThrow(updater, notFoundMessage) {
8126
8612
  try {
8127
8613
  const entity = await updater();
8128
- if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8614
+ if (!entity) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8129
8615
  return entity;
8130
8616
  } catch (err) {
8131
- if (err instanceof TRPCError12) throw err;
8132
- throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
8617
+ if (err instanceof TRPCError13) throw err;
8618
+ throw new TRPCError13({ code: "BAD_REQUEST", message: err.message });
8133
8619
  }
8134
8620
  }
8135
8621
  async function deleteOrNotFound(deleter, notFoundMessage) {
8136
8622
  const deleted = await deleter();
8137
- if (!deleted) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8623
+ if (!deleted) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8138
8624
  return { ok: true };
8139
8625
  }
8140
8626
 
8141
8627
  // ../server/src/trpc/ai-generation-procedure-utils.ts
8142
- import { TRPCError as TRPCError13 } from "@trpc/server";
8628
+ import { TRPCError as TRPCError14 } from "@trpc/server";
8143
8629
 
8144
8630
  // ../server/src/services/ai-generation.ts
8145
8631
  import Anthropic4 from "@anthropic-ai/sdk";
@@ -8195,9 +8681,9 @@ async function* streamProviderText(input) {
8195
8681
  // ../server/src/trpc/ai-generation-procedure-utils.ts
8196
8682
  async function* streamWithActiveProvider(opts) {
8197
8683
  const active = await getActiveProvider(opts.providerId);
8198
- if (!active) throw new TRPCError13({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
8684
+ if (!active) throw new TRPCError14({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
8199
8685
  const { provider, plaintextKey } = active;
8200
- const model = provider.model ?? DEFAULT_MODELS[provider.provider] ?? "";
8686
+ const model = opts.model || provider.model || DEFAULT_MODELS[provider.provider] || "";
8201
8687
  try {
8202
8688
  for await (const text of streamProviderText({
8203
8689
  provider,
@@ -8213,16 +8699,16 @@ async function* streamWithActiveProvider(opts) {
8213
8699
  yield { type: "done", text: "" };
8214
8700
  } catch (err) {
8215
8701
  if (String(err?.message ?? "").startsWith("Unsupported provider:")) {
8216
- throw new TRPCError13({ code: "BAD_REQUEST", message: `Fournisseur non support\xE9: ${provider.provider}` });
8702
+ throw new TRPCError14({ code: "BAD_REQUEST", message: `Fournisseur non support\xE9: ${provider.provider}` });
8217
8703
  }
8218
8704
  throw err;
8219
8705
  }
8220
8706
  }
8221
8707
 
8222
8708
  // ../server/src/services/scenario-runner.ts
8223
- import { mkdir as mkdir12 } from "fs/promises";
8224
- import { randomUUID as randomUUID6 } from "crypto";
8225
- import { join as join14 } from "path";
8709
+ import { mkdir as mkdir13 } from "fs/promises";
8710
+ import { randomUUID as randomUUID7 } from "crypto";
8711
+ import { join as join15 } from "path";
8226
8712
 
8227
8713
  // ../server/src/services/scenario-presets.ts
8228
8714
  var BUILT_IN_PRESETS = [
@@ -8436,21 +8922,21 @@ async function* tapBus(conversationId, signal) {
8436
8922
  }
8437
8923
  if (bus.done) return;
8438
8924
  if (signal.aborted) return;
8439
- await new Promise((resolve5) => {
8925
+ await new Promise((resolve6) => {
8440
8926
  const onAbort = () => {
8441
8927
  bus.listeners.delete(notify);
8442
- resolve5();
8928
+ resolve6();
8443
8929
  };
8444
8930
  const notify = () => {
8445
8931
  signal.removeEventListener("abort", onAbort);
8446
- resolve5();
8932
+ resolve6();
8447
8933
  };
8448
8934
  bus.listeners.add(notify);
8449
8935
  signal.addEventListener("abort", onAbort, { once: true });
8450
8936
  if (cursor < bus.baseIndex + bus.buffer.length || bus.done || signal.aborted) {
8451
8937
  bus.listeners.delete(notify);
8452
8938
  signal.removeEventListener("abort", onAbort);
8453
- resolve5();
8939
+ resolve6();
8454
8940
  }
8455
8941
  });
8456
8942
  }
@@ -8563,7 +9049,7 @@ function applyHireDirectivesFromOutput(manager, scenario, output) {
8563
9049
  }
8564
9050
  const hired = [];
8565
9051
  for (const payload of hireDirectives) {
8566
- const id = `hire-${randomUUID6().slice(0, 8)}`;
9052
+ const id = `hire-${randomUUID7().slice(0, 8)}`;
8567
9053
  const position = computeHirePosition(manager, scenario.nodes);
8568
9054
  ensureGroupCanContainNode(scenario, manager.groupId ?? null, position);
8569
9055
  const node = {
@@ -8927,9 +9413,6 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
8927
9413
  model,
8928
9414
  effort: "normal",
8929
9415
  signal,
8930
- // Scenarios default to interactive PTY to stay on subscription pricing.
8931
- // Override with RUNEYA_CLAUDE_PTY=0 to fall back to headless `-p` mode.
8932
- usePty: aiCli === "claude-code" && process.env["RUNEYA_CLAUDE_PTY"] !== "0",
8933
9416
  onDelta: (text) => {
8934
9417
  accumulated += text;
8935
9418
  onDelta(text);
@@ -8957,9 +9440,9 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
8957
9440
  publish2({ type: "node_waiting_input", nodeId: node.id, question: result.tokenMessage });
8958
9441
  await readme?.(`- \u2753 [${formatTs()}] **${node.label}** \u2014 waiting for user input
8959
9442
  `);
8960
- const answer = await new Promise((resolve5, reject) => {
9443
+ const answer = await new Promise((resolve6, reject) => {
8961
9444
  const key = pendingNodeKey(conversationId, node.id);
8962
- pendingNodeInputs.set(key, resolve5);
9445
+ pendingNodeInputs.set(key, resolve6);
8963
9446
  signal.addEventListener("abort", () => {
8964
9447
  pendingNodeInputs.delete(key);
8965
9448
  reject(new Error("Aborted"));
@@ -8999,7 +9482,7 @@ async function appendNodeOutput(conversationId, node, output) {
8999
9482
  if (!dir) return;
9000
9483
  const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
9001
9484
  const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
9002
- const filePath = join14(dir, `${slug}_${safeId}.md`);
9485
+ const filePath = join15(dir, `${slug}_${safeId}.md`);
9003
9486
  const content = `## Output
9004
9487
 
9005
9488
  ${output}
@@ -9012,9 +9495,9 @@ async function appendToReadme(conversationId, bus, text) {
9012
9495
  const line = `${text}
9013
9496
  `;
9014
9497
  try {
9015
- await mkdir12(dir, { recursive: true });
9498
+ await mkdir13(dir, { recursive: true });
9016
9499
  const { appendFile: appendFile2 } = await import("fs/promises");
9017
- await appendFile2(join14(dir, "README.md"), line, "utf-8");
9500
+ await appendFile2(join15(dir, "README.md"), line, "utf-8");
9018
9501
  } catch {
9019
9502
  }
9020
9503
  publishToBus(bus, { type: "readme_append", text: line });
@@ -9034,7 +9517,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
9034
9517
 
9035
9518
  ${content}
9036
9519
  `;
9037
- const filePath = join14(dir, "FINAL.md");
9520
+ const filePath = join15(dir, "FINAL.md");
9038
9521
  await atomicWriteSecure(filePath, fullContent);
9039
9522
  await appendToReadme(
9040
9523
  conversationId,
@@ -9364,9 +9847,9 @@ ${outputs.get(node.id) ?? ""}`.trim();
9364
9847
  publish2({ type: "node_waiting_input", nodeId: node.id, question });
9365
9848
  await readme(`- \u2753 [${ts()}] **${node.label || node.id}** \u2014 waiting for user input
9366
9849
  `);
9367
- const answer = await new Promise((resolve5, reject) => {
9850
+ const answer = await new Promise((resolve6, reject) => {
9368
9851
  const key = pendingNodeKey(conversationId, node.id);
9369
- pendingNodeInputs.set(key, resolve5);
9852
+ pendingNodeInputs.set(key, resolve6);
9370
9853
  signal.addEventListener("abort", () => {
9371
9854
  pendingNodeInputs.delete(key);
9372
9855
  reject(new Error("Aborted"));
@@ -9703,21 +10186,21 @@ Modify the scenario. Keep existing ids when possible.`;
9703
10186
  }
9704
10187
  var scenarioRouter = router({
9705
10188
  list: protectedProcedure.query(() => scenarioStore.list()),
9706
- get: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => getOrNotFound(() => scenarioStore.get(input.id), "Scenario not found")),
9707
- create: protectedProcedure.input(z16.object({
10189
+ get: protectedProcedure.input(z17.object({ id: Id128Schema })).query(({ input }) => getOrNotFound(() => scenarioStore.get(input.id), "Scenario not found")),
10190
+ create: protectedProcedure.input(z17.object({
9708
10191
  name: NameSchema,
9709
- icon: z16.string().max(64).optional(),
10192
+ icon: z17.string().max(64).optional(),
9710
10193
  description: DescriptionSchema.optional(),
9711
10194
  leaderId: Id128Schema,
9712
- groups: z16.array(ScenarioGroupSchema).max(100).optional(),
9713
- nodes: z16.array(ScenarioNodeSchema).min(1).max(500),
9714
- traversalMode: z16.enum(["hierarchical", "topological"]).optional(),
9715
- presetId: z16.string().max(128).optional(),
9716
- isPublished: z16.boolean().optional()
10195
+ groups: z17.array(ScenarioGroupSchema).max(100).optional(),
10196
+ nodes: z17.array(ScenarioNodeSchema).min(1).max(500),
10197
+ traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
10198
+ presetId: z17.string().max(128).optional(),
10199
+ isPublished: z17.boolean().optional()
9717
10200
  })).mutation(({ input }) => {
9718
10201
  const now = Date.now();
9719
10202
  return createOrBadRequest(() => scenarioStore.create({
9720
- id: randomUUID7(),
10203
+ id: randomUUID8(),
9721
10204
  name: input.name,
9722
10205
  icon: input.icon,
9723
10206
  description: input.description,
@@ -9731,17 +10214,17 @@ var scenarioRouter = router({
9731
10214
  updatedAt: now
9732
10215
  }));
9733
10216
  }),
9734
- update: protectedProcedure.input(z16.object({
10217
+ update: protectedProcedure.input(z17.object({
9735
10218
  id: Id128Schema,
9736
10219
  name: NameSchema.optional(),
9737
- icon: z16.string().max(64).optional(),
10220
+ icon: z17.string().max(64).optional(),
9738
10221
  description: DescriptionSchema.optional(),
9739
10222
  leaderId: Id128Schema.optional(),
9740
- groups: z16.array(ScenarioGroupSchema).max(100).optional(),
9741
- nodes: z16.array(ScenarioNodeSchema).min(1).max(500).optional(),
9742
- traversalMode: z16.enum(["hierarchical", "topological"]).optional(),
9743
- presetId: z16.string().max(128).optional(),
9744
- isPublished: z16.boolean().optional()
10223
+ groups: z17.array(ScenarioGroupSchema).max(100).optional(),
10224
+ nodes: z17.array(ScenarioNodeSchema).min(1).max(500).optional(),
10225
+ traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
10226
+ presetId: z17.string().max(128).optional(),
10227
+ isPublished: z17.boolean().optional()
9745
10228
  })).mutation(({ input }) => updateOrThrow(
9746
10229
  () => scenarioStore.update(input.id, {
9747
10230
  name: input.name,
@@ -9757,18 +10240,18 @@ var scenarioRouter = router({
9757
10240
  }),
9758
10241
  "Scenario not found"
9759
10242
  )),
9760
- delete: protectedProcedure.input(z16.object({ id: Id128Schema })).mutation(({ input }) => deleteOrNotFound(() => scenarioStore.delete(input.id), "Scenario not found")),
9761
- run: protectedProcedure.input(z16.object({
10243
+ delete: protectedProcedure.input(z17.object({ id: Id128Schema })).mutation(({ input }) => deleteOrNotFound(() => scenarioStore.delete(input.id), "Scenario not found")),
10244
+ run: protectedProcedure.input(z17.object({
9762
10245
  conversationId: ConversationIdSchema,
9763
10246
  scenarioId: Id128Schema,
9764
- userRequest: z16.string().max(1e5),
9765
- runnerOverride: z16.enum(["claude-code", "codex"]).optional(),
10247
+ userRequest: z17.string().max(1e5),
10248
+ runnerOverride: z17.enum(["claude-code", "codex"]).optional(),
9766
10249
  modelOverride: Id128Schema.optional(),
9767
10250
  agentId: Id128Schema.optional(),
9768
- resume: z16.boolean().optional()
10251
+ resume: z17.boolean().optional()
9769
10252
  })).subscription(async function* ({ input, signal }) {
9770
10253
  const scenario = await scenarioStore.get(input.scenarioId);
9771
- if (!scenario) throw new TRPCError14({ code: "NOT_FOUND", message: "Scenario not found" });
10254
+ if (!scenario) throw new TRPCError15({ code: "NOT_FOUND", message: "Scenario not found" });
9772
10255
  const canStart = input.userRequest.trim().length > 0 || !!input.resume;
9773
10256
  if (!isScenarioRunning(input.conversationId) && canStart) {
9774
10257
  let userRequest = input.userRequest;
@@ -9784,52 +10267,54 @@ var scenarioRouter = router({
9784
10267
  }
9785
10268
  yield* tapBus(input.conversationId, signal ?? new AbortController().signal);
9786
10269
  }),
9787
- stop: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
10270
+ stop: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
9788
10271
  abortScenario(input.conversationId);
9789
10272
  return { ok: true };
9790
10273
  }),
9791
- sendNodeInput: protectedProcedure.input(z16.object({
10274
+ sendNodeInput: protectedProcedure.input(z17.object({
9792
10275
  conversationId: ConversationIdSchema,
9793
10276
  nodeId: Id128Schema,
9794
- text: z16.string().min(1).max(1e5)
10277
+ text: z17.string().min(1).max(1e5)
9795
10278
  })).mutation(({ input }) => {
9796
10279
  const resolved = resolveNodeInput(input.conversationId, input.nodeId, input.text);
9797
- if (!resolved) throw new TRPCError14({ code: "NOT_FOUND", message: "No pending input request for this node" });
10280
+ if (!resolved) throw new TRPCError15({ code: "NOT_FOUND", message: "No pending input request for this node" });
9798
10281
  return { ok: true };
9799
10282
  }),
9800
- isRunning: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => ({ running: isScenarioRunning(input.conversationId) })),
9801
- runState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => {
10283
+ isRunning: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(({ input }) => ({ running: isScenarioRunning(input.conversationId) })),
10284
+ runState: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(({ input }) => {
9802
10285
  const runState = scenarioRunStore.getRunState(input.conversationId);
9803
10286
  return runState ?? { nodeStates: {}, nodeOutputs: {}, activeNodeId: void 0 };
9804
10287
  }),
9805
- getPersistedRunState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10288
+ getPersistedRunState: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9806
10289
  return await scenarioRunStore.loadPersistedRunState(input.conversationId);
9807
10290
  }),
9808
- getReadme: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10291
+ getReadme: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9809
10292
  return await scenarioRunStore.readReadme(input.conversationId);
9810
10293
  }),
9811
- getSnapshot: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10294
+ getSnapshot: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9812
10295
  return await scenarioRunStore.loadScenarioSnapshot(input.conversationId);
9813
10296
  }),
9814
- streamGenerateWithAI: protectedProcedure.input(z16.object({
9815
- prompt: z16.string().min(1).max(1e4),
10297
+ streamGenerateWithAI: protectedProcedure.input(z17.object({
10298
+ prompt: z17.string().min(1).max(1e4),
9816
10299
  providerId: OptionalProviderIdSchema,
9817
- current: z16.object({
10300
+ model: z17.string().max(200).optional(),
10301
+ current: z17.object({
9818
10302
  leaderId: Id128Schema,
9819
- groups: z16.array(ScenarioGroupSchema).max(100),
9820
- nodes: z16.array(ScenarioNodeSchema).max(500)
10303
+ groups: z17.array(ScenarioGroupSchema).max(100),
10304
+ nodes: z17.array(ScenarioNodeSchema).max(500)
9821
10305
  }).optional()
9822
10306
  })).subscription(async function* ({ input, signal }) {
9823
10307
  const userMessage = buildScenarioUserMessage(input.prompt, input.current);
9824
10308
  yield* streamWithActiveProvider({
9825
10309
  providerId: input.providerId,
10310
+ model: input.model,
9826
10311
  systemPrompt: SCENARIO_SYSTEM_PROMPT,
9827
10312
  userMessage,
9828
10313
  maxTokens: 8192,
9829
10314
  signal: signal ?? void 0
9830
10315
  });
9831
10316
  }),
9832
- streamRunEvents: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).subscription(async function* ({ input }) {
10317
+ streamRunEvents: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).subscription(async function* ({ input }) {
9833
10318
  const convId = input.conversationId;
9834
10319
  const runState = scenarioRunStore.getRunState(convId);
9835
10320
  if (runState) {
@@ -9843,24 +10328,24 @@ var scenarioRouter = router({
9843
10328
  * open a debug PTY with `claude --resume <sessionId>` on the right agent.
9844
10329
  * Falls back to the persisted run-state for past nodes.
9845
10330
  */
9846
- getNodeSession: protectedProcedure.input(z16.object({
10331
+ getNodeSession: protectedProcedure.input(z17.object({
9847
10332
  conversationId: ConversationIdSchema,
9848
10333
  nodeId: Id128Schema
9849
10334
  })).query(async ({ input }) => {
9850
10335
  return await scenarioRunStore.getNodeSessionPersistent(input.conversationId, input.nodeId);
9851
10336
  }),
9852
10337
  listPresets: protectedProcedure.query(() => listPresets()),
9853
- getPreset: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => {
10338
+ getPreset: protectedProcedure.input(z17.object({ id: Id128Schema })).query(({ input }) => {
9854
10339
  const preset = getPreset(input.id);
9855
- if (!preset) throw new TRPCError14({ code: "NOT_FOUND", message: "Preset not found" });
10340
+ if (!preset) throw new TRPCError15({ code: "NOT_FOUND", message: "Preset not found" });
9856
10341
  return preset;
9857
10342
  })
9858
10343
  });
9859
10344
 
9860
10345
  // ../server/src/trpc/routers/debug.ts
9861
- import { randomUUID as randomUUID8 } from "crypto";
9862
- import { TRPCError as TRPCError15 } from "@trpc/server";
9863
- import { z as z17 } from "zod";
10346
+ import { randomUUID as randomUUID9 } from "crypto";
10347
+ import { TRPCError as TRPCError16 } from "@trpc/server";
10348
+ import { z as z18 } from "zod";
9864
10349
  var MAX_BUFFER3 = 300;
9865
10350
  var jobs = /* @__PURE__ */ new Map();
9866
10351
  function publish(job, event) {
@@ -9897,31 +10382,31 @@ async function* tapJob(job, signal) {
9897
10382
  yield evt;
9898
10383
  }
9899
10384
  if (job.done || signal.aborted) return;
9900
- await new Promise((resolve5) => {
10385
+ await new Promise((resolve6) => {
9901
10386
  const onAbort = () => {
9902
10387
  job.listeners.delete(notify);
9903
- resolve5();
10388
+ resolve6();
9904
10389
  };
9905
10390
  const notify = () => {
9906
10391
  signal.removeEventListener("abort", onAbort);
9907
- resolve5();
10392
+ resolve6();
9908
10393
  };
9909
10394
  job.listeners.add(notify);
9910
10395
  signal.addEventListener("abort", onAbort, { once: true });
9911
10396
  if (cursor < job.baseIndex + job.buffer.length || job.done || signal.aborted) {
9912
10397
  job.listeners.delete(notify);
9913
10398
  signal.removeEventListener("abort", onAbort);
9914
- resolve5();
10399
+ resolve6();
9915
10400
  }
9916
10401
  });
9917
10402
  }
9918
10403
  }
9919
10404
  var debugRouter = router({
9920
- startRandomJob: protectedProcedure.input(z17.object({
9921
- intervalMs: z17.number().int().min(100).max(5e3).optional(),
9922
- maxTicks: z17.number().int().min(1).max(1e4).optional()
10405
+ startRandomJob: protectedProcedure.input(z18.object({
10406
+ intervalMs: z18.number().int().min(100).max(5e3).optional(),
10407
+ maxTicks: z18.number().int().min(1).max(1e4).optional()
9923
10408
  }).optional()).mutation(({ input }) => {
9924
- const jobId = randomUUID8();
10409
+ const jobId = randomUUID9();
9925
10410
  const intervalMs = input?.intervalMs ?? 400;
9926
10411
  const maxTicks = input?.maxTicks ?? 500;
9927
10412
  const job = {
@@ -9953,15 +10438,15 @@ var debugRouter = router({
9953
10438
  jobs.set(jobId, job);
9954
10439
  return { jobId, intervalMs, maxTicks, startedAt: job.startedAt };
9955
10440
  }),
9956
- streamRandomJob: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).subscription(async function* ({ input, signal }) {
10441
+ streamRandomJob: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).subscription(async function* ({ input, signal }) {
9957
10442
  const job = jobs.get(input.jobId);
9958
10443
  if (!job) {
9959
- throw new TRPCError15({ code: "NOT_FOUND", message: "Random job not found" });
10444
+ throw new TRPCError16({ code: "NOT_FOUND", message: "Random job not found" });
9960
10445
  }
9961
10446
  yield* tapJob(job, signal ?? new AbortController().signal);
9962
10447
  }),
9963
- stopRandomJob: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).mutation(({ input }) => ({ ok: stopJob(input.jobId, "stopped") })),
9964
- randomJobStatus: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).query(({ input }) => {
10448
+ stopRandomJob: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).mutation(({ input }) => ({ ok: stopJob(input.jobId, "stopped") })),
10449
+ randomJobStatus: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).query(({ input }) => {
9965
10450
  const job = jobs.get(input.jobId);
9966
10451
  return {
9967
10452
  running: !!job && !job.done,
@@ -9970,132 +10455,11 @@ var debugRouter = router({
9970
10455
  })
9971
10456
  });
9972
10457
 
9973
- // ../server/src/trpc/routers/kanban.ts
9974
- import { z as z18 } from "zod";
9975
- import { TRPCError as TRPCError16 } from "@trpc/server";
9976
- async function deleteConversationsForIds(conversationIds) {
9977
- await Promise.all(conversationIds.map(async (id) => {
9978
- await conversationStore.delete(id);
9979
- await scenarioRunStore.deleteRunDir(id);
9980
- imageStorageService.deleteByContextId(id).catch(
9981
- (err) => console.error("[kanban] Image cleanup error:", err)
9982
- );
9983
- }));
9984
- }
9985
- var kanbanRouter = router({
9986
- list: protectedProcedure.input(z18.object({ includeArchived: z18.boolean().optional() }).optional()).query(async ({ input }) => {
9987
- return kanbanStore.list(input?.includeArchived ?? false);
9988
- }),
9989
- get: protectedProcedure.input(z18.object({ id: z18.string().max(128) })).query(async ({ input }) => {
9990
- const board = await kanbanStore.get(input.id);
9991
- if (!board) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.id} not found` });
9992
- return board;
9993
- }),
9994
- create: protectedProcedure.input(z18.object({
9995
- name: z18.string().min(1).max(200),
9996
- description: z18.string().max(1e4).optional()
9997
- })).mutation(async ({ input }) => {
9998
- return kanbanStore.create(input);
9999
- }),
10000
- update: protectedProcedure.input(z18.object({
10001
- id: z18.string().max(128),
10002
- name: z18.string().min(1).max(200).optional(),
10003
- description: z18.string().max(1e4).optional(),
10004
- columns: z18.array(KanbanColumnSchema).optional(),
10005
- archived: z18.boolean().optional()
10006
- })).mutation(async ({ input }) => {
10007
- let removedCardIds = [];
10008
- if (input.columns !== void 0) {
10009
- const existing = await kanbanStore.get(input.id);
10010
- if (existing) {
10011
- const oldCardIds = new Set(existing.columns.flatMap((col) => col.cards.map((c) => c.id)));
10012
- const newCardIds = new Set(input.columns.flatMap((col) => col.cards.map((c) => c.id)));
10013
- removedCardIds = Array.from(oldCardIds).filter((id) => !newCardIds.has(id));
10014
- }
10015
- }
10016
- const board = await kanbanStore.update(input);
10017
- if (!board) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.id} not found` });
10018
- if (removedCardIds.length > 0) {
10019
- const convsByCard = await Promise.all(removedCardIds.map((cardId) => conversationStore.listByKanbanCardId(cardId)));
10020
- const convIds = convsByCard.flat().map((c) => c.id);
10021
- if (convIds.length > 0) await deleteConversationsForIds(convIds);
10022
- }
10023
- return board;
10024
- }),
10025
- moveCard: protectedProcedure.input(z18.object({
10026
- boardId: z18.string().max(128),
10027
- cardId: z18.string().max(128),
10028
- targetColumn: z18.string().min(1).max(200)
10029
- })).mutation(async ({ input }) => {
10030
- const board = await kanbanStore.get(input.boardId);
10031
- if (!board) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.boardId} not found` });
10032
- const { executeKanbanCommands } = await import("./kanban-commands-53WWMCHQ.js");
10033
- await executeKanbanCommands(input.boardId, input.cardId, [{ type: "move_card", columnTitle: input.targetColumn }]);
10034
- return { success: true };
10035
- }),
10036
- addComment: protectedProcedure.input(z18.object({
10037
- boardId: z18.string().max(128),
10038
- cardId: z18.string().max(128),
10039
- comment: z18.string().min(1).max(1e4)
10040
- })).mutation(async ({ input }) => {
10041
- const board = await kanbanStore.get(input.boardId);
10042
- if (!board) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.boardId} not found` });
10043
- const { executeKanbanCommands } = await import("./kanban-commands-53WWMCHQ.js");
10044
- await executeKanbanCommands(input.boardId, input.cardId, [{ type: "add_comment", comment: input.comment }]);
10045
- return { success: true };
10046
- }),
10047
- addCard: protectedProcedure.input(z18.object({
10048
- boardId: z18.string().max(128),
10049
- column: z18.string().min(1).max(200),
10050
- title: z18.string().min(1).max(500),
10051
- content: z18.string().max(5e4).optional()
10052
- })).mutation(async ({ input }) => {
10053
- const board = await kanbanStore.get(input.boardId);
10054
- if (!board) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.boardId} not found` });
10055
- const { executeKanbanBoardCommands } = await import("./kanban-commands-53WWMCHQ.js");
10056
- await executeKanbanBoardCommands(input.boardId, [{ type: "add_card", columnTitle: input.column, cardTitle: input.title, cardContent: input.content }]);
10057
- return { success: true };
10058
- }),
10059
- delete: protectedProcedure.input(z18.object({ id: z18.string().max(128) })).mutation(async ({ input }) => {
10060
- const conversations = await conversationStore.listByKanbanBoardId(input.id);
10061
- const deleted = await kanbanStore.delete(input.id);
10062
- if (!deleted) throw new TRPCError16({ code: "NOT_FOUND", message: `Board ${input.id} not found` });
10063
- await deleteConversationsForIds(conversations.map((c) => c.id));
10064
- return { success: true };
10065
- }),
10066
- onChanged: protectedProcedure.subscription(async function* () {
10067
- const queue = [];
10068
- let resolve5 = null;
10069
- const handler = (event) => {
10070
- if (queue.length >= 200) queue.shift();
10071
- queue.push(event);
10072
- if (resolve5) {
10073
- resolve5();
10074
- resolve5 = null;
10075
- }
10076
- };
10077
- kanbanStore.onChanged(handler);
10078
- try {
10079
- while (true) {
10080
- if (queue.length > 0) {
10081
- yield queue.shift();
10082
- } else {
10083
- await new Promise((r) => {
10084
- resolve5 = r;
10085
- });
10086
- }
10087
- }
10088
- } finally {
10089
- kanbanStore.offChanged(handler);
10090
- }
10091
- })
10092
- });
10093
-
10094
10458
  // ../server/src/trpc/routers/kanban-queue.ts
10095
10459
  import { z as z19 } from "zod";
10096
10460
 
10097
10461
  // ../server/src/services/kanban-queue.ts
10098
- import { randomUUID as randomUUID9 } from "crypto";
10462
+ import { randomUUID as randomUUID10 } from "crypto";
10099
10463
  import { EventEmitter as EventEmitter4 } from "events";
10100
10464
  var KanbanQueueService = class extends EventEmitter4 {
10101
10465
  items = [];
@@ -10118,7 +10482,7 @@ var KanbanQueueService = class extends EventEmitter4 {
10118
10482
  return { id: alreadyRunning.id, addedAt: alreadyRunning.startedAt, ...item };
10119
10483
  }
10120
10484
  const queueItem = {
10121
- id: randomUUID9(),
10485
+ id: randomUUID10(),
10122
10486
  ...item,
10123
10487
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
10124
10488
  };
@@ -10310,13 +10674,13 @@ var kanbanQueueRouter = router({
10310
10674
  }),
10311
10675
  onChanged: protectedProcedure.subscription(async function* () {
10312
10676
  const queue = [];
10313
- let resolve5 = null;
10677
+ let resolve6 = null;
10314
10678
  const handler = () => {
10315
10679
  if (queue.length >= 50) queue.shift();
10316
10680
  queue.push(kanbanQueue.getState());
10317
- if (resolve5) {
10318
- resolve5();
10319
- resolve5 = null;
10681
+ if (resolve6) {
10682
+ resolve6();
10683
+ resolve6 = null;
10320
10684
  }
10321
10685
  };
10322
10686
  kanbanQueue.on("changed", handler);
@@ -10327,7 +10691,7 @@ var kanbanQueueRouter = router({
10327
10691
  yield queue.shift();
10328
10692
  } else {
10329
10693
  await new Promise((r) => {
10330
- resolve5 = r;
10694
+ resolve6 = r;
10331
10695
  });
10332
10696
  }
10333
10697
  }
@@ -10400,7 +10764,7 @@ var terminalRouter = router({
10400
10764
  sessionId: z20.string().max(128)
10401
10765
  })).subscription(async function* ({ input }) {
10402
10766
  const queue = [];
10403
- let resolve5 = null;
10767
+ let resolve6 = null;
10404
10768
  let done = false;
10405
10769
  let unsubscribe;
10406
10770
  try {
@@ -10410,16 +10774,16 @@ var terminalRouter = router({
10410
10774
  { sessionId: input.sessionId },
10411
10775
  (data) => {
10412
10776
  if (typeof data === "string") queue.push(data);
10413
- if (resolve5) {
10414
- resolve5();
10415
- resolve5 = null;
10777
+ if (resolve6) {
10778
+ resolve6();
10779
+ resolve6 = null;
10416
10780
  }
10417
10781
  },
10418
10782
  () => {
10419
10783
  done = true;
10420
- if (resolve5) {
10421
- resolve5();
10422
- resolve5 = null;
10784
+ if (resolve6) {
10785
+ resolve6();
10786
+ resolve6 = null;
10423
10787
  }
10424
10788
  }
10425
10789
  );
@@ -10435,7 +10799,7 @@ var terminalRouter = router({
10435
10799
  yield queue.shift();
10436
10800
  } else {
10437
10801
  await new Promise((r) => {
10438
- resolve5 = r;
10802
+ resolve6 = r;
10439
10803
  });
10440
10804
  }
10441
10805
  }
@@ -10452,7 +10816,7 @@ var utilityRouter = router({
10452
10816
  wait: protectedProcedure.input(z21.object({
10453
10817
  ms: z21.number().int().min(0).max(MAX_WAIT_MS)
10454
10818
  })).mutation(async ({ input }) => {
10455
- await new Promise((resolve5) => setTimeout(resolve5, input.ms));
10819
+ await new Promise((resolve6) => setTimeout(resolve6, input.ms));
10456
10820
  return { waited: input.ms };
10457
10821
  })
10458
10822
  });
@@ -10460,8 +10824,8 @@ var utilityRouter = router({
10460
10824
  // ../server/src/trpc/routers/wiki.ts
10461
10825
  import { z as z22 } from "zod";
10462
10826
  import { TRPCError as TRPCError18 } from "@trpc/server";
10463
- import { readFile as readFile12, readdir as readdir5, stat as stat3, realpath, writeFile as writeFile10, mkdir as mkdir13, rename as rename9, rm as rm2 } from "fs/promises";
10464
- import { resolve as resolvePath, join as join15, relative, sep as sep3, dirname as dirname3 } from "path";
10827
+ import { readFile as readFile13, readdir as readdir5, stat as stat3, realpath, writeFile as writeFile11, mkdir as mkdir14, rename as rename10, rm as rm2 } from "fs/promises";
10828
+ import { resolve as resolvePath, join as join16, relative, sep as sep3, dirname as dirname3 } from "path";
10465
10829
  var SCOPE_SCHEMA = z22.union([
10466
10830
  z22.literal("global"),
10467
10831
  z22.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
@@ -10498,13 +10862,13 @@ async function resolveScope(scope) {
10498
10862
  throw new TRPCError18({ code: "BAD_REQUEST", message: "Sous-chemin invalide" });
10499
10863
  }
10500
10864
  const dataDirAbs = resolvePath(env.DATA_DIR);
10501
- await mkdir13(dataDirAbs, { recursive: true });
10865
+ await mkdir14(dataDirAbs, { recursive: true });
10502
10866
  const base = dirname3(await realpath(dataDirAbs));
10503
10867
  const target = resolvePath(base, sub);
10504
10868
  if (target !== base && !(target + sep3).startsWith(base + sep3)) {
10505
10869
  throw new TRPCError18({ code: "FORBIDDEN", message: "Hors du dossier de donn\xE9es du serveur" });
10506
10870
  }
10507
- await mkdir13(target, { recursive: true });
10871
+ await mkdir14(target, { recursive: true });
10508
10872
  const root = await realpath(target);
10509
10873
  if (root !== base && !(root + sep3).startsWith(base + sep3)) {
10510
10874
  throw new TRPCError18({ code: "FORBIDDEN", message: "Le dossier wiki r\xE9sout hors du dossier de donn\xE9es" });
@@ -10531,7 +10895,7 @@ async function walkLocal(root, dir, depth, counter) {
10531
10895
  for (const e of entries) {
10532
10896
  if (counter.n >= MAX_FILES) break;
10533
10897
  if (e.name.startsWith(".") && e.name !== ".") continue;
10534
- const abs = join15(dir, e.name);
10898
+ const abs = join16(dir, e.name);
10535
10899
  const rel = relative(root, abs).split(sep3).join("/");
10536
10900
  if (e.isDirectory()) {
10537
10901
  if (IGNORED_DIRS.has(e.name)) continue;
@@ -10599,7 +10963,7 @@ var wikiRouter = router({
10599
10963
  const s = await stat3(file);
10600
10964
  if (!s.isFile()) throw new TRPCError18({ code: "BAD_REQUEST", message: "Pas un fichier" });
10601
10965
  if (s.size > MAX_FILE_BYTES) throw new TRPCError18({ code: "PAYLOAD_TOO_LARGE", message: "Fichier trop volumineux" });
10602
- const content = await readFile12(file, "utf-8");
10966
+ const content = await readFile13(file, "utf-8");
10603
10967
  return { path: input.path, content };
10604
10968
  }),
10605
10969
  write: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA, content: z22.string().max(5e6) })).mutation(async ({ input }) => {
@@ -10611,8 +10975,8 @@ var wikiRouter = router({
10611
10975
  return agentManager.agentMutation(target.agentId, "wiki.writeMarkdown", { cwd: target.cwd, path: input.path, content: input.content });
10612
10976
  }
10613
10977
  const file = await resolveSafeLocal(target.root, input.path, false);
10614
- await mkdir13(dirname3(file), { recursive: true });
10615
- await writeFile10(file, input.content, "utf-8");
10978
+ await mkdir14(dirname3(file), { recursive: true });
10979
+ await writeFile11(file, input.content, "utf-8");
10616
10980
  return { path: input.path, ok: true };
10617
10981
  }),
10618
10982
  createFile: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
@@ -10630,8 +10994,8 @@ var wikiRouter = router({
10630
10994
  } catch (err) {
10631
10995
  if (err instanceof TRPCError18) throw err;
10632
10996
  }
10633
- await mkdir13(dirname3(file), { recursive: true });
10634
- await writeFile10(file, "", "utf-8");
10997
+ await mkdir14(dirname3(file), { recursive: true });
10998
+ await writeFile11(file, "", "utf-8");
10635
10999
  return { path: input.path, ok: true };
10636
11000
  }),
10637
11001
  createDir: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
@@ -10640,7 +11004,7 @@ var wikiRouter = router({
10640
11004
  return agentManager.agentMutation(target.agentId, "wiki.createDir", { cwd: target.cwd, path: input.path });
10641
11005
  }
10642
11006
  const dir = await resolveSafeLocal(target.root, input.path, false);
10643
- await mkdir13(dir, { recursive: true });
11007
+ await mkdir14(dir, { recursive: true });
10644
11008
  return { path: input.path, ok: true };
10645
11009
  }),
10646
11010
  rename: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, fromPath: REL_PATH_SCHEMA, toPath: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
@@ -10656,8 +11020,8 @@ var wikiRouter = router({
10656
11020
  } catch (err) {
10657
11021
  if (err instanceof TRPCError18) throw err;
10658
11022
  }
10659
- await mkdir13(dirname3(to), { recursive: true });
10660
- await rename9(from, to);
11023
+ await mkdir14(dirname3(to), { recursive: true });
11024
+ await rename10(from, to);
10661
11025
  return { fromPath: input.fromPath, toPath: input.toPath, ok: true };
10662
11026
  }),
10663
11027
  ensureRoot: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA })).mutation(async ({ input }) => {
@@ -10686,83 +11050,24 @@ var wikiRouter = router({
10686
11050
  });
10687
11051
 
10688
11052
  // ../server/src/trpc/routers/config.ts
10689
- var configRouter = router({
10690
- cloud: publicProcedure.query(() => ({
10691
- apiUrl: env.CLOUD_URL,
10692
- appUrl: env.CLOUD_APP_URL
10693
- }))
10694
- });
10695
-
10696
- // ../server/src/trpc/routers/cloud-sync.ts
10697
- import { z as z24 } from "zod";
10698
- import { TRPCError as TRPCError20 } from "@trpc/server";
10699
-
10700
- // ../server/src/services/cloud-sync-store.ts
10701
- import { readFile as readFile13, writeFile as writeFile11, rename as rename10, mkdir as mkdir14, chmod as chmod8 } from "fs/promises";
10702
- import { join as join16 } from "path";
10703
- var FILENAME4 = "cloud-sync.json";
10704
- var CloudSyncStore = class {
10705
- links = /* @__PURE__ */ new Map();
10706
- loaded = false;
10707
- getFilePath() {
10708
- return join16(env.DATA_DIR, FILENAME4);
10709
- }
10710
- async load() {
10711
- if (this.loaded) return;
10712
- try {
10713
- const raw = await readFile13(this.getFilePath(), "utf-8");
10714
- const data = JSON.parse(raw);
10715
- for (const l of data) this.links.set(l.orgId, { orgId: l.orgId, cloudUrl: l.cloudUrl });
10716
- } catch {
10717
- }
10718
- this.loaded = true;
10719
- }
10720
- async save() {
10721
- await mkdir14(env.DATA_DIR, { recursive: true });
10722
- const filePath = this.getFilePath();
10723
- const tmpPath = filePath + ".tmp";
10724
- const data = JSON.stringify(Array.from(this.links.values()), null, 2);
10725
- await writeFile11(tmpPath, data, "utf-8");
10726
- await rename10(tmpPath, filePath);
10727
- await chmod8(filePath, 384);
10728
- }
10729
- /** Store (or replace) the sync link for an organization. */
10730
- async upsert(orgId, cloudUrl) {
10731
- await this.load();
10732
- this.links.set(orgId, { orgId, cloudUrl });
10733
- await this.save();
10734
- }
10735
- async remove(orgId) {
10736
- await this.load();
10737
- if (this.links.delete(orgId)) await this.save();
10738
- }
10739
- /** All sync links (for the bridge on startup). */
10740
- async list() {
10741
- await this.load();
10742
- return Array.from(this.links.values());
10743
- }
10744
- async get(orgId) {
10745
- await this.load();
10746
- return this.links.get(orgId) ?? null;
10747
- }
10748
- };
10749
- var cloudSyncStore = new CloudSyncStore();
11053
+ var configRouter = router({
11054
+ cloud: publicProcedure.query(() => ({
11055
+ apiUrl: env.CLOUD_URL,
11056
+ appUrl: env.CLOUD_APP_URL
11057
+ }))
11058
+ });
10750
11059
 
10751
- // ../server/src/services/cloud-sync-bridge.ts
10752
- import { randomUUID as randomUUID10 } from "crypto";
10753
- import * as Y from "yjs";
11060
+ // ../server/src/trpc/routers/cloud-sync.ts
11061
+ import { z as z23 } from "zod";
11062
+ import { TRPCError as TRPCError19 } from "@trpc/server";
10754
11063
 
10755
- // ../server/src/services/cloud-session-store.ts
10756
- import { readFile as readFile14, writeFile as writeFile12, rename as rename11, mkdir as mkdir15, chmod as chmod9, unlink as unlink3 } from "fs/promises";
10757
- import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "crypto";
11064
+ // ../server/src/services/cloud-sync-store.ts
11065
+ import { readFile as readFile14, writeFile as writeFile12, rename as rename11, mkdir as mkdir15, chmod as chmod9 } from "fs/promises";
10758
11066
  import { join as join17 } from "path";
10759
- var FILENAME5 = "cloud-session.json";
10760
- var STATE_TTL_MS = 5 * 60 * 1e3;
10761
- var CloudSessionStore = class {
10762
- session = null;
11067
+ var FILENAME5 = "cloud-sync.json";
11068
+ var CloudSyncStore = class {
11069
+ links = /* @__PURE__ */ new Map();
10763
11070
  loaded = false;
10764
- /** Pending login states (CSRF binding between `start` and the callback). */
10765
- pending = /* @__PURE__ */ new Map();
10766
11071
  getFilePath() {
10767
11072
  return join17(env.DATA_DIR, FILENAME5);
10768
11073
  }
@@ -10770,86 +11075,46 @@ var CloudSessionStore = class {
10770
11075
  if (this.loaded) return;
10771
11076
  try {
10772
11077
  const raw = await readFile14(this.getFilePath(), "utf-8");
10773
- this.session = JSON.parse(raw);
11078
+ const data = JSON.parse(raw);
11079
+ for (const l of data) this.links.set(l.orgId, { orgId: l.orgId, cloudUrl: l.cloudUrl });
10774
11080
  } catch {
10775
11081
  }
10776
11082
  this.loaded = true;
10777
11083
  }
10778
- /**
10779
- * Mint a state value binding a login attempt to this server. Returned to the
10780
- * cloud page and echoed back on the callback, so a redirect we never
10781
- * initiated cannot install a token.
10782
- */
10783
- createState(returnTo) {
10784
- this.sweepStates();
10785
- const state = randomBytes3(32).toString("hex");
10786
- this.pending.set(state, { expiresAt: Date.now() + STATE_TTL_MS, returnTo });
10787
- return state;
10788
- }
10789
- /**
10790
- * Consume a state — single use, constant-time compare, TTL enforced. Returns
10791
- * the URL to send the browser back to, or null when the state is unknown or
10792
- * expired (i.e. a callback this server never initiated).
10793
- */
10794
- consumeState(candidate) {
10795
- this.sweepStates();
10796
- for (const [state, entry] of this.pending) {
10797
- const a = Buffer.from(state);
10798
- const b = Buffer.from(candidate);
10799
- if (a.length === b.length && timingSafeEqual2(a, b)) {
10800
- this.pending.delete(state);
10801
- return entry.expiresAt > Date.now() ? entry.returnTo : null;
10802
- }
10803
- }
10804
- return null;
10805
- }
10806
- sweepStates() {
10807
- const now = Date.now();
10808
- for (const [state, entry] of this.pending) {
10809
- if (entry.expiresAt <= now) this.pending.delete(state);
10810
- }
10811
- }
10812
11084
  async save() {
10813
11085
  await mkdir15(env.DATA_DIR, { recursive: true });
10814
11086
  const filePath = this.getFilePath();
10815
11087
  const tmpPath = filePath + ".tmp";
10816
- await writeFile12(tmpPath, JSON.stringify(this.session, null, 2), "utf-8");
11088
+ const data = JSON.stringify(Array.from(this.links.values()), null, 2);
11089
+ await writeFile12(tmpPath, data, "utf-8");
10817
11090
  await rename11(tmpPath, filePath);
10818
11091
  await chmod9(filePath, 384);
10819
11092
  }
10820
- /** Store the session token obtained from the one-time-token exchange. */
10821
- async set(cloudUrl, token) {
11093
+ /** Store (or replace) the sync link for an organization. */
11094
+ async upsert(orgId, cloudUrl) {
10822
11095
  await this.load();
10823
- this.session = { cloudUrl, encryptedToken: encryptValue(token) };
11096
+ this.links.set(orgId, { orgId, cloudUrl });
10824
11097
  await this.save();
10825
11098
  }
10826
- /** The decrypted session token, or null when not logged in. */
10827
- async getToken() {
11099
+ async remove(orgId) {
10828
11100
  await this.load();
10829
- if (!this.session) return null;
10830
- try {
10831
- return decryptValue(this.session.encryptedToken);
10832
- } catch {
10833
- return null;
10834
- }
11101
+ if (this.links.delete(orgId)) await this.save();
10835
11102
  }
10836
- async isLinked() {
10837
- return await this.getToken() !== null;
11103
+ /** All sync links (for the bridge on startup). */
11104
+ async list() {
11105
+ await this.load();
11106
+ return Array.from(this.links.values());
10838
11107
  }
10839
- /** Forget the session (sign-out). */
10840
- async clear() {
11108
+ async get(orgId) {
10841
11109
  await this.load();
10842
- this.session = null;
10843
- this.pending.clear();
10844
- try {
10845
- await unlink3(this.getFilePath());
10846
- } catch {
10847
- }
11110
+ return this.links.get(orgId) ?? null;
10848
11111
  }
10849
11112
  };
10850
- var cloudSessionStore = new CloudSessionStore();
11113
+ var cloudSyncStore = new CloudSyncStore();
10851
11114
 
10852
11115
  // ../server/src/services/cloud-sync-bridge.ts
11116
+ import { randomUUID as randomUUID11 } from "crypto";
11117
+ import * as Y from "yjs";
10853
11118
  var LOCAL_ORIGIN = "local-bridge";
10854
11119
  var REMOTE_ORIGIN = "remote";
10855
11120
  var RECONNECT_DELAY_MS = 3e3;
@@ -10865,7 +11130,7 @@ var OrgSyncConnection = class {
10865
11130
  projects = getCollection(this.doc, "projects");
10866
11131
  services = getCollection(this.doc, "services");
10867
11132
  environments = getCollection(this.doc, "environments");
10868
- conn = randomUUID10();
11133
+ conn = randomUUID11();
10869
11134
  link;
10870
11135
  stopped = false;
10871
11136
  initializing = true;
@@ -10965,7 +11230,7 @@ var OrgSyncConnection = class {
10965
11230
  await this.pushUpdate(diff);
10966
11231
  } catch (err) {
10967
11232
  if (err instanceof SyncAuthError) throw err;
10968
- console.error(`[cloud-sync] initial push failed for org ${this.link.orgId}:`, err.message);
11233
+ console.error("[cloud-sync] initial push failed for org %s:", this.link.orgId, err.message);
10969
11234
  }
10970
11235
  }
10971
11236
  async pushUpdate(update) {
@@ -10983,7 +11248,7 @@ var OrgSyncConnection = class {
10983
11248
  this.abort?.abort();
10984
11249
  return;
10985
11250
  }
10986
- if (!this.stopped) console.error(`[cloud-sync] push failed for org ${this.link.orgId}:`, err.message);
11251
+ if (!this.stopped) console.error("[cloud-sync] push failed for org %s:", this.link.orgId, err.message);
10987
11252
  }
10988
11253
  }
10989
11254
  /**
@@ -11005,7 +11270,7 @@ var OrgSyncConnection = class {
11005
11270
  await this.delay(UNAUTHENTICATED_RETRY_MS);
11006
11271
  continue;
11007
11272
  }
11008
- if (!this.stopped) console.error(`[cloud-sync] stream error for org ${this.link.orgId}:`, err.message);
11273
+ if (!this.stopped) console.error("[cloud-sync] stream error for org %s:", this.link.orgId, err.message);
11009
11274
  }
11010
11275
  await this.delay(RECONNECT_DELAY_MS);
11011
11276
  }
@@ -11095,7 +11360,7 @@ var OrgSyncConnection = class {
11095
11360
  });
11096
11361
  await projectStore.addEnvironment(projectId, localEnv.id);
11097
11362
  } catch (err) {
11098
- console.error(`[cloud-sync] env bootstrap failed for project ${projectId}:`, err.message);
11363
+ console.error("[cloud-sync] env bootstrap failed for project %s:", projectId, err.message);
11099
11364
  }
11100
11365
  }
11101
11366
  /**
@@ -11198,7 +11463,7 @@ var CloudSyncBridge = class {
11198
11463
  try {
11199
11464
  await conn.start();
11200
11465
  } catch (err) {
11201
- console.error(`[cloud-sync] failed to start org ${link.orgId}:`, err.message);
11466
+ console.error("[cloud-sync] failed to start org %s:", link.orgId, err.message);
11202
11467
  }
11203
11468
  }
11204
11469
  disconnect(orgId) {
@@ -11215,113 +11480,6 @@ var CloudSyncBridge = class {
11215
11480
  };
11216
11481
  var cloudSyncBridge = new CloudSyncBridge();
11217
11482
 
11218
- // ../server/src/trpc/routers/cloud-auth.ts
11219
- import { z as z23 } from "zod";
11220
- import { TRPCError as TRPCError19 } from "@trpc/server";
11221
- var DEFAULT_CLOUD_API_URL = "https://api.runeya.dev";
11222
- var DEFAULT_CLOUD_APP_URL = "https://runeya.dev";
11223
- function getCloudApiUrl() {
11224
- return (env.CLOUD_URL ?? DEFAULT_CLOUD_API_URL).replace(/\/+$/, "");
11225
- }
11226
- function getCloudAppUrl() {
11227
- return (env.CLOUD_APP_URL ?? DEFAULT_CLOUD_APP_URL).replace(/\/+$/, "");
11228
- }
11229
- function isLocalOrigin(url) {
11230
- try {
11231
- const { protocol, hostname } = new URL(url);
11232
- return (protocol === "http:" || protocol === "https:") && (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]");
11233
- } catch {
11234
- return false;
11235
- }
11236
- }
11237
- async function cloudFetch(path, init = {}) {
11238
- const token = await cloudSessionStore.getToken();
11239
- if (!token) {
11240
- throw new TRPCError19({ code: "UNAUTHORIZED", message: "Not linked to the Runeya cloud." });
11241
- }
11242
- return fetch(`${getCloudApiUrl()}${path}`, {
11243
- method: init.method ?? "GET",
11244
- headers: {
11245
- Authorization: `Bearer ${token}`,
11246
- ...init.body !== void 0 ? { "Content-Type": "application/json" } : {}
11247
- },
11248
- ...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
11249
- signal: AbortSignal.timeout(15e3)
11250
- });
11251
- }
11252
- var PROXY_PREFIXES = ["/organization/", "/project/", "/chat/", "/user/prefs"];
11253
- function isAllowedProxyPath(path) {
11254
- if (!path.startsWith("/") || path.includes("..")) return false;
11255
- const [pathname] = path.split("?");
11256
- return PROXY_PREFIXES.some((p) => pathname === p || pathname.startsWith(p));
11257
- }
11258
- var cloudAuthRouter = router({
11259
- /** Build the cloud authorization URL to open in the browser. */
11260
- startLogin: protectedProcedure.input(z23.object({ returnTo: z23.string().url().max(2048) })).mutation(async ({ input }) => {
11261
- if (!isLocalOrigin(input.returnTo)) {
11262
- throw new TRPCError19({ code: "BAD_REQUEST", message: "returnTo must be a local origin." });
11263
- }
11264
- const state = cloudSessionStore.createState(input.returnTo);
11265
- const callback = `http://127.0.0.1:${env.PORT}/api/cloud/callback`;
11266
- const url = `${getCloudAppUrl()}/link-local?callback=${encodeURIComponent(callback)}&state=${encodeURIComponent(state)}`;
11267
- return { url };
11268
- }),
11269
- /** Whether this machine holds a cloud session. */
11270
- status: protectedProcedure.query(async () => {
11271
- return { linked: await cloudSessionStore.isLinked() };
11272
- }),
11273
- /** The cloud session (user + org), fetched with the stored token. */
11274
- session: protectedProcedure.query(async () => {
11275
- if (!await cloudSessionStore.isLinked()) return null;
11276
- const res = await cloudFetch("/api/auth/get-session");
11277
- if (!res.ok) return null;
11278
- return await res.json();
11279
- }),
11280
- /**
11281
- * Forward a cloud API call, authenticated with this machine's token.
11282
- *
11283
- * This is what replaces the browser's direct, cookie-authenticated calls to
11284
- * the cloud: the web UI asks its own server, which holds the credential.
11285
- */
11286
- proxy: protectedProcedure.input(
11287
- z23.object({
11288
- path: z23.string().min(1).max(512),
11289
- method: z23.enum(["GET", "POST"]).default("GET"),
11290
- query: z23.record(z23.string().max(128), z23.string().max(2048)).optional(),
11291
- body: z23.unknown().optional()
11292
- })
11293
- ).mutation(async ({ input }) => {
11294
- if (!isAllowedProxyPath(input.path)) {
11295
- throw new TRPCError19({ code: "FORBIDDEN", message: `Path not allowed: ${input.path}` });
11296
- }
11297
- const qs = input.query ? `?${new URLSearchParams(input.query).toString()}` : "";
11298
- const res = await cloudFetch(`/api/auth${input.path}${qs}`, {
11299
- method: input.method,
11300
- ...input.method === "POST" ? { body: input.body ?? {} } : {}
11301
- });
11302
- const text = await res.text();
11303
- let data = null;
11304
- try {
11305
- data = text ? JSON.parse(text) : null;
11306
- } catch {
11307
- data = text;
11308
- }
11309
- if (!res.ok) return { data: null, error: { status: res.status, ...data } };
11310
- return { data, error: null };
11311
- }),
11312
- /** Drop the cloud session, both here and on the cloud. */
11313
- signOut: protectedProcedure.mutation(async () => {
11314
- if (await cloudSessionStore.isLinked()) {
11315
- try {
11316
- await cloudFetch("/api/auth/sign-out", { method: "POST", body: {} });
11317
- } catch {
11318
- }
11319
- }
11320
- await cloudSessionStore.clear();
11321
- return { success: true };
11322
- })
11323
- });
11324
-
11325
11483
  // ../server/src/services/master-password.ts
11326
11484
  import { randomBytes as randomBytes4, createCipheriv, createDecipheriv } from "crypto";
11327
11485
  import { readFile as readFile15, writeFile as writeFile13, chmod as chmod10, stat as stat4, rename as rename12 } from "fs/promises";
@@ -11492,25 +11650,25 @@ async function hasEncryptedKeyFile(dataDir) {
11492
11650
  async function reconcileOrgCanary(orgId) {
11493
11651
  const key = getEncryptionKey();
11494
11652
  if (!key) {
11495
- throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "Encryption key not loaded." });
11653
+ throw new TRPCError19({ code: "PRECONDITION_FAILED", message: "Encryption key not loaded." });
11496
11654
  }
11497
11655
  const local = await readVerifyFile(env.DATA_DIR);
11498
11656
  if (!local) {
11499
- throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "This machine has no key canary yet." });
11657
+ throw new TRPCError19({ code: "PRECONDITION_FAILED", message: "This machine has no key canary yet." });
11500
11658
  }
11501
11659
  const res = await cloudFetch("/api/auth/organization/canary", {
11502
11660
  method: "POST",
11503
11661
  body: { organizationId: orgId, canary: local }
11504
11662
  });
11505
11663
  if (!res.ok) {
11506
- throw new TRPCError20({ code: "BAD_GATEWAY", message: "Could not reach the organization on the cloud." });
11664
+ throw new TRPCError19({ code: "BAD_GATEWAY", message: "Could not reach the organization on the cloud." });
11507
11665
  }
11508
11666
  const { canary } = await res.json();
11509
11667
  if (typeof canary !== "string") {
11510
- throw new TRPCError20({ code: "BAD_GATEWAY", message: "The cloud returned no key canary." });
11668
+ throw new TRPCError19({ code: "BAD_GATEWAY", message: "The cloud returned no key canary." });
11511
11669
  }
11512
11670
  if (!matchesCanary(canary, key)) {
11513
- throw new TRPCError20({
11671
+ throw new TRPCError19({
11514
11672
  code: "FORBIDDEN",
11515
11673
  message: "This organization uses a different encryption key. Ask a member for the key already in use, then set it up on this machine before syncing."
11516
11674
  });
@@ -11524,9 +11682,9 @@ var cloudSyncRouter = router({
11524
11682
  * to sync a project whose secrets will not open.
11525
11683
  */
11526
11684
  link: protectedProcedure.input(
11527
- z24.object({
11528
- orgId: z24.string().min(1).max(128),
11529
- cloudUrl: z24.string().url().max(2048)
11685
+ z23.object({
11686
+ orgId: z23.string().min(1).max(128),
11687
+ cloudUrl: z23.string().url().max(2048)
11530
11688
  })
11531
11689
  ).mutation(async ({ input }) => {
11532
11690
  await reconcileOrgCanary(input.orgId);
@@ -11535,7 +11693,7 @@ var cloudSyncRouter = router({
11535
11693
  return { success: true };
11536
11694
  }),
11537
11695
  /** Stop syncing an org and forget its token. */
11538
- unlink: protectedProcedure.input(z24.object({ orgId: z24.string().min(1).max(128) })).mutation(async ({ input }) => {
11696
+ unlink: protectedProcedure.input(z23.object({ orgId: z23.string().min(1).max(128) })).mutation(async ({ input }) => {
11539
11697
  cloudSyncBridge.disconnect(input.orgId);
11540
11698
  await cloudSyncStore.remove(input.orgId);
11541
11699
  return { success: true };
@@ -11572,7 +11730,6 @@ var appRouter = router({
11572
11730
  scenario: scenarioRouter,
11573
11731
  git: gitRouter,
11574
11732
  debug: debugRouter,
11575
- kanban: kanbanRouter,
11576
11733
  kanbanQueue: kanbanQueueRouter,
11577
11734
  terminal: terminalRouter,
11578
11735
  utility: utilityRouter,
@@ -11689,7 +11846,7 @@ async function ensureDataGitignore(dataDir) {
11689
11846
  // ../server/src/migrations/migrate-from-legacy.ts
11690
11847
  import { createRequire } from "module";
11691
11848
  import { randomBytes as randomBytes5, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
11692
- import { randomUUID as randomUUID11 } from "crypto";
11849
+ import { randomUUID as randomUUID12 } from "crypto";
11693
11850
  import {
11694
11851
  readFile as readFile16,
11695
11852
  writeFile as writeFile15,
@@ -11831,8 +11988,8 @@ async function atomicWrite(path, data) {
11831
11988
  await rename13(tmp, path);
11832
11989
  }
11833
11990
  async function runLegacyMigration(dataDir) {
11834
- const { resolve: resolve5 } = await import("path");
11835
- const abCwd = resolve5(process.cwd());
11991
+ const { resolve: resolve6 } = await import("path");
11992
+ const abCwd = resolve6(process.cwd());
11836
11993
  const legacyRoot = join21(abCwd, env.DATA_DIR);
11837
11994
  const legacyKeyPath = join21(legacyRoot, "dbs", "encryption-key.json");
11838
11995
  try {
@@ -11901,11 +12058,11 @@ async function runLegacyMigration(dataDir) {
11901
12058
  const data = await readEncrypted(join21(parsersDir, file));
11902
12059
  if (data?.id) legacyParsers.push(data);
11903
12060
  }
11904
- const projectMonoId = randomUUID11();
12061
+ const projectMonoId = randomUUID12();
11905
12062
  const envIdMap = {};
11906
12063
  const labelToKey = {};
11907
12064
  for (const [name, legacyEnv] of Object.entries(legacyEnvs)) {
11908
- envIdMap[name] = randomUUID11();
12065
+ envIdMap[name] = randomUUID12();
11909
12066
  if (legacyEnv?.label) labelToKey[legacyEnv.label] = name;
11910
12067
  }
11911
12068
  const runeEnvs = [];
@@ -11930,7 +12087,7 @@ async function runLegacyMigration(dataDir) {
11930
12087
  if (hasInvalidInterpolation(value)) continue;
11931
12088
  const cleaned = cleanVarKey(key);
11932
12089
  if (inheritedVars[cleaned] === value) continue;
11933
- const varId = randomUUID11();
12090
+ const varId = randomUUID12();
11934
12091
  variables[varId] = makeVariable(cleaned, value);
11935
12092
  varIdLookup.set(`${envIdMap[name]}:${cleaned}`, varId);
11936
12093
  }
@@ -11951,7 +12108,7 @@ async function runLegacyMigration(dataDir) {
11951
12108
  const serviceIdMap = {};
11952
12109
  const runeServices = [];
11953
12110
  for (const [name, legacy] of Object.entries(legacyServices)) {
11954
- const id = randomUUID11();
12111
+ const id = randomUUID12();
11955
12112
  serviceIdMap[name] = id;
11956
12113
  const rootPath = legacy.rootPath || "";
11957
12114
  const rootPathCwd = rootPath && rootPath !== "." ? rootPath : "";
@@ -11960,7 +12117,7 @@ async function runLegacyMigration(dataDir) {
11960
12117
  const parts = [cmd.spawnCmd, ...cmd.spawnArgs || []].filter(Boolean);
11961
12118
  if (!parts.length) continue;
11962
12119
  commands.push({
11963
- id: cmd.id || randomUUID11(),
12120
+ id: cmd.id || randomUUID12(),
11964
12121
  label: "Launch",
11965
12122
  command: parts.join(" "),
11966
12123
  cwd: cmd.spawnOptions?.cwd || "",
@@ -12021,7 +12178,7 @@ async function runLegacyMigration(dataDir) {
12021
12178
  const legacyBootstrapCmds = container.bootstrap?.commands || [];
12022
12179
  if (legacyBootstrapCmds.length > 0) {
12023
12180
  dockerConfig.bootstrapCommands = legacyBootstrapCmds.filter((bs) => bs.cmd || bs.entrypoint).map((bs, idx) => ({
12024
- id: bs.id || randomUUID11(),
12181
+ id: bs.id || randomUUID12(),
12025
12182
  label: bs.label || `Bootstrap ${idx + 1}`,
12026
12183
  command: bs.cmd || "",
12027
12184
  ...bs.entrypoint ? { entrypoint: bs.entrypoint } : {},
@@ -12069,7 +12226,7 @@ async function runLegacyMigration(dataDir) {
12069
12226
  cwd: serviceCwd,
12070
12227
  runner: dockerConfig ? "docker" : "native",
12071
12228
  shortcuts: (legacy.shortcuts || []).filter((s) => s.spawnCmd || s.label).map((s) => ({
12072
- id: s.id || randomUUID11(),
12229
+ id: s.id || randomUUID12(),
12073
12230
  label: s.label || s.spawnCmd || "",
12074
12231
  command: [s.spawnCmd, ...s.spawnArgs || []].filter(Boolean).join(" "),
12075
12232
  ...s.spawnOptions?.cwd ? { cwd: s.spawnOptions.cwd } : {}
@@ -12109,7 +12266,7 @@ async function runLegacyMigration(dataDir) {
12109
12266
  );
12110
12267
  if (!svcScopedEnv) {
12111
12268
  svcScopedEnv = {
12112
- id: randomUUID11(),
12269
+ id: randomUUID12(),
12113
12270
  projectId: projectMonoId,
12114
12271
  name: `${service?.name || name} (${globalEnv.name})`,
12115
12272
  scope: "service",
@@ -12133,7 +12290,7 @@ async function runLegacyMigration(dataDir) {
12133
12290
  const varPost = typeof varData === "object" ? String(varData.suffix ?? "") : "";
12134
12291
  if (!varKey || existingKeys.has(cleanVarKey(varKey))) continue;
12135
12292
  const cleaned = cleanVarKey(varKey);
12136
- const varId = randomUUID11();
12293
+ const varId = randomUUID12();
12137
12294
  svcScopedEnv.variables[varId] = makeVariable(cleaned, varValue, varPre, varPost);
12138
12295
  existingKeys.add(cleaned);
12139
12296
  }
@@ -12153,7 +12310,7 @@ async function runLegacyMigration(dataDir) {
12153
12310
  );
12154
12311
  if (!svcScopedEnv) {
12155
12312
  svcScopedEnv = {
12156
- id: randomUUID11(),
12313
+ id: randomUUID12(),
12157
12314
  projectId: projectMonoId,
12158
12315
  name: `${service?.name || serviceName} (${globalEnv.name})`,
12159
12316
  scope: "service",
@@ -12174,7 +12331,7 @@ async function runLegacyMigration(dataDir) {
12174
12331
  const baseEntry = Object.entries(svcScopedEnv.variables).find(([, v]) => v.key === cleaned);
12175
12332
  let varId = baseEntry?.[0];
12176
12333
  if (!varId) {
12177
- varId = randomUUID11();
12334
+ varId = randomUUID12();
12178
12335
  svcScopedEnv.variables[varId] = {
12179
12336
  key: cleaned,
12180
12337
  value: { value: "", pre: "", post: "", isSecret: false, injectIntoAi: false }
@@ -12204,13 +12361,13 @@ async function runLegacyMigration(dataDir) {
12204
12361
  flatOverrides[varId] = makeVariable(cleaned, value);
12205
12362
  }
12206
12363
  } else {
12207
- flatOverrides[randomUUID11()] = makeVariable(cleaned, value);
12364
+ flatOverrides[randomUUID12()] = makeVariable(cleaned, value);
12208
12365
  }
12209
12366
  } else {
12210
12367
  const cleaned = cleanVarKey(key);
12211
12368
  const existingVarId = Object.entries(env2.variables).find(([, v]) => v.key === cleaned)?.[0];
12212
12369
  if (!existingVarId) {
12213
- const varId = randomUUID11();
12370
+ const varId = randomUUID12();
12214
12371
  env2.variables[varId] = makeVariable(cleaned, value);
12215
12372
  varIdLookup.set(`${envId}:${cleaned}`, varId);
12216
12373
  }
@@ -12298,9 +12455,9 @@ async function findLegacyKeyInBackup(dataDir) {
12298
12455
  return null;
12299
12456
  }
12300
12457
  async function runOverrideOnlyMigration(dataDir) {
12301
- const { resolve: resolve5 } = await import("path");
12302
- const abDataDir = resolve5(dataDir);
12303
- const abCwd = resolve5(process.cwd());
12458
+ const { resolve: resolve6 } = await import("path");
12459
+ const abDataDir = resolve6(dataDir);
12460
+ const abCwd = resolve6(process.cwd());
12304
12461
  const candidateDirs = [
12305
12462
  join21(abCwd, ".runeya", "dbs", "overrides"),
12306
12463
  join21(abDataDir, "dbs", "overrides")
@@ -12395,7 +12552,7 @@ async function runOverrideOnlyMigration(dataDir) {
12395
12552
  if (isSameAsBase(value, baseVar)) continue;
12396
12553
  flatOverrides[varId] = makeVariable(cleaned, value);
12397
12554
  } else {
12398
- flatOverrides[randomUUID11()] = makeVariable(cleaned, value);
12555
+ flatOverrides[randomUUID12()] = makeVariable(cleaned, value);
12399
12556
  }
12400
12557
  }
12401
12558
  }
@@ -12432,7 +12589,7 @@ async function runOverrideOnlyMigration(dataDir) {
12432
12589
  );
12433
12590
  if (!svcScopedEnv) {
12434
12591
  svcScopedEnv = {
12435
- id: randomUUID11(),
12592
+ id: randomUUID12(),
12436
12593
  projectId: globalEnv["projectId"],
12437
12594
  name: `${service?.["name"] || serviceName} (${globalEnv["name"]})`,
12438
12595
  scope: "service",
@@ -12454,7 +12611,7 @@ async function runOverrideOnlyMigration(dataDir) {
12454
12611
  const baseEntry = Object.entries(svcScopedEnv["variables"]).find(([, v]) => v.key === cleaned);
12455
12612
  let varId = baseEntry?.[0];
12456
12613
  if (!varId) {
12457
- varId = randomUUID11();
12614
+ varId = randomUUID12();
12458
12615
  svcScopedEnv["variables"][varId] = {
12459
12616
  key: cleaned,
12460
12617
  value: { value: "", pre: "", post: "", isSecret: false, injectIntoAi: false }
@@ -12903,7 +13060,7 @@ async function createLocalServer() {
12903
13060
  }
12904
13061
  }
12905
13062
  if (!existsSync2(env.DATA_DIR)) await mkdir17(env.DATA_DIR);
12906
- const absoluteDataDir = resolve4(env.DATA_DIR);
13063
+ const absoluteDataDir = resolve5(env.DATA_DIR);
12907
13064
  console.log(`[server] Data directory: ${absoluteDataDir}`);
12908
13065
  await ensureDataGitignore(env.DATA_DIR);
12909
13066
  await settingsManager.getSettings();
@@ -13012,7 +13169,102 @@ async function createLocalServer() {
13012
13169
  res.status(502).send("Could not reach the Runeya cloud.");
13013
13170
  }
13014
13171
  });
13015
- app.get("/api/cloud/chat/stream", async (req, res) => {
13172
+ function proxyCloudStream(localPath, upstreamPath) {
13173
+ app.get(localPath, async (req, res) => {
13174
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13175
+ try {
13176
+ await verifyJwt(jwt);
13177
+ } catch {
13178
+ res.status(401).end();
13179
+ return;
13180
+ }
13181
+ const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13182
+ if (!organizationId) {
13183
+ res.status(400).end();
13184
+ return;
13185
+ }
13186
+ const token = await cloudSessionStore.getToken();
13187
+ if (!token) {
13188
+ res.status(401).end();
13189
+ return;
13190
+ }
13191
+ const upstreamAbort = new AbortController();
13192
+ req.on("close", () => upstreamAbort.abort());
13193
+ try {
13194
+ const upstream = await fetch(
13195
+ `${getCloudApiUrl()}${upstreamPath}?organizationId=${encodeURIComponent(organizationId)}`,
13196
+ {
13197
+ headers: { Authorization: `Bearer ${token}`, Accept: "text/event-stream" },
13198
+ signal: upstreamAbort.signal
13199
+ }
13200
+ );
13201
+ if (!upstream.ok || !upstream.body) {
13202
+ res.status(upstream.status === 401 ? 401 : 502).end();
13203
+ return;
13204
+ }
13205
+ res.writeHead(200, {
13206
+ "Content-Type": "text/event-stream",
13207
+ "Cache-Control": "no-cache",
13208
+ Connection: "keep-alive",
13209
+ "X-Accel-Buffering": "no"
13210
+ });
13211
+ const reader = upstream.body.getReader();
13212
+ for (; ; ) {
13213
+ const { done, value } = await reader.read();
13214
+ if (done) break;
13215
+ res.write(value);
13216
+ }
13217
+ res.end();
13218
+ } catch {
13219
+ if (!res.headersSent) res.status(502).end();
13220
+ else res.end();
13221
+ }
13222
+ });
13223
+ }
13224
+ app.post(
13225
+ "/api/cloud/kanban/image",
13226
+ express.raw({ type: () => true, limit: "64mb" }),
13227
+ async (req, res) => {
13228
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13229
+ try {
13230
+ await verifyJwt(jwt);
13231
+ } catch {
13232
+ res.status(401).end();
13233
+ return;
13234
+ }
13235
+ const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13236
+ if (!organizationId) {
13237
+ res.status(400).end();
13238
+ return;
13239
+ }
13240
+ const token = await cloudSessionStore.getToken();
13241
+ if (!token) {
13242
+ res.status(401).end();
13243
+ return;
13244
+ }
13245
+ const upstreamQuery = new URLSearchParams({ organizationId });
13246
+ if (typeof req.query["name"] === "string") upstreamQuery.set("name", req.query["name"]);
13247
+ try {
13248
+ const upstream = await fetch(
13249
+ `${getCloudApiUrl()}/api/kanban/image?${upstreamQuery.toString()}`,
13250
+ {
13251
+ method: "POST",
13252
+ headers: {
13253
+ Authorization: `Bearer ${token}`,
13254
+ "Content-Type": String(req.headers["content-type"] ?? "application/octet-stream")
13255
+ },
13256
+ body: req.body,
13257
+ // A 55 MB body over a home upstream link takes minutes, not seconds.
13258
+ signal: AbortSignal.timeout(3e5)
13259
+ }
13260
+ );
13261
+ res.status(upstream.status).json(await upstream.json().catch(() => ({})));
13262
+ } catch {
13263
+ res.status(502).json({ error: "upload-failed" });
13264
+ }
13265
+ }
13266
+ );
13267
+ app.delete("/api/cloud/kanban/file/:id", async (req, res) => {
13016
13268
  const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13017
13269
  try {
13018
13270
  await verifyJwt(jwt);
@@ -13020,9 +13272,27 @@ async function createLocalServer() {
13020
13272
  res.status(401).end();
13021
13273
  return;
13022
13274
  }
13023
- const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13024
- if (!organizationId) {
13025
- res.status(400).end();
13275
+ const token = await cloudSessionStore.getToken();
13276
+ if (!token) {
13277
+ res.status(401).end();
13278
+ return;
13279
+ }
13280
+ try {
13281
+ const upstream = await fetch(
13282
+ `${getCloudApiUrl()}/api/kanban/file/${encodeURIComponent(req.params.id)}`,
13283
+ { method: "DELETE", headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(15e3) }
13284
+ );
13285
+ res.status(upstream.status).end();
13286
+ } catch {
13287
+ res.status(502).end();
13288
+ }
13289
+ });
13290
+ app.get("/api/cloud/kanban/file/:id", async (req, res) => {
13291
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13292
+ try {
13293
+ await verifyJwt(jwt);
13294
+ } catch {
13295
+ res.status(401).end();
13026
13296
  return;
13027
13297
  }
13028
13298
  const token = await cloudSessionStore.getToken();
@@ -13030,38 +13300,27 @@ async function createLocalServer() {
13030
13300
  res.status(401).end();
13031
13301
  return;
13032
13302
  }
13033
- const upstreamAbort = new AbortController();
13034
- req.on("close", () => upstreamAbort.abort());
13035
13303
  try {
13036
13304
  const upstream = await fetch(
13037
- `${getCloudApiUrl()}/api/chat/stream?organizationId=${encodeURIComponent(organizationId)}`,
13038
- {
13039
- headers: { Authorization: `Bearer ${token}`, Accept: "text/event-stream" },
13040
- signal: upstreamAbort.signal
13041
- }
13305
+ `${getCloudApiUrl()}/api/kanban/file/${encodeURIComponent(req.params.id)}`,
13306
+ { headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(3e5) }
13042
13307
  );
13043
- if (!upstream.ok || !upstream.body) {
13044
- res.status(upstream.status === 401 ? 401 : 502).end();
13308
+ if (!upstream.ok) {
13309
+ res.status(upstream.status).end();
13045
13310
  return;
13046
13311
  }
13047
- res.writeHead(200, {
13048
- "Content-Type": "text/event-stream",
13049
- "Cache-Control": "no-cache",
13050
- Connection: "keep-alive",
13051
- "X-Accel-Buffering": "no"
13052
- });
13053
- const reader = upstream.body.getReader();
13054
- for (; ; ) {
13055
- const { done, value } = await reader.read();
13056
- if (done) break;
13057
- res.write(value);
13058
- }
13059
- res.end();
13312
+ res.set("Content-Type", upstream.headers.get("content-type") ?? "application/octet-stream");
13313
+ const disposition = upstream.headers.get("content-disposition");
13314
+ if (disposition) res.set("Content-Disposition", disposition);
13315
+ res.set("X-Content-Type-Options", "nosniff");
13316
+ res.set("Content-Security-Policy", "default-src 'none'; sandbox");
13317
+ res.send(Buffer.from(await upstream.arrayBuffer()));
13060
13318
  } catch {
13061
- if (!res.headersSent) res.status(502).end();
13062
- else res.end();
13319
+ res.status(502).end();
13063
13320
  }
13064
13321
  });
13322
+ proxyCloudStream("/api/cloud/chat/stream", "/api/chat/stream");
13323
+ proxyCloudStream("/api/cloud/kanban/stream", "/api/kanban/stream");
13065
13324
  app.post("/api/unlock", authLimiter, csrfMiddleware, express.json({ limit: "10kb" }), async (req, res) => {
13066
13325
  try {
13067
13326
  const body = req.body;
@@ -13242,7 +13501,18 @@ async function createLocalServer() {
13242
13501
  });
13243
13502
  const CONV_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
13244
13503
  const IMAGE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13245
- app.post("/api/images/upload", upload.single("image"), async (req, res) => {
13504
+ const uploadSingleImage = (req, res, next) => {
13505
+ upload.single("image")(req, res, (err) => {
13506
+ if (err) {
13507
+ const isTooLarge = err.code === "LIMIT_FILE_SIZE";
13508
+ const message = err instanceof Error ? err.message : "Upload failed";
13509
+ res.status(isTooLarge ? 413 : 400).json({ error: message });
13510
+ return;
13511
+ }
13512
+ next();
13513
+ });
13514
+ };
13515
+ app.post("/api/images/upload", uploadSingleImage, async (req, res) => {
13246
13516
  try {
13247
13517
  if (!req.file) {
13248
13518
  res.status(400).json({ error: "No image file provided" });
@@ -13280,7 +13550,7 @@ async function createLocalServer() {
13280
13550
  res.status(400).json({ error: "Invalid imageId" });
13281
13551
  return;
13282
13552
  }
13283
- const result = await imageStorageService.getImagePath(imageId);
13553
+ const result = await imageStorageService.getImagePath(imageId, conversationId);
13284
13554
  if (!result) {
13285
13555
  res.status(404).json({ error: "Image not found" });
13286
13556
  return;
@@ -13362,6 +13632,7 @@ async function createLocalServer() {
13362
13632
  const shutdown = async () => {
13363
13633
  console.log("[server] Shutting down...");
13364
13634
  clearInterval(wsTickInterval);
13635
+ stopClaudeTranscriptWatcher();
13365
13636
  imageCleanupService.stop();
13366
13637
  cloudSyncBridge.stopAll();
13367
13638
  await Promise.all([
@@ -13370,12 +13641,12 @@ async function createLocalServer() {
13370
13641
  ]);
13371
13642
  server.closeAllConnections();
13372
13643
  await Promise.race([
13373
- new Promise((resolve5) => wss.close(() => resolve5())),
13374
- new Promise((resolve5) => setTimeout(resolve5, 2e3))
13644
+ new Promise((resolve6) => wss.close(() => resolve6())),
13645
+ new Promise((resolve6) => setTimeout(resolve6, 2e3))
13375
13646
  ]);
13376
13647
  await Promise.race([
13377
- new Promise((resolve5) => server.close(() => resolve5())),
13378
- new Promise((resolve5) => setTimeout(resolve5, 2e3))
13648
+ new Promise((resolve6) => server.close(() => resolve6())),
13649
+ new Promise((resolve6) => setTimeout(resolve6, 2e3))
13379
13650
  ]);
13380
13651
  console.log("[server] Shutdown complete");
13381
13652
  };
@@ -13396,7 +13667,7 @@ var PullEnvError = class extends Error {
13396
13667
  };
13397
13668
  async function promptPassword() {
13398
13669
  process.stderr.write("Master password: ");
13399
- return new Promise((resolve5) => {
13670
+ return new Promise((resolve6) => {
13400
13671
  const stdin = process.stdin;
13401
13672
  const wasRaw = stdin.isRaw;
13402
13673
  if (stdin.isTTY) stdin.setRawMode(true);
@@ -13409,7 +13680,7 @@ async function promptPassword() {
13409
13680
  stdin.pause();
13410
13681
  stdin.removeListener("data", onData);
13411
13682
  process.stderr.write("\n");
13412
- resolve5(input);
13683
+ resolve6(input);
13413
13684
  } else if (ch === "") {
13414
13685
  process.exit(1);
13415
13686
  } else if (ch === "\x7F" || ch === "\b") {
@@ -13466,13 +13737,13 @@ async function pullEnv(opts) {
13466
13737
 
13467
13738
  // ../server/src/index.ts
13468
13739
  dotenv.config({ quiet: true });
13469
- if (process.env["DATA_DIR"]) {
13470
- const resolved = resolvePath2(process.env["DATA_DIR"], "..");
13740
+ if (process.env["RUNEYA_DATA_DIR"]) {
13741
+ const resolved = resolvePath2(process.env["RUNEYA_DATA_DIR"], "..");
13471
13742
  process.chdir(resolved);
13472
- console.log(`[server] Working directory: ${resolved} (from DATA_DIR)`);
13743
+ console.log(`[server] Working directory: ${resolved} (from RUNEYA_DATA_DIR)`);
13473
13744
  }
13474
13745
  function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500) {
13475
- return new Promise((resolve5, reject) => {
13746
+ return new Promise((resolve6, reject) => {
13476
13747
  let attempts = 0;
13477
13748
  const wssErrorHandler = () => {
13478
13749
  };
@@ -13493,7 +13764,7 @@ function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500)
13493
13764
  server.listen(port, host, () => {
13494
13765
  server.removeListener("error", onError);
13495
13766
  wss?.removeListener("error", wssErrorHandler);
13496
- resolve5();
13767
+ resolve6();
13497
13768
  });
13498
13769
  };
13499
13770
  tryListen();
@@ -13540,4 +13811,4 @@ export {
13540
13811
  createLocalServer,
13541
13812
  pullEnv
13542
13813
  };
13543
- //# sourceMappingURL=src-K32WI425.js.map
13814
+ //# sourceMappingURL=src-3TOPUVAK.js.map