@runeya/runeya 2.0.21 → 2.0.24

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-EGBDYHL5.js} +1540 -1119
  10. package/src-EGBDYHL5.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,608 @@ 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
+ async function executeKanbanCommands(boardId, cardId, commands, conversationId) {
5675
+ if (commands.length === 0) return;
5676
+ let board = await kanbanStore.get(boardId);
5677
+ if (!board) {
5678
+ console.warn(`[kanban-commands] Board ${boardId} not found, skipping ${commands.length} command(s)`);
5679
+ return;
5680
+ }
5681
+ const cardExistsInBoard = () => board.columns.some((col) => col.cards.some((c) => c.id === cardId));
5682
+ if (!cardExistsInBoard()) {
5683
+ for (let attempt = 1; attempt <= 5; attempt++) {
5684
+ console.warn(`[kanban-commands] Card ${cardId} not found in board, retry ${attempt}/5...`);
5685
+ await new Promise((r) => setTimeout(r, 500 * attempt));
5686
+ board = await kanbanStore.get(boardId);
5687
+ if (!board) return;
5688
+ if (cardExistsInBoard()) break;
5689
+ }
5690
+ if (!cardExistsInBoard()) {
5691
+ console.error(`[kanban-commands] Card ${cardId} still not found after retries, skipping ${commands.length} command(s)`);
5692
+ return;
5693
+ }
5694
+ }
5695
+ for (const cmd of commands) {
5696
+ if (cmd.type === "move_card" && cmd.columnTitle) {
5697
+ const moved = moveCard(board, cardId, cmd.columnTitle);
5698
+ if (!moved) console.warn(`[kanban-commands] move_card failed: card=${cardId} target="${cmd.columnTitle}"`);
5699
+ } else if (cmd.type === "add_comment" && cmd.comment) {
5700
+ addComment(board, cardId, cmd.comment, conversationId);
5701
+ } else if (cmd.type === "add_card" && cmd.columnTitle && cmd.cardTitle) {
5702
+ addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);
5703
+ }
5704
+ }
5705
+ await kanbanStore.update({ id: board.id, columns: board.columns });
5706
+ }
5707
+ async function executeKanbanBoardCommands(boardId, commands) {
5708
+ if (commands.length === 0) return;
5709
+ const board = await kanbanStore.get(boardId);
5710
+ if (!board) return;
5711
+ for (const cmd of commands) {
5712
+ if (cmd.type === "add_card" && cmd.columnTitle && cmd.cardTitle) {
5713
+ addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);
5714
+ }
5715
+ }
5716
+ await kanbanStore.update({ id: board.id, columns: board.columns });
5717
+ }
5718
+ function moveCard(board, cardId, targetColTitle) {
5719
+ let sourceCol = "";
5720
+ let card = null;
5721
+ let sourceColIdx = -1;
5722
+ let cardIdx = -1;
5723
+ for (let ci = 0; ci < board.columns.length; ci++) {
5724
+ const col = board.columns[ci];
5725
+ const idx = col.cards.findIndex((c) => c.id === cardId);
5726
+ if (idx !== -1) {
5727
+ sourceCol = col.title;
5728
+ card = col.cards[idx];
5729
+ sourceColIdx = ci;
5730
+ cardIdx = idx;
5731
+ break;
5732
+ }
5733
+ }
5734
+ if (!card || sourceColIdx === -1) return false;
5735
+ const targetCol = board.columns.find(
5736
+ (c) => c.title.toLowerCase() === targetColTitle.toLowerCase()
5737
+ );
5738
+ if (!targetCol) {
5739
+ console.warn(`[kanban-commands] Target column "${targetColTitle}" not found. Available: ${board.columns.map((c) => c.title).join(", ")}`);
5740
+ return false;
5741
+ }
5742
+ if (targetCol.id === board.columns[sourceColIdx].id) return true;
5743
+ board.columns[sourceColIdx].cards.splice(cardIdx, 1);
5744
+ card.order = targetCol.cards.length;
5745
+ card.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5746
+ card.history = [...card.history ?? [], {
5747
+ type: "moved",
5748
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5749
+ detail: `"${sourceCol}" \u2192 "${targetCol.title}"`,
5750
+ author: "ai"
5751
+ }];
5752
+ targetCol.cards.push(card);
5753
+ return true;
5754
+ }
5755
+ function addCardToColumn(board, columnTitle, cardTitle, cardContent) {
5756
+ const col = board.columns.find((c) => c.title.toLowerCase() === columnTitle.toLowerCase());
5757
+ if (!col) return;
5758
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5759
+ const card = {
5760
+ id: randomUUID5(),
5761
+ title: cardTitle,
5762
+ markdown: cardContent ?? "",
5763
+ images: [],
5764
+ order: col.cards.length,
5765
+ history: [{ type: "created", timestamp: now, detail: `AI \u2014 In column "${col.title}"` }],
5766
+ createdAt: now,
5767
+ updatedAt: now
5768
+ };
5769
+ col.cards.push(card);
5770
+ }
5771
+ function addComment(board, cardId, comment, conversationId) {
5772
+ for (const col of board.columns) {
5773
+ const card = col.cards.find((c) => c.id === cardId);
5774
+ if (card) {
5775
+ card.history = [...card.history ?? [], {
5776
+ type: "comment",
5777
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5778
+ detail: comment,
5779
+ author: "ai",
5780
+ ...conversationId ? { conversationId } : {}
5781
+ }];
5782
+ card.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5783
+ return;
5784
+ }
5785
+ }
5786
+ }
5787
+ function generateKanbanSystemPrompt(board, cardId) {
5788
+ let currentColTitle = "";
5789
+ let card = null;
5790
+ for (const col of board.columns) {
5791
+ const found = col.cards.find((c) => c.id === cardId);
5792
+ if (found) {
5793
+ currentColTitle = col.title;
5794
+ card = found;
5795
+ break;
5796
+ }
5797
+ }
5798
+ const sortedColumns = [...board.columns].sort((a, b) => a.order - b.order);
5799
+ const lines = [];
5800
+ lines.push("## Kanban Board Context");
5801
+ lines.push("");
5802
+ lines.push(`Tu travailles dans le contexte d'une carte Kanban.`);
5803
+ lines.push(`- **Board** : ${board.name}`);
5804
+ if (card) {
5805
+ lines.push(`- **Carte** : ${card.title}`);
5806
+ lines.push(`- **Colonne actuelle** : \`${currentColTitle}\``);
5807
+ }
5808
+ lines.push("");
5809
+ lines.push("### Colonnes disponibles (noms exacts \xE0 utiliser) :");
5810
+ for (const col of sortedColumns) {
5811
+ const cardCount = col.cards.length;
5812
+ const isCurrent = card && col.cards.some((c) => c.id === cardId);
5813
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})${isCurrent ? " \u2190 **colonne actuelle**" : ""}`);
5814
+ }
5815
+ lines.push("");
5816
+ if (card) {
5817
+ const comments = (card.history ?? []).filter((e) => e.type === "comment");
5818
+ if (comments.length > 0) {
5819
+ lines.push("### Commentaires existants sur cette carte :");
5820
+ lines.push("");
5821
+ for (const c of comments.slice(-20)) {
5822
+ const who = c.author === "ai" ? "AI" : "Utilisateur";
5823
+ const date = new Date(c.timestamp).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
5824
+ lines.push(`- **${who}** (${date}) : ${c.detail}`);
5825
+ }
5826
+ lines.push("");
5827
+ }
5828
+ }
5829
+ lines.push("### Outils Kanban disponibles");
5830
+ lines.push("");
5831
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
5832
+ if (card) lines.push(`- **cardId** (carte actuelle) : \`${card.id}\``);
5833
+ lines.push("");
5834
+ lines.push("Tu disposes des outils suivants pour interagir avec le board :");
5835
+ lines.push("- `kanban_move_card(boardId, cardId, targetColumn)` \u2014 D\xE9placer la carte vers une autre colonne");
5836
+ lines.push("- `kanban_add_comment(boardId, cardId, comment)` \u2014 Ajouter un commentaire sur la carte");
5837
+ lines.push("");
5838
+ lines.push("### OBLIGATOIRE \u2014 D\xE9cision Kanban");
5839
+ lines.push("");
5840
+ lines.push("\xC0 chaque r\xE9ponse, tu DOIS obligatoirement faire les deux actions suivantes :");
5841
+ lines.push("");
5842
+ lines.push("**1. \xC9valuer et d\xE9placer la carte si n\xE9cessaire** via `kanban_move_card` :");
5843
+ lines.push(" - **Au d\xE9but** si tu sais d\xE9j\xE0 que tu vas travailler activement (ex. tu passes en `In progress`).");
5844
+ lines.push(" - **\xC0 la fin** pour une d\xE9cision forte apr\xE8s analyse compl\xE8te (ex. tu conclus que c'est `Done`).");
5845
+ lines.push(" - Si tu ne fais qu'analyser ou poser des questions \u2192 **ne pas d\xE9placer**.");
5846
+ lines.push("");
5847
+ lines.push(" > \u26A0\uFE0F **CRITIQUE** : En fin de r\xE9ponse, tu DOIS TOUJOURS \xE9valuer si la colonne actuelle est encore correcte.");
5848
+ lines.push("");
5849
+ lines.push("**2. Appeler `kanban_add_comment`** r\xE9sumant ce que tu as fait ou constat\xE9 (**\xE0 la fin** de ta r\xE9ponse).");
5850
+ lines.push("");
5851
+ lines.push("> \u{1F6AB} **INTERDIT** : Terminer une r\xE9ponse sans appeler `kanban_add_comment`. C'est non n\xE9gociable.");
5852
+ lines.push("");
5853
+ return lines.join("\n");
5854
+ }
5855
+ function generateKanbanPopulatePrompt(board) {
5856
+ const lines = [];
5857
+ lines.push("## Kanban Board \u2014 Mode Populate");
5858
+ lines.push("");
5859
+ lines.push(`Tu es en mode **populate** sur le tableau Kanban **"${board.name}"**.`);
5860
+ 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.`);
5861
+ lines.push("");
5862
+ lines.push("Ton UNIQUE r\xF4le est de cr\xE9er des cartes Kanban pertinentes en r\xE9ponse \xE0 la demande de l'utilisateur.");
5863
+ lines.push("Tu ne dois PAS r\xE9pondre \xE0 la question, ni donner d'explication, ni \xE9crire du code, ni montrer de composant.");
5864
+ lines.push("Tu dois UNIQUEMENT produire des tags XML `<kanban_add_card>` pour cr\xE9er des cartes dans le tableau.");
5865
+ if (board.description) {
5866
+ lines.push("");
5867
+ lines.push(`Description du board : ${board.description}`);
5868
+ }
5869
+ lines.push("");
5870
+ const sortedCols = [...board.columns].sort((a, b) => a.order - b.order);
5871
+ lines.push("### Colonnes existantes (noms exacts \xE0 utiliser) :");
5872
+ for (const col of sortedCols) {
5873
+ const cardCount = col.cards.length;
5874
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})`);
5875
+ for (const card of col.cards) {
5876
+ lines.push(` - ${card.title}`);
5877
+ }
5878
+ }
5879
+ lines.push("");
5880
+ lines.push("### Outil disponible");
5881
+ lines.push("");
5882
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
5883
+ lines.push("");
5884
+ lines.push("Pour chaque carte \xE0 cr\xE9er, appelle l'outil `kanban_add_card(boardId, column, title, content)` :");
5885
+ lines.push("- `column` : nom exact de la colonne cible");
5886
+ lines.push("- `title` : titre concis de la carte");
5887
+ lines.push("- `content` : contenu markdown d\xE9taill\xE9 et actionnable");
5888
+ lines.push("");
5889
+ lines.push("### R\xE8gles STRICTES");
5890
+ lines.push("");
5891
+ lines.push("1. **NE R\xC9PONDS PAS** \xE0 la question de l'utilisateur. Ne donne aucune explication, aucun texte libre.");
5892
+ lines.push("2. **Appelle uniquement** `kanban_add_card`, rien d'autre.");
5893
+ lines.push("3. Tu ne peux PAS cr\xE9er de nouvelles colonnes. Dispatch les cartes dans les colonnes existantes list\xE9es ci-dessus.");
5894
+ lines.push("4. Utilise les noms **exacts** des colonnes.");
5895
+ lines.push("5. Chaque carte doit avoir un titre concis et un contenu markdown d\xE9taill\xE9 et actionnable.");
5896
+ lines.push("6. D\xE9compose la demande de l'utilisateur en t\xE2ches concr\xE8tes, chacune \xE9tant une carte.");
5897
+ lines.push("7. Dispatch chaque carte dans la colonne la plus appropri\xE9e selon sa nature.");
5898
+ lines.push("");
5899
+ return lines.join("\n");
5900
+ }
5901
+
5902
+ // ../server/src/services/ai-runners/claude-code-runner.ts
5903
+ import { spawn as spawn2 } from "child_process";
5904
+
5905
+ // ../server/src/services/ai-runners/scenario-tokens.ts
5906
+ function parseEndToken(output) {
5907
+ const tail = output.trimEnd().slice(-2e3);
5908
+ const branchMatch = tail.match(/<pass\s+branch="(\d+)"\s*\/>/);
5909
+ if (branchMatch) {
5910
+ return { status: "pass", message: "", branchChoice: Number.parseInt(branchMatch[1], 10) };
5911
+ }
5912
+ if (/<pass\s*\/>/.test(tail)) {
5913
+ return { status: "pass", message: "" };
5914
+ }
5915
+ const errorMatch = tail.match(/<error>([\s\S]*?)<\/error>/);
5916
+ if (errorMatch) {
5917
+ return { status: "error", message: errorMatch[1].trim() };
5918
+ }
5919
+ const askMatch = tail.match(/<ask_user>([\s\S]*?)<\/ask_user>/);
5920
+ if (askMatch) {
5921
+ return { status: "ask_user", message: askMatch[1].trim() };
5922
+ }
5923
+ return { status: "none", message: "" };
5924
+ }
5925
+ var TOKEN_INSTRUCTIONS = `
5926
+ ## Instructions de signalisation (OBLIGATOIRE)
5927
+
5928
+ \xC0 la fin de CHAQUE r\xE9ponse, tu DOIS terminer par exactement l'un de ces tags XML :
5929
+
5930
+ - Si tu as termin\xE9 avec succ\xE8s (chemin par d\xE9faut) : \`<pass />\`
5931
+ - 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)
5932
+ - Si tu as rencontr\xE9 une erreur bloquante : \`<error>description courte</error>\`
5933
+ - 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 ?")
5934
+
5935
+ Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9ponse.
5936
+ `.trim();
5937
+
5938
+ // ../server/src/services/ai-runners/claude-pty-runner.ts
5939
+ import { randomUUID as randomUUID6 } from "crypto";
5940
+
5941
+ // ../server/src/services/ai-runners/claude-pty-tail.ts
5942
+ import { open } from "fs/promises";
5943
+ import { watch as watch2 } from "fs";
5944
+ async function waitForFile(sessionId, signal, timeoutMs) {
5945
+ const start = Date.now();
5946
+ while (Date.now() - start < timeoutMs) {
5947
+ if (signal.aborted) throw new Error("Aborted");
5948
+ const path = await findClaudeSessionFile(sessionId);
5949
+ if (path) return path;
5950
+ await new Promise((r) => setTimeout(r, 150));
5951
+ }
5952
+ throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);
5953
+ }
5954
+ function tailTranscript(opts) {
5955
+ const { sessionId, signal, onEntry, fileAppearTimeoutMs = 3e4 } = opts;
5956
+ let stopped = false;
5957
+ let watcher = null;
5958
+ let fh = null;
5959
+ let offset = 0;
5960
+ let pendingBuf = "";
5961
+ let reading = false;
5962
+ let queuedRead = false;
5963
+ const cleanup = async () => {
5964
+ stopped = true;
5965
+ if (watcher) {
5966
+ try {
5967
+ watcher.close();
5968
+ } catch {
5969
+ }
5970
+ watcher = null;
5971
+ }
5972
+ if (fh) {
5973
+ try {
5974
+ await fh.close();
5975
+ } catch {
5976
+ }
5977
+ fh = null;
5978
+ }
5979
+ };
5980
+ const drainBuffer = () => {
5981
+ let idx;
5982
+ while ((idx = pendingBuf.indexOf("\n")) >= 0) {
5983
+ const line = pendingBuf.slice(0, idx).trim();
5984
+ pendingBuf = pendingBuf.slice(idx + 1);
5985
+ if (!line) continue;
5986
+ try {
5987
+ const entry = JSON.parse(line);
5988
+ onEntry(entry);
5989
+ } catch {
5990
+ }
5991
+ }
5992
+ };
5993
+ const readMore = async () => {
5994
+ if (!fh || stopped) return;
5995
+ if (reading) {
5996
+ queuedRead = true;
5997
+ return;
5998
+ }
5999
+ reading = true;
6000
+ try {
6001
+ while (!stopped) {
6002
+ const st = await fh.stat();
6003
+ if (st.size <= offset) break;
6004
+ const len = st.size - offset;
6005
+ const buf = Buffer.alloc(len);
6006
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
5308
6007
  if (bytesRead <= 0) break;
5309
6008
  offset += bytesRead;
5310
6009
  pendingBuf += buf.slice(0, bytesRead).toString("utf-8");
@@ -5337,12 +6036,12 @@ function tailTranscript(opts) {
5337
6036
  const poll = setInterval(() => {
5338
6037
  void readMore();
5339
6038
  }, 500);
5340
- await new Promise((resolve5) => {
6039
+ await new Promise((resolve6) => {
5341
6040
  const tick = setInterval(() => {
5342
6041
  if (stopped) {
5343
6042
  clearInterval(tick);
5344
6043
  clearInterval(poll);
5345
- resolve5();
6044
+ resolve6();
5346
6045
  }
5347
6046
  }, 100);
5348
6047
  });
@@ -5361,7 +6060,7 @@ function tailTranscript(opts) {
5361
6060
 
5362
6061
  // ../server/src/services/ai-runners/claude-pty-runner.ts
5363
6062
  async function getAgentManager() {
5364
- const { agentManager: agentManager2 } = await import("./agent-manager-QPZUYLJA.js");
6063
+ const { agentManager: agentManager2 } = await import("./agent-manager-2P4THUVA.js");
5365
6064
  return agentManager2;
5366
6065
  }
5367
6066
  function buildPtyArgs(params, sessionId, opts) {
@@ -5431,7 +6130,7 @@ function dispatchEntry(entry, state, cbs) {
5431
6130
  async function runClaudeViaPty(params) {
5432
6131
  const { agentId, signal, skipTokenInstructions } = params;
5433
6132
  if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
5434
- const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID5();
6133
+ const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID6();
5435
6134
  const args = buildPtyArgs(params, sessionId, { excludeMcp: true });
5436
6135
  const agentMgr = await getAgentManager();
5437
6136
  const displayArgs = args.map((a) => {
@@ -5440,7 +6139,7 @@ async function runClaudeViaPty(params) {
5440
6139
  return a;
5441
6140
  });
5442
6141
  params.onCommand?.(`claude ${displayArgs.join(" ")}`);
5443
- return new Promise((resolve5, reject) => {
6142
+ return new Promise((resolve6, reject) => {
5444
6143
  let settled = false;
5445
6144
  let spawnId = null;
5446
6145
  const state = { assistantText: "", tools: /* @__PURE__ */ new Map() };
@@ -5463,7 +6162,7 @@ async function runClaudeViaPty(params) {
5463
6162
  settled = true;
5464
6163
  cleanup();
5465
6164
  void killSpawn();
5466
- resolve5(result);
6165
+ resolve6(result);
5467
6166
  };
5468
6167
  const fail = (err) => {
5469
6168
  if (settled) return;
@@ -5536,10 +6235,6 @@ async function runClaudeViaPty(params) {
5536
6235
  }
5537
6236
 
5538
6237
  // ../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
6238
  function createClaudeStreamState() {
5544
6239
  return {
5545
6240
  fullContent: "",
@@ -5762,116 +6457,16 @@ function processClaudeStdoutLines(lines, streamState, params, ctx) {
5762
6457
  }
5763
6458
  var ClaudeCodeRunner = class {
5764
6459
  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
6460
  if (params.agentId) {
5770
- return this.runViaAgent(params);
6461
+ return runClaudeViaPty(params);
5771
6462
  }
5772
6463
  return this.runLocal(params);
5773
6464
  }
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
6465
  // ── Run locally (original implementation) ───────────────────────────────
5871
6466
  runLocal(params) {
5872
6467
  const { signal, skipTokenInstructions } = params;
5873
6468
  const { args, env: env2, stdinData, sessionArg, hasImages: _hasImages } = buildClaudeSpawnConfig(params);
5874
- return new Promise((resolve5, reject) => {
6469
+ return new Promise((resolve6, reject) => {
5875
6470
  const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
5876
6471
  console.log(`[claude-code-runner] arg size: ${totalArgSize} bytes (${(totalArgSize / 1024 / 1024).toFixed(2)} MB), session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
5877
6472
  const displayArgs = args.map((a) => {
@@ -5913,7 +6508,7 @@ var ClaudeCodeRunner = class {
5913
6508
  });
5914
6509
  proc.on("close", (code) => {
5915
6510
  if (ctx.earlyResult) {
5916
- resolve5({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
6511
+ resolve6({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
5917
6512
  return;
5918
6513
  }
5919
6514
  signal.removeEventListener("abort", onAbort);
@@ -5930,7 +6525,7 @@ var ClaudeCodeRunner = class {
5930
6525
  return;
5931
6526
  }
5932
6527
  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 });
6528
+ resolve6({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });
5934
6529
  });
5935
6530
  proc.on("error", (err) => {
5936
6531
  if (ctx.settled) return;
@@ -5986,12 +6581,12 @@ var CodexAppServerClient = class extends EventEmitter3 {
5986
6581
  if (forceId !== void 0 && forceId >= this.nextId) {
5987
6582
  this.nextId = forceId + 1;
5988
6583
  }
5989
- return new Promise((resolve5, reject) => {
6584
+ return new Promise((resolve6, reject) => {
5990
6585
  const timer = setTimeout(() => {
5991
6586
  this.pending.delete(id);
5992
6587
  reject(new Error(`Request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`));
5993
6588
  }, this.requestTimeoutMs);
5994
- this.pending.set(id, { resolve: resolve5, reject, timer });
6589
+ this.pending.set(id, { resolve: resolve6, reject, timer });
5995
6590
  this.writeLine({ jsonrpc: "2.0", method, id, params: params ?? {} });
5996
6591
  });
5997
6592
  }
@@ -6023,8 +6618,8 @@ var CodexAppServerClient = class extends EventEmitter3 {
6023
6618
  this.rl = null;
6024
6619
  const proc = this.proc;
6025
6620
  this.proc = null;
6026
- return new Promise((resolve5) => {
6027
- proc.on("exit", () => resolve5());
6621
+ return new Promise((resolve6) => {
6622
+ proc.on("exit", () => resolve6());
6028
6623
  proc.kill("SIGTERM");
6029
6624
  const forceTimer = setTimeout(() => {
6030
6625
  proc.kill("SIGKILL");
@@ -6117,8 +6712,8 @@ async function shutdownCodexAppServer() {
6117
6712
  await client.shutdown();
6118
6713
  }
6119
6714
  }
6120
- async function getAgentManager3() {
6121
- const { agentManager: agentManager2 } = await import("./agent-manager-QPZUYLJA.js");
6715
+ async function getAgentManager2() {
6716
+ const { agentManager: agentManager2 } = await import("./agent-manager-2P4THUVA.js");
6122
6717
  return agentManager2;
6123
6718
  }
6124
6719
  function parseRuneyaCurlCommand(command) {
@@ -6187,7 +6782,7 @@ var CodexRunner = class {
6187
6782
  // ── Run via agent (app-server JSON-RPC via agent tRPC routes) ───────────
6188
6783
  async runViaAgent(params) {
6189
6784
  const { agentId, prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken, injectedEnv } = params;
6190
- const agentMgr = await getAgentManager3();
6785
+ const agentMgr = await getAgentManager2();
6191
6786
  if (isResume && params.sessionId && !this.lastThreadId) {
6192
6787
  this.lastThreadId = params.sessionId;
6193
6788
  }
@@ -6233,7 +6828,7 @@ ${prompt}`;
6233
6828
  }
6234
6829
  input.push({ type: "text", text: fullPrompt });
6235
6830
  const turnRef = { id: null };
6236
- return new Promise((resolve5, reject) => {
6831
+ return new Promise((resolve6, reject) => {
6237
6832
  let settled = false;
6238
6833
  let fullContent = "";
6239
6834
  let thinkingEmitted = false;
@@ -6282,7 +6877,7 @@ ${prompt}`;
6282
6877
  params.onDelta(delta);
6283
6878
  const { status, message, branchChoice } = parseEndToken(fullContent);
6284
6879
  if (status !== "none") {
6285
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6880
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6286
6881
  }
6287
6882
  }
6288
6883
  } else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
@@ -6356,7 +6951,7 @@ ${prompt}`;
6356
6951
  return;
6357
6952
  }
6358
6953
  const { status, message, branchChoice } = parseEndToken(fullContent);
6359
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6954
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6360
6955
  } else if (method === "turn/errored") {
6361
6956
  const error = p["error"];
6362
6957
  const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
@@ -6431,7 +7026,7 @@ ${prompt}`;
6431
7026
  sandboxPolicy: { type: "dangerFullAccess" }
6432
7027
  });
6433
7028
  const turnId = turnRes.turn.id;
6434
- return new Promise((resolve5, reject) => {
7029
+ return new Promise((resolve6, reject) => {
6435
7030
  let settled = false;
6436
7031
  let fullContent = "";
6437
7032
  let thinkingEmitted = false;
@@ -6457,7 +7052,7 @@ ${prompt}`;
6457
7052
  params.onDelta(delta);
6458
7053
  const { status, message, branchChoice } = parseEndToken(fullContent);
6459
7054
  if (status !== "none") {
6460
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
7055
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6461
7056
  }
6462
7057
  }
6463
7058
  } else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
@@ -6531,7 +7126,7 @@ ${prompt}`;
6531
7126
  return;
6532
7127
  }
6533
7128
  const { status, message, branchChoice } = parseEndToken(fullContent);
6534
- settle(() => resolve5({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
7129
+ settle(() => resolve6({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
6535
7130
  } else if (method === "turn/errored") {
6536
7131
  const error = p["error"];
6537
7132
  const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
@@ -6552,18 +7147,18 @@ ${prompt}`;
6552
7147
 
6553
7148
  // ../server/src/trpc/routers/chat.ts
6554
7149
  var execFileAsync2 = promisify2(execFile3);
6555
- var MessageSchema = z14.object({
6556
- role: z14.enum(["user", "assistant"]),
6557
- content: z14.string().max(1e5)
7150
+ var MessageSchema = z15.object({
7151
+ role: z15.enum(["user", "assistant"]),
7152
+ content: z15.string().max(1e5)
6558
7153
  });
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()
7154
+ var ImagePartSchema = z15.object({
7155
+ type: z15.literal("image"),
7156
+ 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),
7157
+ url: z15.string().max(500),
7158
+ mimeType: z15.string().max(100),
7159
+ altText: z15.string().max(500).optional()
6565
7160
  });
6566
- var ImagePartsSchema = z14.array(ImagePartSchema).max(5).optional();
7161
+ var ImagePartsSchema = z15.array(ImagePartSchema).max(5).optional();
6567
7162
  var LANGUAGE_INSTRUCTION = "Always respond in the same language the user writes in.";
6568
7163
  async function buildSystemPrompt(apiKey) {
6569
7164
  const allServices = await serviceStore.list();
@@ -6720,7 +7315,7 @@ async function* runOpenAI(client, model, initialMessages, apiKey, authToken, sig
6720
7315
  yield { type: "done", text: "" };
6721
7316
  }
6722
7317
  async function* drainSession(session, signal, isReconnect) {
6723
- if (process.env.DEBUG) console.log("[chat]: drainSession");
7318
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: drainSession");
6724
7319
  if (isReconnect) {
6725
7320
  for (const part of session.parts) {
6726
7321
  if (part.type === "thinking") {
@@ -6873,7 +7468,7 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
6873
7468
  if (result) capturedFinalResult = result;
6874
7469
  }
6875
7470
  }).then(() => {
6876
- if (process.env.DEBUG) console.log("[chat]: claude end...");
7471
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: claude end...");
6877
7472
  if (activeSessionRegistry.get(convId) !== session) return;
6878
7473
  if (capturedFinalResult && capturedFinalResult.length > session.fullContent.length) {
6879
7474
  const missing = capturedFinalResult.slice(session.fullContent.length);
@@ -6882,7 +7477,7 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
6882
7477
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
6883
7478
  activeSessionRegistry.markDone(convId);
6884
7479
  }).catch((err) => {
6885
- if (process.env.DEBUG) console.error("[chat]: error...", err?.message, err);
7480
+ if (process.env.RUNEYA_DEBUG) console.error("[chat]: error...", err?.message, err);
6886
7481
  if (activeSessionRegistry.get(convId) !== session) return;
6887
7482
  activeSessionRegistry.pushEvent(convId, { type: "error", message: `Claude Code error: ${err.message}` });
6888
7483
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
@@ -6944,24 +7539,24 @@ async function startCodexSession(convId, prompt, opts = {}) {
6944
7539
  });
6945
7540
  return { session, done };
6946
7541
  }
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"),
7542
+ var MessagePartSchema = z15.discriminatedUnion("type", [
7543
+ z15.object({ type: z15.literal("thinking"), text: z15.string().max(1e5) }),
7544
+ z15.object({ type: z15.literal("text"), text: z15.string().max(1e5) }),
7545
+ z15.object({
7546
+ type: z15.literal("tool"),
6952
7547
  id: Id128Schema,
6953
7548
  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()
7549
+ input: z15.record(z15.string(), z15.unknown()).optional(),
7550
+ rawInput: z15.string().max(1e4).optional(),
7551
+ result: z15.unknown().optional(),
7552
+ isError: z15.boolean().optional(),
7553
+ pending: z15.boolean()
6959
7554
  })
6960
7555
  ]);
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()
7556
+ var StoredMessageSchema = z15.object({
7557
+ role: z15.enum(["user", "assistant"]),
7558
+ content: z15.string().max(1e5),
7559
+ parts: z15.array(MessagePartSchema).max(200).optional()
6965
7560
  });
6966
7561
  var chatRouter = router({
6967
7562
  conversations: router({
@@ -6969,13 +7564,13 @@ var chatRouter = router({
6969
7564
  const initial = await conversationStore.list();
6970
7565
  yield { type: "initial", conversations: initial };
6971
7566
  const queue = [];
6972
- let resolve5 = null;
7567
+ let resolve6 = null;
6973
7568
  const handler = (event) => {
6974
7569
  if (queue.length >= 500) queue.shift();
6975
7570
  queue.push(event);
6976
- if (resolve5) {
6977
- resolve5();
6978
- resolve5 = null;
7571
+ if (resolve6) {
7572
+ resolve6();
7573
+ resolve6 = null;
6979
7574
  }
6980
7575
  };
6981
7576
  conversationStore.onChanged(handler);
@@ -6985,7 +7580,7 @@ var chatRouter = router({
6985
7580
  yield queue.shift();
6986
7581
  } else {
6987
7582
  await new Promise((r) => {
6988
- resolve5 = r;
7583
+ resolve6 = r;
6989
7584
  });
6990
7585
  }
6991
7586
  }
@@ -6994,25 +7589,25 @@ var chatRouter = router({
6994
7589
  }
6995
7590
  }),
6996
7591
  list: protectedProcedure.query(() => conversationStore.list()),
6997
- get: protectedProcedure.input(z14.object({ id: Id128Schema })).query(async ({ input }) => {
7592
+ get: protectedProcedure.input(z15.object({ id: Id128Schema })).query(async ({ input }) => {
6998
7593
  const conv = await conversationStore.get(input.id);
6999
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7594
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7000
7595
  return conv;
7001
7596
  }),
7002
- create: protectedProcedure.input(z14.object({
7597
+ create: protectedProcedure.input(z15.object({
7003
7598
  title: ShortString200Schema.optional(),
7004
- isCli: z14.boolean().optional(),
7005
- runner: z14.enum(["claude-code", "codex"]).optional(),
7599
+ isCli: z15.boolean().optional(),
7600
+ runner: z15.enum(["claude-code", "codex"]).optional(),
7006
7601
  providerId: Id128Schema.optional(),
7007
7602
  scenarioId: Id128Schema.optional(),
7008
- scenarioInput: z14.string().max(1e5).optional(),
7603
+ scenarioInput: z15.string().max(1e5).optional(),
7009
7604
  kanbanBoardId: Id128Schema.optional(),
7010
7605
  kanbanCardId: Id128Schema.optional()
7011
7606
  })).mutation(async ({ input }) => {
7012
7607
  let scenario = null;
7013
7608
  if (input.scenarioId) {
7014
7609
  scenario = await scenarioStore.get(input.scenarioId);
7015
- if (!scenario) throw new TRPCError10({ code: "NOT_FOUND", message: "Scenario not found" });
7610
+ if (!scenario) throw new TRPCError11({ code: "NOT_FOUND", message: "Scenario not found" });
7016
7611
  }
7017
7612
  const conv = await conversationStore.create(
7018
7613
  input.title,
@@ -7033,22 +7628,22 @@ var chatRouter = router({
7033
7628
  }
7034
7629
  return conv;
7035
7630
  }),
7036
- update: protectedProcedure.input(z14.object({
7631
+ update: protectedProcedure.input(z15.object({
7037
7632
  id: Id128Schema,
7038
7633
  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()
7634
+ messages: z15.array(StoredMessageSchema).max(500).optional(),
7635
+ contextUsage: z15.object({
7636
+ inputTokens: z15.number().int().nonnegative(),
7637
+ contextWindow: z15.number().int().positive()
7043
7638
  }).optional()
7044
7639
  })).mutation(async ({ input }) => {
7045
7640
  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" });
7641
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7047
7642
  return conv;
7048
7643
  }),
7049
- delete: protectedProcedure.input(z14.object({ id: Id128Schema })).mutation(async ({ input }) => {
7644
+ delete: protectedProcedure.input(z15.object({ id: Id128Schema })).mutation(async ({ input }) => {
7050
7645
  const existing = await conversationStore.get(input.id);
7051
- if (!existing) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7646
+ if (!existing) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7052
7647
  for (const agent of agentManager.listAgents()) {
7053
7648
  const agentId = agent.config.id;
7054
7649
  agentManager.agentQuery(
@@ -7068,13 +7663,13 @@ var chatRouter = router({
7068
7663
  (err) => console.error("[chat.delete] Image cleanup error:", err)
7069
7664
  );
7070
7665
  }),
7071
- archive: protectedProcedure.input(z14.object({ id: Id128Schema, archived: z14.boolean() })).mutation(async ({ input }) => {
7666
+ archive: protectedProcedure.input(z15.object({ id: Id128Schema, archived: z15.boolean() })).mutation(async ({ input }) => {
7072
7667
  const conv = await conversationStore.update(input.id, { archived: input.archived });
7073
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7668
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7074
7669
  return conv;
7075
7670
  })
7076
7671
  }),
7077
- listModels: protectedProcedure.input(z14.object({ providerId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7672
+ listModels: protectedProcedure.input(z15.object({ providerId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7078
7673
  const active = await getActiveProvider(input?.providerId);
7079
7674
  if (!active) return [];
7080
7675
  try {
@@ -7083,7 +7678,7 @@ var chatRouter = router({
7083
7678
  return [];
7084
7679
  }
7085
7680
  }),
7086
- checkCliAvailability: protectedProcedure.input(z14.object({ agentId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7681
+ checkCliAvailability: protectedProcedure.input(z15.object({ agentId: Id128Schema.optional() }).optional()).query(async ({ input }) => {
7087
7682
  if (input?.agentId) {
7088
7683
  try {
7089
7684
  const res = await agentManager.agentQuery(
@@ -7101,9 +7696,13 @@ var chatRouter = router({
7101
7696
  ]);
7102
7697
  return { available: claudeOk, codexAvailable: codexOk };
7103
7698
  }),
7104
- enhanceScenarioPrompt: protectedProcedure.input(z14.object({ text: z14.string().max(1e4), providerId: Id128Schema.optional() })).mutation(async ({ input }) => {
7699
+ enhanceScenarioPrompt: protectedProcedure.input(z15.object({
7700
+ text: z15.string().max(1e4),
7701
+ providerId: Id128Schema.optional(),
7702
+ model: z15.string().max(200).optional()
7703
+ })).mutation(async ({ input }) => {
7105
7704
  const active = await getActiveProvider(input.providerId);
7106
- if (!active) throw new TRPCError10({ code: "BAD_REQUEST", message: "No AI provider configured" });
7705
+ if (!active) throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
7107
7706
  const systemPrompt = `You are an expert at writing clear, concise user request briefs for AI-powered workflows.
7108
7707
  When given a rough user request, rewrite it to be:
7109
7708
  - Clear and well-structured
@@ -7112,7 +7711,7 @@ When given a rough user request, rewrite it to be:
7112
7711
  - Concise but complete (no fluff)
7113
7712
  Return ONLY the improved text, no explanation, no preamble.`;
7114
7713
  const { provider, plaintextKey } = active;
7115
- const model = provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7714
+ const model = input.model || provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7116
7715
  if (provider.provider === "anthropic") {
7117
7716
  const client = new Anthropic2({ apiKey: plaintextKey });
7118
7717
  const res = await client.messages.create({
@@ -7154,39 +7753,39 @@ Return ONLY the improved text, no explanation, no preamble.`;
7154
7753
  }
7155
7754
  }),
7156
7755
  getStreamingStatus: protectedProcedure.query(() => activeSessionRegistry.listActive()),
7157
- stopSession: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
7158
- if (process.env.DEBUG) console.log("[chat]: stopSession");
7756
+ stopSession: protectedProcedure.input(z15.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
7757
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: stopSession");
7159
7758
  const killed = activeSessionRegistry.kill(input.conversationId);
7160
- if (!killed) throw new TRPCError10({ code: "NOT_FOUND", message: "No active session for this conversation" });
7759
+ if (!killed) throw new TRPCError11({ code: "NOT_FOUND", message: "No active session for this conversation" });
7161
7760
  return { ok: true };
7162
7761
  }),
7163
- streamViaClaudeCode: protectedProcedure.input(z14.object({
7762
+ streamViaClaudeCode: protectedProcedure.input(z15.object({
7164
7763
  conversationId: ConversationIdSchema,
7165
- prompt: z14.string().max(1e5),
7166
- contextNote: z14.string().max(2e5).optional(),
7764
+ prompt: z15.string().max(1e5),
7765
+ contextNote: z15.string().max(2e5).optional(),
7167
7766
  model: ShortString200Schema.optional(),
7168
- effort: z14.enum(["low", "medium", "high"]).optional(),
7169
- reconnect: z14.boolean().optional(),
7767
+ effort: z15.enum(["low", "medium", "high"]).optional(),
7768
+ reconnect: z15.boolean().optional(),
7170
7769
  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(),
7770
+ environmentId: z15.string().max(128).optional(),
7771
+ 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
7772
  imageParts: ImagePartsSchema
7174
7773
  })).subscription(async function* ({ input, ctx, signal }) {
7175
- if (process.env.DEBUG) console.log("[chat]: Reconnect");
7774
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: Reconnect");
7176
7775
  const existingSession = activeSessionRegistry.get(input.conversationId);
7177
7776
  if (input.reconnect && existingSession) {
7178
7777
  yield* drainSession(existingSession, signal, true);
7179
7778
  return;
7180
7779
  }
7181
- if (!input.prompt) throw new TRPCError10({ code: "BAD_REQUEST", message: "Prompt is required for new messages" });
7780
+ if (!input.prompt) throw new TRPCError11({ code: "BAD_REQUEST", message: "Prompt is required for new messages" });
7182
7781
  const conv = await conversationStore.get(input.conversationId);
7183
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7782
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7184
7783
  console.log(`[kanban-debug] streamViaClaudeCode start \u2014 convId=${conv.id} kanbanBoardId=${conv.kanbanBoardId ?? "null"}`);
7185
7784
  const isResume = conv.messages.length > 0;
7186
7785
  const userParts = input.imageParts && input.imageParts.length > 0 ? [...input.prompt.trim() ? [{ type: "text", text: input.prompt }] : [], ...input.imageParts] : void 0;
7187
7786
  conv.messages.push({ role: "user", content: input.prompt, parts: userParts, contextNote: input.contextNote });
7188
7787
  conv.messages.push({ role: "assistant", content: "" });
7189
- if (process.env.DEBUG) console.log("[chat]: update");
7788
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: update");
7190
7789
  conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => {
7191
7790
  });
7192
7791
  const cliPrompt = input.contextNote ? `${input.contextNote}
@@ -7215,7 +7814,7 @@ ${input.prompt}` : input.prompt;
7215
7814
  const imagePaths = [];
7216
7815
  if (input.imageIds && input.imageIds.length > 0) {
7217
7816
  for (const id of input.imageIds) {
7218
- const result = await imageStorageService.getImagePath(id);
7817
+ const result = await imageStorageService.getImagePath(id, input.conversationId);
7219
7818
  if (result) imagePaths.push(result.filePath);
7220
7819
  }
7221
7820
  }
@@ -7228,14 +7827,14 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7228
7827
  try {
7229
7828
  session = activeSessionRegistry.registerProviderApiSession(convId, ac);
7230
7829
  } catch {
7231
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7830
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7232
7831
  }
7233
7832
  let capturedFinalResult = "";
7234
7833
  const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();
7235
7834
  if (resolvedAgentId) {
7236
7835
  session.agentId = resolvedAgentId;
7237
7836
  }
7238
- if (process.env.DEBUG) console.log("[chat]: claude run...");
7837
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: claude run...");
7239
7838
  const injectedEnv = await resolveInjectedEnv(input.environmentId);
7240
7839
  new ClaudeCodeRunner().run({
7241
7840
  agentId: resolvedAgentId ?? void 0,
@@ -7276,18 +7875,18 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7276
7875
  activeSessionRegistry.pushEvent(convId, { type: "done", text: "" });
7277
7876
  activeSessionRegistry.markDone(convId);
7278
7877
  });
7279
- if (process.env.DEBUG) console.log("[chat]: drainSession...");
7878
+ if (process.env.RUNEYA_DEBUG) console.log("[chat]: drainSession...");
7280
7879
  yield* drainSession(session, signal, false);
7281
7880
  }),
7282
- streamViaCodex: protectedProcedure.input(z14.object({
7881
+ streamViaCodex: protectedProcedure.input(z15.object({
7283
7882
  conversationId: ConversationIdSchema,
7284
- prompt: z14.string().max(1e5),
7285
- contextNote: z14.string().max(2e5).optional(),
7883
+ prompt: z15.string().max(1e5),
7884
+ contextNote: z15.string().max(2e5).optional(),
7286
7885
  model: ShortString200Schema.optional(),
7287
- reconnect: z14.boolean().optional(),
7886
+ reconnect: z15.boolean().optional(),
7288
7887
  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(),
7888
+ environmentId: z15.string().max(128).optional(),
7889
+ 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
7890
  imageParts: ImagePartsSchema
7292
7891
  })).subscription(async function* ({ input, ctx, signal }) {
7293
7892
  const existingSession = activeSessionRegistry.get(input.conversationId);
@@ -7295,9 +7894,9 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
7295
7894
  yield* drainSession(existingSession, signal, true);
7296
7895
  return;
7297
7896
  }
7298
- if (!input.prompt) throw new TRPCError10({ code: "BAD_REQUEST", message: "Prompt is required" });
7897
+ if (!input.prompt) throw new TRPCError11({ code: "BAD_REQUEST", message: "Prompt is required" });
7299
7898
  const conv = await conversationStore.get(input.conversationId);
7300
- if (!conv) throw new TRPCError10({ code: "NOT_FOUND", message: "Conversation not found" });
7899
+ if (!conv) throw new TRPCError11({ code: "NOT_FOUND", message: "Conversation not found" });
7301
7900
  const isResume = !!conv.codexThreadId;
7302
7901
  const codexUserParts = input.imageParts && input.imageParts.length > 0 ? [...input.prompt.trim() ? [{ type: "text", text: input.prompt }] : [], ...input.imageParts] : void 0;
7303
7902
  conv.messages.push({ role: "user", content: input.prompt, parts: codexUserParts, contextNote: input.contextNote });
@@ -7317,14 +7916,14 @@ ${input.prompt}` : input.prompt;
7317
7916
  systemContext += "\n\n" + (conv.kanbanCardId ? generateKanbanSystemPrompt(kanbanBoard, conv.kanbanCardId) : generateKanbanPopulatePrompt(kanbanBoard));
7318
7917
  }
7319
7918
  }
7320
- const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" }))) : [];
7919
+ const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" })), input.conversationId) : [];
7321
7920
  const convId = input.conversationId;
7322
7921
  const ac = new AbortController();
7323
7922
  let session;
7324
7923
  try {
7325
7924
  session = activeSessionRegistry.registerProviderApiSession(convId, ac);
7326
7925
  } catch {
7327
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7926
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7328
7927
  }
7329
7928
  const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();
7330
7929
  if (resolvedAgentId) {
@@ -7364,13 +7963,13 @@ ${input.prompt}` : input.prompt;
7364
7963
  });
7365
7964
  yield* drainSession(session, signal, false);
7366
7965
  }),
7367
- send: protectedProcedure.input(z14.object({
7966
+ send: protectedProcedure.input(z15.object({
7368
7967
  conversationId: ConversationIdSchema,
7369
- messages: z14.array(MessageSchema).min(0).max(100),
7968
+ messages: z15.array(MessageSchema).min(0).max(100),
7370
7969
  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(),
7970
+ contextNote: z15.string().max(2e5).optional(),
7971
+ reconnect: z15.boolean().optional(),
7972
+ 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
7973
  imageParts: ImagePartsSchema
7375
7974
  })).subscription(async function* ({ input, ctx, signal }) {
7376
7975
  const existingSession = activeSessionRegistry.get(input.conversationId);
@@ -7379,12 +7978,12 @@ ${input.prompt}` : input.prompt;
7379
7978
  return;
7380
7979
  }
7381
7980
  if (input.messages.length === 0) {
7382
- throw new TRPCError10({ code: "BAD_REQUEST", message: "Messages are required for new messages" });
7981
+ throw new TRPCError11({ code: "BAD_REQUEST", message: "Messages are required for new messages" });
7383
7982
  }
7384
7983
  const conv = await conversationStore.get(input.conversationId);
7385
7984
  const active = await getActiveProvider(conv?.providerId);
7386
7985
  if (!active) {
7387
- throw new TRPCError10({
7986
+ throw new TRPCError11({
7388
7987
  code: "PRECONDITION_FAILED",
7389
7988
  message: "No AI provider configured for this conversation. Please select one when starting a new chat."
7390
7989
  });
@@ -7410,12 +8009,12 @@ ${input.prompt}` : input.prompt;
7410
8009
  system += "\n\n" + (conv.kanbanCardId ? generateKanbanSystemPrompt(kanbanBoard, conv.kanbanCardId) : generateKanbanPopulatePrompt(kanbanBoard));
7411
8010
  }
7412
8011
  }
7413
- const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" }))) : [];
8012
+ const resolvedImages = input.imageIds && input.imageIds.length > 0 ? await resolveImagesToBase64(input.imageIds.map((id) => ({ imageId: id, mimeType: "" })), input.conversationId) : [];
7414
8013
  let session;
7415
8014
  try {
7416
8015
  session = activeSessionRegistry.registerDirectProviderApiSession(input.conversationId);
7417
8016
  } catch {
7418
- throw new TRPCError10({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
8017
+ throw new TRPCError11({ code: "CONFLICT", message: "A session is already running for this conversation. Stop it first." });
7419
8018
  }
7420
8019
  const apiSignal = session.abortController.signal;
7421
8020
  const runStream = async () => {
@@ -7487,25 +8086,25 @@ ${input.prompt}` : input.prompt;
7487
8086
  // ── PTY passthrough (interactive Claude Code session) ────────────────────
7488
8087
  pty: router({
7489
8088
  /** 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)
8089
+ start: protectedProcedure.input(z15.object({
8090
+ agentId: z15.string().max(128).optional(),
8091
+ sessionId: z15.string().max(128).optional(),
8092
+ runner: z15.enum(["claude-code", "codex"]).default("claude-code"),
8093
+ resume: z15.boolean().default(false),
8094
+ model: z15.string().max(128).optional(),
8095
+ cwd: z15.string().max(4096).optional(),
8096
+ cols: z15.number().int().min(1).max(1e3).default(120),
8097
+ rows: z15.number().int().min(1).max(1e3).default(30),
8098
+ enableMcp: z15.boolean().default(false),
8099
+ apiBaseUrl: z15.string().max(512).optional(),
8100
+ environmentId: z15.string().max(128).optional(),
8101
+ attachedContexts: z15.array(z15.object({
8102
+ label: z15.string().max(200),
8103
+ text: z15.string().max(1e6)
7505
8104
  })).max(20).optional()
7506
8105
  })).mutation(async ({ input, ctx }) => {
7507
8106
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7508
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8107
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7509
8108
  if (input.sessionId) {
7510
8109
  try {
7511
8110
  const existing = await agentManager.agentQuery(
@@ -7641,15 +8240,15 @@ Context:
7641
8240
  return { spawnId: res.spawnId, agentId, reattached: false };
7642
8241
  }),
7643
8242
  /** 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)
8243
+ stream: protectedProcedure.input(z15.object({
8244
+ agentId: z15.string().max(128).optional(),
8245
+ spawnId: z15.string().max(128),
8246
+ fromOffset: z15.number().int().min(0).default(0)
7648
8247
  })).subscription(async function* ({ input, signal }) {
7649
8248
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7650
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8249
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7651
8250
  const queue = [];
7652
- let resolve5 = null;
8251
+ let resolve6 = null;
7653
8252
  let stopped = false;
7654
8253
  const unsub = agentManager.agentSubscribe(
7655
8254
  agentId,
@@ -7657,16 +8256,16 @@ Context:
7657
8256
  { spawnId: input.spawnId, fromOffset: input.fromOffset },
7658
8257
  (data) => {
7659
8258
  queue.push(data);
7660
- if (resolve5) {
7661
- resolve5();
7662
- resolve5 = null;
8259
+ if (resolve6) {
8260
+ resolve6();
8261
+ resolve6 = null;
7663
8262
  }
7664
8263
  },
7665
8264
  () => {
7666
8265
  stopped = true;
7667
- if (resolve5) {
7668
- resolve5();
7669
- resolve5 = null;
8266
+ if (resolve6) {
8267
+ resolve6();
8268
+ resolve6 = null;
7670
8269
  }
7671
8270
  }
7672
8271
  );
@@ -7682,7 +8281,7 @@ Context:
7682
8281
  }
7683
8282
  if (stopped) break;
7684
8283
  await new Promise((r) => {
7685
- resolve5 = r;
8284
+ resolve6 = r;
7686
8285
  });
7687
8286
  }
7688
8287
  } finally {
@@ -7690,27 +8289,27 @@ Context:
7690
8289
  }
7691
8290
  }),
7692
8291
  /** 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)
8292
+ write: protectedProcedure.input(z15.object({
8293
+ agentId: z15.string().max(128).optional(),
8294
+ spawnId: z15.string().max(128),
8295
+ data: z15.string().max(1e6)
7697
8296
  })).mutation(async ({ input }) => {
7698
8297
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7699
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8298
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7700
8299
  return agentManager.agentMutation(agentId, "ai.writePty", {
7701
8300
  spawnId: input.spawnId,
7702
8301
  data: input.data
7703
8302
  });
7704
8303
  }),
7705
8304
  /** 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)
8305
+ resize: protectedProcedure.input(z15.object({
8306
+ agentId: z15.string().max(128).optional(),
8307
+ spawnId: z15.string().max(128),
8308
+ cols: z15.number().int().min(1).max(1e3),
8309
+ rows: z15.number().int().min(1).max(1e3)
7711
8310
  })).mutation(async ({ input }) => {
7712
8311
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7713
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8312
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7714
8313
  return agentManager.agentMutation(agentId, "ai.resizePty", {
7715
8314
  spawnId: input.spawnId,
7716
8315
  cols: input.cols,
@@ -7723,14 +8322,14 @@ Context:
7723
8322
  * picks it up. Returns the tmp path and which clipboard method succeeded
7724
8323
  * (null if none — caller should inject `@<path>` instead of Ctrl+V).
7725
8324
  */
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)
8325
+ pasteImage: protectedProcedure.input(z15.object({
8326
+ agentId: z15.string().max(128).optional(),
8327
+ sessionId: z15.string().max(128).optional(),
8328
+ mimeType: z15.string().max(64),
8329
+ base64: z15.string().max(15e6)
7731
8330
  })).mutation(async ({ input }) => {
7732
8331
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7733
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8332
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7734
8333
  const res = await agentManager.agentMutation(agentId, "ai.pasteImage", {
7735
8334
  sessionId: input.sessionId,
7736
8335
  mimeType: input.mimeType,
@@ -7745,7 +8344,7 @@ Context:
7745
8344
  * Read the transcript Claude Code stores on disk for a given session.
7746
8345
  * Returns parsed JSONL entries (user / assistant messages, tool uses, etc.).
7747
8346
  */
7748
- transcript: protectedProcedure.input(z14.object({ sessionId: z14.string().max(128) })).query(async ({ input }) => {
8347
+ transcript: protectedProcedure.input(z15.object({ sessionId: z15.string().max(128) })).query(async ({ input }) => {
7749
8348
  return readClaudeTranscript(input.sessionId);
7750
8349
  }),
7751
8350
  /**
@@ -7753,9 +8352,9 @@ Context:
7753
8352
  * when was the transcript JSONL last touched? Used by the non-PTY view
7754
8353
  * to show a "Claude is thinking…" indicator while output streams.
7755
8354
  */
7756
- activity: protectedProcedure.input(z14.object({
7757
- agentId: z14.string().max(128).optional(),
7758
- sessionId: z14.string().max(128)
8355
+ activity: protectedProcedure.input(z15.object({
8356
+ agentId: z15.string().max(128).optional(),
8357
+ sessionId: z15.string().max(128)
7759
8358
  })).query(async ({ input }) => {
7760
8359
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7761
8360
  let running = false;
@@ -7781,10 +8380,10 @@ Context:
7781
8380
  * them on the conversation, so the non-PTY chat view stays in sync.
7782
8381
  * Uses the conversation id as session id (PTY sessions are spawned that way).
7783
8382
  */
7784
- syncTranscript: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
8383
+ syncTranscript: protectedProcedure.input(z15.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
7785
8384
  const conv = await conversationStore.get(input.conversationId);
7786
8385
  if (!conv) {
7787
- throw new TRPCError10({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
8386
+ throw new TRPCError11({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
7788
8387
  }
7789
8388
  const synced = await syncConversationFromClaudeTranscript(conv.id);
7790
8389
  const after = await conversationStore.get(conv.id);
@@ -7795,17 +8394,17 @@ Context:
7795
8394
  * with the given sessionId. Used to benchmark the PTY → tRPC → xterm.js
7796
8395
  * pipeline. Open the conversation in PTY mode to attach to it.
7797
8396
  */
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)
8397
+ debugStress: protectedProcedure.input(z15.object({
8398
+ sessionId: z15.string().max(128),
8399
+ agentId: z15.string().max(128).optional(),
8400
+ bytesPerSec: z15.number().int().min(100).max(1e7).default(5e4),
8401
+ chunkSize: z15.number().int().min(1).max(65536).default(256),
8402
+ durationMs: z15.number().int().min(0).max(36e5).default(0),
8403
+ cols: z15.number().int().min(1).max(1e3).default(120),
8404
+ rows: z15.number().int().min(1).max(1e3).default(30)
7806
8405
  })).mutation(async ({ input }) => {
7807
8406
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7808
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8407
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7809
8408
  const stressSessionId = `${input.sessionId}-stress-${Date.now().toString(36)}`;
7810
8409
  const { existsSync: existsSync3 } = await import("fs");
7811
8410
  const candidates = [
@@ -7822,7 +8421,7 @@ Context:
7822
8421
  }
7823
8422
  }
7824
8423
  if (!scriptPath) {
7825
- throw new TRPCError10({
8424
+ throw new TRPCError11({
7826
8425
  code: "INTERNAL_SERVER_ERROR",
7827
8426
  message: `pty-stress-gen.mjs not found near ${fileURLToPath(import.meta.url)}`
7828
8427
  });
@@ -7847,12 +8446,12 @@ Context:
7847
8446
  return { spawnId: res.spawnId, agentId, stressSessionId };
7848
8447
  }),
7849
8448
  /** Kill a PTY session. */
7850
- kill: protectedProcedure.input(z14.object({
7851
- agentId: z14.string().max(128).optional(),
7852
- spawnId: z14.string().max(128)
8449
+ kill: protectedProcedure.input(z15.object({
8450
+ agentId: z15.string().max(128).optional(),
8451
+ spawnId: z15.string().max(128)
7853
8452
  })).mutation(async ({ input }) => {
7854
8453
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7855
- if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
8454
+ if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
7856
8455
  return agentManager.agentMutation(agentId, "ai.kill", {
7857
8456
  spawnId: input.spawnId
7858
8457
  });
@@ -7861,125 +8460,125 @@ Context:
7861
8460
  });
7862
8461
 
7863
8462
  // ../server/src/trpc/routers/git.ts
7864
- import { z as z15 } from "zod";
7865
- import { TRPCError as TRPCError11 } from "@trpc/server";
8463
+ import { z as z16 } from "zod";
8464
+ import { TRPCError as TRPCError12 } from "@trpc/server";
7866
8465
  import Anthropic3 from "@anthropic-ai/sdk";
7867
8466
  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("../"), {
8467
+ var SERVICE_ID_SCHEMA = z16.string().max(128);
8468
+ var SAFE_PATH_SCHEMA = z16.string().min(1).max(4096).refine((p) => !p.startsWith("/") && !p.includes("../"), {
7870
8469
  message: "Path must be relative and not traverse up"
7871
8470
  });
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._\-/]+$/);
8471
+ var REMOTE_SCHEMA = z16.string().min(1).max(128).regex(/^[a-zA-Z0-9_\-.]+$/);
8472
+ var BRANCH_SCHEMA = z16.string().min(1).max(256).regex(/^[a-zA-Z0-9._\-/]+$/);
7874
8473
  async function resolveServiceContext(serviceId) {
7875
8474
  const service = await serviceStore.get(serviceId);
7876
8475
  if (!service) {
7877
- throw new TRPCError11({ code: "NOT_FOUND", message: "Service not found" });
8476
+ throw new TRPCError12({ code: "NOT_FOUND", message: "Service not found" });
7878
8477
  }
7879
8478
  const agentId = await resolveEffectiveAgentId(service);
7880
8479
  if (!agentId) {
7881
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent configured for this service" });
8480
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent configured for this service" });
7882
8481
  }
7883
8482
  return { agentId, cwd: service.cwd || "." };
7884
8483
  }
7885
8484
  var gitTargetShape = {
7886
8485
  serviceId: SERVICE_ID_SCHEMA.optional(),
7887
- agentId: z15.string().max(128).optional()
8486
+ agentId: z16.string().max(128).optional()
7888
8487
  };
7889
8488
  async function resolveGitContext(input) {
7890
8489
  if (input.serviceId) return resolveServiceContext(input.serviceId);
7891
8490
  const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7892
8491
  if (!agentId) {
7893
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent available" });
8492
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent available" });
7894
8493
  }
7895
8494
  return { agentId, cwd: "." };
7896
8495
  }
7897
8496
  var gitRouter = router({
7898
8497
  /** Get full git status */
7899
- status: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8498
+ status: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
7900
8499
  const { agentId, cwd } = await resolveGitContext(input);
7901
8500
  return agentManager.agentQuery(agentId, "git.status", { cwd });
7902
8501
  }),
7903
8502
  /** Get commit log */
7904
- log: protectedProcedure.input(z15.object({ ...gitTargetShape, limit: z15.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
8503
+ log: protectedProcedure.input(z16.object({ ...gitTargetShape, limit: z16.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
7905
8504
  const { agentId, cwd } = await resolveGitContext(input);
7906
8505
  return agentManager.agentQuery(agentId, "git.log", { cwd, limit: input.limit });
7907
8506
  }),
7908
8507
  /** List local and remote branches */
7909
- branches: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8508
+ branches: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
7910
8509
  const { agentId, cwd } = await resolveGitContext(input);
7911
8510
  return agentManager.agentQuery(agentId, "git.branches", { cwd });
7912
8511
  }),
7913
8512
  /** Checkout a branch */
7914
- checkout: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z15.boolean().default(false) })).mutation(async ({ input }) => {
8513
+ checkout: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z16.boolean().default(false) })).mutation(async ({ input }) => {
7915
8514
  const { agentId, cwd } = await resolveGitContext(input);
7916
8515
  return agentManager.agentMutation(agentId, "git.checkout", { cwd, branch: input.branch, create: input.create });
7917
8516
  }),
7918
8517
  /** Stage files */
7919
- stage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
8518
+ stage: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7920
8519
  const { agentId, cwd } = await resolveGitContext(input);
7921
8520
  return agentManager.agentMutation(agentId, "git.stage", { cwd, paths: input.paths });
7922
8521
  }),
7923
8522
  /** Stage all files */
7924
- stageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
8523
+ stageAll: protectedProcedure.input(z16.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7925
8524
  const { agentId, cwd } = await resolveGitContext(input);
7926
8525
  return agentManager.agentMutation(agentId, "git.stageAll", { cwd });
7927
8526
  }),
7928
8527
  /** Unstage files */
7929
- unstage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
8528
+ unstage: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7930
8529
  const { agentId, cwd } = await resolveGitContext(input);
7931
8530
  return agentManager.agentMutation(agentId, "git.unstage", { cwd, paths: input.paths });
7932
8531
  }),
7933
8532
  /** Unstage all files */
7934
- unstageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
8533
+ unstageAll: protectedProcedure.input(z16.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7935
8534
  const { agentId, cwd } = await resolveGitContext(input);
7936
8535
  return agentManager.agentMutation(agentId, "git.unstageAll", { cwd });
7937
8536
  }),
7938
8537
  /** Commit staged changes */
7939
- commit: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().min(1).max(4096) })).mutation(async ({ input }) => {
8538
+ commit: protectedProcedure.input(z16.object({ ...gitTargetShape, message: z16.string().min(1).max(4096) })).mutation(async ({ input }) => {
7940
8539
  const { agentId, cwd } = await resolveGitContext(input);
7941
8540
  return agentManager.agentMutation(agentId, "git.commit", { cwd, message: input.message });
7942
8541
  }),
7943
8542
  /** Push to remote */
7944
- push: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z15.boolean().default(false) })).mutation(async ({ input }) => {
8543
+ push: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z16.boolean().default(false) })).mutation(async ({ input }) => {
7945
8544
  const { agentId, cwd } = await resolveGitContext(input);
7946
8545
  return agentManager.agentMutation(agentId, "git.push", { cwd, remote: input.remote, setUpstream: input.setUpstream });
7947
8546
  }),
7948
8547
  /** Pull from remote */
7949
- pull: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
8548
+ pull: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7950
8549
  const { agentId, cwd } = await resolveGitContext(input);
7951
8550
  return agentManager.agentMutation(agentId, "git.pull", { cwd, remote: input.remote });
7952
8551
  }),
7953
8552
  /** Fetch from remote */
7954
- fetch: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
8553
+ fetch: protectedProcedure.input(z16.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7955
8554
  const { agentId, cwd } = await resolveGitContext(input);
7956
8555
  return agentManager.agentMutation(agentId, "git.fetch", { cwd, remote: input.remote });
7957
8556
  }),
7958
8557
  /** Get diff for a file or the whole index */
7959
- diff: protectedProcedure.input(z15.object({
8558
+ diff: protectedProcedure.input(z16.object({
7960
8559
  ...gitTargetShape,
7961
- path: z15.string().max(4096).optional(),
7962
- staged: z15.boolean().default(false),
7963
- untracked: z15.boolean().default(false)
8560
+ path: z16.string().max(4096).optional(),
8561
+ staged: z16.boolean().default(false),
8562
+ untracked: z16.boolean().default(false)
7964
8563
  })).query(async ({ input }) => {
7965
8564
  const { agentId, cwd } = await resolveGitContext(input);
7966
8565
  return agentManager.agentQuery(agentId, "git.diff", { cwd, path: input.path, staged: input.staged, untracked: input.untracked });
7967
8566
  }),
7968
8567
  /** 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 }) => {
8568
+ discard: protectedProcedure.input(z16.object({ ...gitTargetShape, paths: z16.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7970
8569
  const { agentId, cwd } = await resolveGitContext(input);
7971
8570
  return agentManager.agentMutation(agentId, "git.discard", { cwd, paths: input.paths });
7972
8571
  }),
7973
8572
  /** 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 }) => {
8573
+ generateCommitMessage: protectedProcedure.input(z16.object({ ...gitTargetShape, providerId: OptionalProviderIdSchema, model: z16.string().max(200).optional() })).mutation(async ({ input }) => {
7975
8574
  const { agentId, cwd } = await resolveGitContext(input);
7976
8575
  const { diff } = await agentManager.agentQuery(agentId, "git.stagedDiff", { cwd });
7977
8576
  if (!diff.trim()) {
7978
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
8577
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
7979
8578
  }
7980
- const active = await getActiveProvider();
8579
+ const active = await getActiveProvider(input.providerId);
7981
8580
  if (!active) {
7982
- throw new TRPCError11({ code: "BAD_REQUEST", message: "No AI provider configured" });
8581
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "No AI provider configured" });
7983
8582
  }
7984
8583
  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
8584
  Rules:
@@ -7988,7 +8587,7 @@ Rules:
7988
8587
  - Write in English
7989
8588
  - Return ONLY the commit message, nothing else`;
7990
8589
  const { provider, plaintextKey } = active;
7991
- const model = provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
8590
+ const model = input.model || provider.model || DEFAULT_MODELS[provider.provider] || "claude-opus-4-6";
7992
8591
  if (provider.provider === "anthropic") {
7993
8592
  const client = new Anthropic3({ apiKey: plaintextKey });
7994
8593
  const res = await client.messages.create({
@@ -8012,48 +8611,48 @@ Rules:
8012
8611
  });
8013
8612
  return { message: res.choices[0]?.message?.content?.trim() ?? "" };
8014
8613
  }
8015
- throw new TRPCError11({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
8614
+ throw new TRPCError12({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
8016
8615
  }),
8017
8616
  /** 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 }) => {
8617
+ merge: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, noFf: z16.boolean().default(false) })).mutation(async ({ input }) => {
8019
8618
  const { agentId, cwd } = await resolveGitContext(input);
8020
8619
  return agentManager.agentMutation(agentId, "git.merge", { cwd, branch: input.branch, noFf: input.noFf });
8021
8620
  }),
8022
8621
  /** Rebase current branch onto another branch */
8023
- rebase: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
8622
+ rebase: protectedProcedure.input(z16.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
8024
8623
  const { agentId, cwd } = await resolveGitContext(input);
8025
8624
  return agentManager.agentMutation(agentId, "git.rebase", { cwd, branch: input.branch });
8026
8625
  }),
8027
8626
  /** List stash entries */
8028
- stashList: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8627
+ stashList: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
8029
8628
  const { agentId, cwd } = await resolveGitContext(input);
8030
8629
  return agentManager.agentQuery(agentId, "git.stashList", { cwd });
8031
8630
  }),
8032
8631
  /** Stash current changes */
8033
- stashPush: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().max(4096).optional(), includeUntracked: z15.boolean().default(true) })).mutation(async ({ input }) => {
8632
+ stashPush: protectedProcedure.input(z16.object({ ...gitTargetShape, message: z16.string().max(4096).optional(), includeUntracked: z16.boolean().default(true) })).mutation(async ({ input }) => {
8034
8633
  const { agentId, cwd } = await resolveGitContext(input);
8035
8634
  return agentManager.agentMutation(agentId, "git.stashPush", { cwd, message: input.message, includeUntracked: input.includeUntracked });
8036
8635
  }),
8037
8636
  /** Pop a stash entry */
8038
- stashPop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8637
+ stashPop: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8039
8638
  const { agentId, cwd } = await resolveGitContext(input);
8040
8639
  return agentManager.agentMutation(agentId, "git.stashPop", { cwd, index: input.index });
8041
8640
  }),
8042
8641
  /** 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 }) => {
8642
+ stashApply: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8044
8643
  const { agentId, cwd } = await resolveGitContext(input);
8045
8644
  return agentManager.agentMutation(agentId, "git.stashApply", { cwd, index: input.index });
8046
8645
  }),
8047
8646
  /** Drop a stash entry */
8048
- stashDrop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8647
+ stashDrop: protectedProcedure.input(z16.object({ ...gitTargetShape, index: z16.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
8049
8648
  const { agentId, cwd } = await resolveGitContext(input);
8050
8649
  return agentManager.agentMutation(agentId, "git.stashDrop", { cwd, index: input.index });
8051
8650
  }),
8052
8651
  /** Watch .git/ for changes and stream updated status */
8053
- onStatusChange: protectedProcedure.input(z15.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
8652
+ onStatusChange: protectedProcedure.input(z16.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
8054
8653
  const { agentId, cwd } = await resolveGitContext(input);
8055
8654
  const queue = [];
8056
- let resolve5 = null;
8655
+ let resolve6 = null;
8057
8656
  let done = false;
8058
8657
  let unsubscribe;
8059
8658
  try {
@@ -8063,21 +8662,21 @@ Rules:
8063
8662
  { cwd },
8064
8663
  (data) => {
8065
8664
  queue.push(data);
8066
- if (resolve5) {
8067
- resolve5();
8068
- resolve5 = null;
8665
+ if (resolve6) {
8666
+ resolve6();
8667
+ resolve6 = null;
8069
8668
  }
8070
8669
  },
8071
8670
  () => {
8072
8671
  done = true;
8073
- if (resolve5) {
8074
- resolve5();
8075
- resolve5 = null;
8672
+ if (resolve6) {
8673
+ resolve6();
8674
+ resolve6 = null;
8076
8675
  }
8077
8676
  }
8078
8677
  );
8079
8678
  } catch (err) {
8080
- throw new TRPCError11({
8679
+ throw new TRPCError12({
8081
8680
  code: "BAD_REQUEST",
8082
8681
  message: `Cannot subscribe to git status: ${err.message}`
8083
8682
  });
@@ -8088,7 +8687,7 @@ Rules:
8088
8687
  yield queue.shift();
8089
8688
  } else {
8090
8689
  await new Promise((r) => {
8091
- resolve5 = r;
8690
+ resolve6 = r;
8092
8691
  });
8093
8692
  }
8094
8693
  }
@@ -8097,49 +8696,49 @@ Rules:
8097
8696
  }
8098
8697
  }),
8099
8698
  /** List remotes */
8100
- remotes: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
8699
+ remotes: protectedProcedure.input(z16.object({ ...gitTargetShape })).query(async ({ input }) => {
8101
8700
  const { agentId, cwd } = await resolveGitContext(input);
8102
8701
  return agentManager.agentQuery(agentId, "git.remotes", { cwd });
8103
8702
  })
8104
8703
  });
8105
8704
 
8106
8705
  // ../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";
8706
+ import { randomUUID as randomUUID8 } from "crypto";
8707
+ import { TRPCError as TRPCError15 } from "@trpc/server";
8708
+ import { z as z17 } from "zod";
8110
8709
 
8111
8710
  // ../server/src/trpc/crud-procedure-utils.ts
8112
- import { TRPCError as TRPCError12 } from "@trpc/server";
8711
+ import { TRPCError as TRPCError13 } from "@trpc/server";
8113
8712
  async function getOrNotFound(getter, notFoundMessage) {
8114
8713
  const entity = await getter();
8115
- if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8714
+ if (!entity) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8116
8715
  return entity;
8117
8716
  }
8118
8717
  async function createOrBadRequest(creator) {
8119
8718
  try {
8120
8719
  return await creator();
8121
8720
  } catch (err) {
8122
- throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
8721
+ throw new TRPCError13({ code: "BAD_REQUEST", message: err.message });
8123
8722
  }
8124
8723
  }
8125
8724
  async function updateOrThrow(updater, notFoundMessage) {
8126
8725
  try {
8127
8726
  const entity = await updater();
8128
- if (!entity) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8727
+ if (!entity) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8129
8728
  return entity;
8130
8729
  } catch (err) {
8131
- if (err instanceof TRPCError12) throw err;
8132
- throw new TRPCError12({ code: "BAD_REQUEST", message: err.message });
8730
+ if (err instanceof TRPCError13) throw err;
8731
+ throw new TRPCError13({ code: "BAD_REQUEST", message: err.message });
8133
8732
  }
8134
8733
  }
8135
8734
  async function deleteOrNotFound(deleter, notFoundMessage) {
8136
8735
  const deleted = await deleter();
8137
- if (!deleted) throw new TRPCError12({ code: "NOT_FOUND", message: notFoundMessage });
8736
+ if (!deleted) throw new TRPCError13({ code: "NOT_FOUND", message: notFoundMessage });
8138
8737
  return { ok: true };
8139
8738
  }
8140
8739
 
8141
8740
  // ../server/src/trpc/ai-generation-procedure-utils.ts
8142
- import { TRPCError as TRPCError13 } from "@trpc/server";
8741
+ import { TRPCError as TRPCError14 } from "@trpc/server";
8143
8742
 
8144
8743
  // ../server/src/services/ai-generation.ts
8145
8744
  import Anthropic4 from "@anthropic-ai/sdk";
@@ -8195,9 +8794,9 @@ async function* streamProviderText(input) {
8195
8794
  // ../server/src/trpc/ai-generation-procedure-utils.ts
8196
8795
  async function* streamWithActiveProvider(opts) {
8197
8796
  const active = await getActiveProvider(opts.providerId);
8198
- if (!active) throw new TRPCError13({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
8797
+ if (!active) throw new TRPCError14({ code: "BAD_REQUEST", message: "Aucun fournisseur IA configur\xE9" });
8199
8798
  const { provider, plaintextKey } = active;
8200
- const model = provider.model ?? DEFAULT_MODELS[provider.provider] ?? "";
8799
+ const model = opts.model || provider.model || DEFAULT_MODELS[provider.provider] || "";
8201
8800
  try {
8202
8801
  for await (const text of streamProviderText({
8203
8802
  provider,
@@ -8213,16 +8812,16 @@ async function* streamWithActiveProvider(opts) {
8213
8812
  yield { type: "done", text: "" };
8214
8813
  } catch (err) {
8215
8814
  if (String(err?.message ?? "").startsWith("Unsupported provider:")) {
8216
- throw new TRPCError13({ code: "BAD_REQUEST", message: `Fournisseur non support\xE9: ${provider.provider}` });
8815
+ throw new TRPCError14({ code: "BAD_REQUEST", message: `Fournisseur non support\xE9: ${provider.provider}` });
8217
8816
  }
8218
8817
  throw err;
8219
8818
  }
8220
8819
  }
8221
8820
 
8222
8821
  // ../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";
8822
+ import { mkdir as mkdir13 } from "fs/promises";
8823
+ import { randomUUID as randomUUID7 } from "crypto";
8824
+ import { join as join15 } from "path";
8226
8825
 
8227
8826
  // ../server/src/services/scenario-presets.ts
8228
8827
  var BUILT_IN_PRESETS = [
@@ -8436,21 +9035,21 @@ async function* tapBus(conversationId, signal) {
8436
9035
  }
8437
9036
  if (bus.done) return;
8438
9037
  if (signal.aborted) return;
8439
- await new Promise((resolve5) => {
9038
+ await new Promise((resolve6) => {
8440
9039
  const onAbort = () => {
8441
9040
  bus.listeners.delete(notify);
8442
- resolve5();
9041
+ resolve6();
8443
9042
  };
8444
9043
  const notify = () => {
8445
9044
  signal.removeEventListener("abort", onAbort);
8446
- resolve5();
9045
+ resolve6();
8447
9046
  };
8448
9047
  bus.listeners.add(notify);
8449
9048
  signal.addEventListener("abort", onAbort, { once: true });
8450
9049
  if (cursor < bus.baseIndex + bus.buffer.length || bus.done || signal.aborted) {
8451
9050
  bus.listeners.delete(notify);
8452
9051
  signal.removeEventListener("abort", onAbort);
8453
- resolve5();
9052
+ resolve6();
8454
9053
  }
8455
9054
  });
8456
9055
  }
@@ -8563,7 +9162,7 @@ function applyHireDirectivesFromOutput(manager, scenario, output) {
8563
9162
  }
8564
9163
  const hired = [];
8565
9164
  for (const payload of hireDirectives) {
8566
- const id = `hire-${randomUUID6().slice(0, 8)}`;
9165
+ const id = `hire-${randomUUID7().slice(0, 8)}`;
8567
9166
  const position = computeHirePosition(manager, scenario.nodes);
8568
9167
  ensureGroupCanContainNode(scenario, manager.groupId ?? null, position);
8569
9168
  const node = {
@@ -8927,9 +9526,6 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
8927
9526
  model,
8928
9527
  effort: "normal",
8929
9528
  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
9529
  onDelta: (text) => {
8934
9530
  accumulated += text;
8935
9531
  onDelta(text);
@@ -8957,9 +9553,9 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
8957
9553
  publish2({ type: "node_waiting_input", nodeId: node.id, question: result.tokenMessage });
8958
9554
  await readme?.(`- \u2753 [${formatTs()}] **${node.label}** \u2014 waiting for user input
8959
9555
  `);
8960
- const answer = await new Promise((resolve5, reject) => {
9556
+ const answer = await new Promise((resolve6, reject) => {
8961
9557
  const key = pendingNodeKey(conversationId, node.id);
8962
- pendingNodeInputs.set(key, resolve5);
9558
+ pendingNodeInputs.set(key, resolve6);
8963
9559
  signal.addEventListener("abort", () => {
8964
9560
  pendingNodeInputs.delete(key);
8965
9561
  reject(new Error("Aborted"));
@@ -8999,7 +9595,7 @@ async function appendNodeOutput(conversationId, node, output) {
8999
9595
  if (!dir) return;
9000
9596
  const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
9001
9597
  const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
9002
- const filePath = join14(dir, `${slug}_${safeId}.md`);
9598
+ const filePath = join15(dir, `${slug}_${safeId}.md`);
9003
9599
  const content = `## Output
9004
9600
 
9005
9601
  ${output}
@@ -9012,9 +9608,9 @@ async function appendToReadme(conversationId, bus, text) {
9012
9608
  const line = `${text}
9013
9609
  `;
9014
9610
  try {
9015
- await mkdir12(dir, { recursive: true });
9611
+ await mkdir13(dir, { recursive: true });
9016
9612
  const { appendFile: appendFile2 } = await import("fs/promises");
9017
- await appendFile2(join14(dir, "README.md"), line, "utf-8");
9613
+ await appendFile2(join15(dir, "README.md"), line, "utf-8");
9018
9614
  } catch {
9019
9615
  }
9020
9616
  publishToBus(bus, { type: "readme_append", text: line });
@@ -9034,7 +9630,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
9034
9630
 
9035
9631
  ${content}
9036
9632
  `;
9037
- const filePath = join14(dir, "FINAL.md");
9633
+ const filePath = join15(dir, "FINAL.md");
9038
9634
  await atomicWriteSecure(filePath, fullContent);
9039
9635
  await appendToReadme(
9040
9636
  conversationId,
@@ -9364,9 +9960,9 @@ ${outputs.get(node.id) ?? ""}`.trim();
9364
9960
  publish2({ type: "node_waiting_input", nodeId: node.id, question });
9365
9961
  await readme(`- \u2753 [${ts()}] **${node.label || node.id}** \u2014 waiting for user input
9366
9962
  `);
9367
- const answer = await new Promise((resolve5, reject) => {
9963
+ const answer = await new Promise((resolve6, reject) => {
9368
9964
  const key = pendingNodeKey(conversationId, node.id);
9369
- pendingNodeInputs.set(key, resolve5);
9965
+ pendingNodeInputs.set(key, resolve6);
9370
9966
  signal.addEventListener("abort", () => {
9371
9967
  pendingNodeInputs.delete(key);
9372
9968
  reject(new Error("Aborted"));
@@ -9703,21 +10299,21 @@ Modify the scenario. Keep existing ids when possible.`;
9703
10299
  }
9704
10300
  var scenarioRouter = router({
9705
10301
  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({
10302
+ get: protectedProcedure.input(z17.object({ id: Id128Schema })).query(({ input }) => getOrNotFound(() => scenarioStore.get(input.id), "Scenario not found")),
10303
+ create: protectedProcedure.input(z17.object({
9708
10304
  name: NameSchema,
9709
- icon: z16.string().max(64).optional(),
10305
+ icon: z17.string().max(64).optional(),
9710
10306
  description: DescriptionSchema.optional(),
9711
10307
  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()
10308
+ groups: z17.array(ScenarioGroupSchema).max(100).optional(),
10309
+ nodes: z17.array(ScenarioNodeSchema).min(1).max(500),
10310
+ traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
10311
+ presetId: z17.string().max(128).optional(),
10312
+ isPublished: z17.boolean().optional()
9717
10313
  })).mutation(({ input }) => {
9718
10314
  const now = Date.now();
9719
10315
  return createOrBadRequest(() => scenarioStore.create({
9720
- id: randomUUID7(),
10316
+ id: randomUUID8(),
9721
10317
  name: input.name,
9722
10318
  icon: input.icon,
9723
10319
  description: input.description,
@@ -9731,17 +10327,17 @@ var scenarioRouter = router({
9731
10327
  updatedAt: now
9732
10328
  }));
9733
10329
  }),
9734
- update: protectedProcedure.input(z16.object({
10330
+ update: protectedProcedure.input(z17.object({
9735
10331
  id: Id128Schema,
9736
10332
  name: NameSchema.optional(),
9737
- icon: z16.string().max(64).optional(),
10333
+ icon: z17.string().max(64).optional(),
9738
10334
  description: DescriptionSchema.optional(),
9739
10335
  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()
10336
+ groups: z17.array(ScenarioGroupSchema).max(100).optional(),
10337
+ nodes: z17.array(ScenarioNodeSchema).min(1).max(500).optional(),
10338
+ traversalMode: z17.enum(["hierarchical", "topological"]).optional(),
10339
+ presetId: z17.string().max(128).optional(),
10340
+ isPublished: z17.boolean().optional()
9745
10341
  })).mutation(({ input }) => updateOrThrow(
9746
10342
  () => scenarioStore.update(input.id, {
9747
10343
  name: input.name,
@@ -9757,18 +10353,18 @@ var scenarioRouter = router({
9757
10353
  }),
9758
10354
  "Scenario not found"
9759
10355
  )),
9760
- delete: protectedProcedure.input(z16.object({ id: Id128Schema })).mutation(({ input }) => deleteOrNotFound(() => scenarioStore.delete(input.id), "Scenario not found")),
9761
- run: protectedProcedure.input(z16.object({
10356
+ delete: protectedProcedure.input(z17.object({ id: Id128Schema })).mutation(({ input }) => deleteOrNotFound(() => scenarioStore.delete(input.id), "Scenario not found")),
10357
+ run: protectedProcedure.input(z17.object({
9762
10358
  conversationId: ConversationIdSchema,
9763
10359
  scenarioId: Id128Schema,
9764
- userRequest: z16.string().max(1e5),
9765
- runnerOverride: z16.enum(["claude-code", "codex"]).optional(),
10360
+ userRequest: z17.string().max(1e5),
10361
+ runnerOverride: z17.enum(["claude-code", "codex"]).optional(),
9766
10362
  modelOverride: Id128Schema.optional(),
9767
10363
  agentId: Id128Schema.optional(),
9768
- resume: z16.boolean().optional()
10364
+ resume: z17.boolean().optional()
9769
10365
  })).subscription(async function* ({ input, signal }) {
9770
10366
  const scenario = await scenarioStore.get(input.scenarioId);
9771
- if (!scenario) throw new TRPCError14({ code: "NOT_FOUND", message: "Scenario not found" });
10367
+ if (!scenario) throw new TRPCError15({ code: "NOT_FOUND", message: "Scenario not found" });
9772
10368
  const canStart = input.userRequest.trim().length > 0 || !!input.resume;
9773
10369
  if (!isScenarioRunning(input.conversationId) && canStart) {
9774
10370
  let userRequest = input.userRequest;
@@ -9784,52 +10380,54 @@ var scenarioRouter = router({
9784
10380
  }
9785
10381
  yield* tapBus(input.conversationId, signal ?? new AbortController().signal);
9786
10382
  }),
9787
- stop: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
10383
+ stop: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).mutation(({ input }) => {
9788
10384
  abortScenario(input.conversationId);
9789
10385
  return { ok: true };
9790
10386
  }),
9791
- sendNodeInput: protectedProcedure.input(z16.object({
10387
+ sendNodeInput: protectedProcedure.input(z17.object({
9792
10388
  conversationId: ConversationIdSchema,
9793
10389
  nodeId: Id128Schema,
9794
- text: z16.string().min(1).max(1e5)
10390
+ text: z17.string().min(1).max(1e5)
9795
10391
  })).mutation(({ input }) => {
9796
10392
  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" });
10393
+ if (!resolved) throw new TRPCError15({ code: "NOT_FOUND", message: "No pending input request for this node" });
9798
10394
  return { ok: true };
9799
10395
  }),
9800
- isRunning: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => ({ running: isScenarioRunning(input.conversationId) })),
9801
- runState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(({ input }) => {
10396
+ isRunning: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(({ input }) => ({ running: isScenarioRunning(input.conversationId) })),
10397
+ runState: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(({ input }) => {
9802
10398
  const runState = scenarioRunStore.getRunState(input.conversationId);
9803
10399
  return runState ?? { nodeStates: {}, nodeOutputs: {}, activeNodeId: void 0 };
9804
10400
  }),
9805
- getPersistedRunState: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10401
+ getPersistedRunState: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9806
10402
  return await scenarioRunStore.loadPersistedRunState(input.conversationId);
9807
10403
  }),
9808
- getReadme: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10404
+ getReadme: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9809
10405
  return await scenarioRunStore.readReadme(input.conversationId);
9810
10406
  }),
9811
- getSnapshot: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
10407
+ getSnapshot: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).query(async ({ input }) => {
9812
10408
  return await scenarioRunStore.loadScenarioSnapshot(input.conversationId);
9813
10409
  }),
9814
- streamGenerateWithAI: protectedProcedure.input(z16.object({
9815
- prompt: z16.string().min(1).max(1e4),
10410
+ streamGenerateWithAI: protectedProcedure.input(z17.object({
10411
+ prompt: z17.string().min(1).max(1e4),
9816
10412
  providerId: OptionalProviderIdSchema,
9817
- current: z16.object({
10413
+ model: z17.string().max(200).optional(),
10414
+ current: z17.object({
9818
10415
  leaderId: Id128Schema,
9819
- groups: z16.array(ScenarioGroupSchema).max(100),
9820
- nodes: z16.array(ScenarioNodeSchema).max(500)
10416
+ groups: z17.array(ScenarioGroupSchema).max(100),
10417
+ nodes: z17.array(ScenarioNodeSchema).max(500)
9821
10418
  }).optional()
9822
10419
  })).subscription(async function* ({ input, signal }) {
9823
10420
  const userMessage = buildScenarioUserMessage(input.prompt, input.current);
9824
10421
  yield* streamWithActiveProvider({
9825
10422
  providerId: input.providerId,
10423
+ model: input.model,
9826
10424
  systemPrompt: SCENARIO_SYSTEM_PROMPT,
9827
10425
  userMessage,
9828
10426
  maxTokens: 8192,
9829
10427
  signal: signal ?? void 0
9830
10428
  });
9831
10429
  }),
9832
- streamRunEvents: protectedProcedure.input(z16.object({ conversationId: ConversationIdSchema })).subscription(async function* ({ input }) {
10430
+ streamRunEvents: protectedProcedure.input(z17.object({ conversationId: ConversationIdSchema })).subscription(async function* ({ input }) {
9833
10431
  const convId = input.conversationId;
9834
10432
  const runState = scenarioRunStore.getRunState(convId);
9835
10433
  if (runState) {
@@ -9843,24 +10441,24 @@ var scenarioRouter = router({
9843
10441
  * open a debug PTY with `claude --resume <sessionId>` on the right agent.
9844
10442
  * Falls back to the persisted run-state for past nodes.
9845
10443
  */
9846
- getNodeSession: protectedProcedure.input(z16.object({
10444
+ getNodeSession: protectedProcedure.input(z17.object({
9847
10445
  conversationId: ConversationIdSchema,
9848
10446
  nodeId: Id128Schema
9849
10447
  })).query(async ({ input }) => {
9850
10448
  return await scenarioRunStore.getNodeSessionPersistent(input.conversationId, input.nodeId);
9851
10449
  }),
9852
10450
  listPresets: protectedProcedure.query(() => listPresets()),
9853
- getPreset: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => {
10451
+ getPreset: protectedProcedure.input(z17.object({ id: Id128Schema })).query(({ input }) => {
9854
10452
  const preset = getPreset(input.id);
9855
- if (!preset) throw new TRPCError14({ code: "NOT_FOUND", message: "Preset not found" });
10453
+ if (!preset) throw new TRPCError15({ code: "NOT_FOUND", message: "Preset not found" });
9856
10454
  return preset;
9857
10455
  })
9858
10456
  });
9859
10457
 
9860
10458
  // ../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";
10459
+ import { randomUUID as randomUUID9 } from "crypto";
10460
+ import { TRPCError as TRPCError16 } from "@trpc/server";
10461
+ import { z as z18 } from "zod";
9864
10462
  var MAX_BUFFER3 = 300;
9865
10463
  var jobs = /* @__PURE__ */ new Map();
9866
10464
  function publish(job, event) {
@@ -9897,31 +10495,31 @@ async function* tapJob(job, signal) {
9897
10495
  yield evt;
9898
10496
  }
9899
10497
  if (job.done || signal.aborted) return;
9900
- await new Promise((resolve5) => {
10498
+ await new Promise((resolve6) => {
9901
10499
  const onAbort = () => {
9902
10500
  job.listeners.delete(notify);
9903
- resolve5();
10501
+ resolve6();
9904
10502
  };
9905
10503
  const notify = () => {
9906
10504
  signal.removeEventListener("abort", onAbort);
9907
- resolve5();
10505
+ resolve6();
9908
10506
  };
9909
10507
  job.listeners.add(notify);
9910
10508
  signal.addEventListener("abort", onAbort, { once: true });
9911
10509
  if (cursor < job.baseIndex + job.buffer.length || job.done || signal.aborted) {
9912
10510
  job.listeners.delete(notify);
9913
10511
  signal.removeEventListener("abort", onAbort);
9914
- resolve5();
10512
+ resolve6();
9915
10513
  }
9916
10514
  });
9917
10515
  }
9918
10516
  }
9919
10517
  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()
10518
+ startRandomJob: protectedProcedure.input(z18.object({
10519
+ intervalMs: z18.number().int().min(100).max(5e3).optional(),
10520
+ maxTicks: z18.number().int().min(1).max(1e4).optional()
9923
10521
  }).optional()).mutation(({ input }) => {
9924
- const jobId = randomUUID8();
10522
+ const jobId = randomUUID9();
9925
10523
  const intervalMs = input?.intervalMs ?? 400;
9926
10524
  const maxTicks = input?.maxTicks ?? 500;
9927
10525
  const job = {
@@ -9953,15 +10551,15 @@ var debugRouter = router({
9953
10551
  jobs.set(jobId, job);
9954
10552
  return { jobId, intervalMs, maxTicks, startedAt: job.startedAt };
9955
10553
  }),
9956
- streamRandomJob: protectedProcedure.input(z17.object({ jobId: z17.string().max(128) })).subscription(async function* ({ input, signal }) {
10554
+ streamRandomJob: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).subscription(async function* ({ input, signal }) {
9957
10555
  const job = jobs.get(input.jobId);
9958
10556
  if (!job) {
9959
- throw new TRPCError15({ code: "NOT_FOUND", message: "Random job not found" });
10557
+ throw new TRPCError16({ code: "NOT_FOUND", message: "Random job not found" });
9960
10558
  }
9961
10559
  yield* tapJob(job, signal ?? new AbortController().signal);
9962
10560
  }),
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 }) => {
10561
+ stopRandomJob: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).mutation(({ input }) => ({ ok: stopJob(input.jobId, "stopped") })),
10562
+ randomJobStatus: protectedProcedure.input(z18.object({ jobId: z18.string().max(128) })).query(({ input }) => {
9965
10563
  const job = jobs.get(input.jobId);
9966
10564
  return {
9967
10565
  running: !!job && !job.done,
@@ -9970,132 +10568,11 @@ var debugRouter = router({
9970
10568
  })
9971
10569
  });
9972
10570
 
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
10571
  // ../server/src/trpc/routers/kanban-queue.ts
10095
10572
  import { z as z19 } from "zod";
10096
10573
 
10097
10574
  // ../server/src/services/kanban-queue.ts
10098
- import { randomUUID as randomUUID9 } from "crypto";
10575
+ import { randomUUID as randomUUID10 } from "crypto";
10099
10576
  import { EventEmitter as EventEmitter4 } from "events";
10100
10577
  var KanbanQueueService = class extends EventEmitter4 {
10101
10578
  items = [];
@@ -10118,7 +10595,7 @@ var KanbanQueueService = class extends EventEmitter4 {
10118
10595
  return { id: alreadyRunning.id, addedAt: alreadyRunning.startedAt, ...item };
10119
10596
  }
10120
10597
  const queueItem = {
10121
- id: randomUUID9(),
10598
+ id: randomUUID10(),
10122
10599
  ...item,
10123
10600
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
10124
10601
  };
@@ -10310,13 +10787,13 @@ var kanbanQueueRouter = router({
10310
10787
  }),
10311
10788
  onChanged: protectedProcedure.subscription(async function* () {
10312
10789
  const queue = [];
10313
- let resolve5 = null;
10790
+ let resolve6 = null;
10314
10791
  const handler = () => {
10315
10792
  if (queue.length >= 50) queue.shift();
10316
10793
  queue.push(kanbanQueue.getState());
10317
- if (resolve5) {
10318
- resolve5();
10319
- resolve5 = null;
10794
+ if (resolve6) {
10795
+ resolve6();
10796
+ resolve6 = null;
10320
10797
  }
10321
10798
  };
10322
10799
  kanbanQueue.on("changed", handler);
@@ -10327,7 +10804,7 @@ var kanbanQueueRouter = router({
10327
10804
  yield queue.shift();
10328
10805
  } else {
10329
10806
  await new Promise((r) => {
10330
- resolve5 = r;
10807
+ resolve6 = r;
10331
10808
  });
10332
10809
  }
10333
10810
  }
@@ -10337,16 +10814,52 @@ var kanbanQueueRouter = router({
10337
10814
  })
10338
10815
  });
10339
10816
 
10340
- // ../server/src/trpc/routers/terminal.ts
10817
+ // ../server/src/trpc/routers/kanban.ts
10341
10818
  import { z as z20 } from "zod";
10819
+ var kanbanRouter = router({
10820
+ moveCard: protectedProcedure.input(z20.object({
10821
+ boardId: z20.string().max(128),
10822
+ cardId: z20.string().max(128),
10823
+ targetColumn: z20.string().max(256)
10824
+ })).mutation(async ({ input }) => {
10825
+ await executeKanbanCommands(input.boardId, input.cardId, [
10826
+ { type: "move_card", columnTitle: input.targetColumn }
10827
+ ]);
10828
+ return { ok: true };
10829
+ }),
10830
+ addComment: protectedProcedure.input(z20.object({
10831
+ boardId: z20.string().max(128),
10832
+ cardId: z20.string().max(128),
10833
+ comment: z20.string().max(2e4)
10834
+ })).mutation(async ({ input }) => {
10835
+ await executeKanbanCommands(input.boardId, input.cardId, [
10836
+ { type: "add_comment", comment: input.comment }
10837
+ ]);
10838
+ return { ok: true };
10839
+ }),
10840
+ addCard: protectedProcedure.input(z20.object({
10841
+ boardId: z20.string().max(128),
10842
+ column: z20.string().max(256),
10843
+ title: z20.string().max(512),
10844
+ content: z20.string().max(2e4).optional()
10845
+ })).mutation(async ({ input }) => {
10846
+ await executeKanbanBoardCommands(input.boardId, [
10847
+ { type: "add_card", columnTitle: input.column, cardTitle: input.title, cardContent: input.content }
10848
+ ]);
10849
+ return { ok: true };
10850
+ })
10851
+ });
10852
+
10853
+ // ../server/src/trpc/routers/terminal.ts
10854
+ import { z as z21 } from "zod";
10342
10855
  import { TRPCError as TRPCError17 } from "@trpc/server";
10343
10856
  var terminalRouter = router({
10344
10857
  /** Create a PTY shell session on the target agent */
10345
- create: protectedProcedure.input(z20.object({
10346
- agentId: z20.string().max(128),
10347
- cwd: z20.string().max(512).optional(),
10348
- cols: z20.number().int().min(1).max(512).optional(),
10349
- rows: z20.number().int().min(1).max(256).optional()
10858
+ create: protectedProcedure.input(z21.object({
10859
+ agentId: z21.string().max(128),
10860
+ cwd: z21.string().max(512).optional(),
10861
+ cols: z21.number().int().min(1).max(512).optional(),
10862
+ rows: z21.number().int().min(1).max(256).optional()
10350
10863
  })).mutation(async ({ input }) => {
10351
10864
  const result = await agentManager.agentMutation(
10352
10865
  input.agentId,
@@ -10356,10 +10869,10 @@ var terminalRouter = router({
10356
10869
  return result;
10357
10870
  }),
10358
10871
  /** Send keystrokes / data to a PTY session */
10359
- write: protectedProcedure.input(z20.object({
10360
- agentId: z20.string().max(128),
10361
- sessionId: z20.string().max(128),
10362
- data: z20.string().max(65536)
10872
+ write: protectedProcedure.input(z21.object({
10873
+ agentId: z21.string().max(128),
10874
+ sessionId: z21.string().max(128),
10875
+ data: z21.string().max(65536)
10363
10876
  })).mutation(async ({ input }) => {
10364
10877
  await agentManager.agentMutation(
10365
10878
  input.agentId,
@@ -10369,11 +10882,11 @@ var terminalRouter = router({
10369
10882
  return { ok: true };
10370
10883
  }),
10371
10884
  /** Resize the PTY */
10372
- resize: protectedProcedure.input(z20.object({
10373
- agentId: z20.string().max(128),
10374
- sessionId: z20.string().max(128),
10375
- cols: z20.number().int().min(1).max(512),
10376
- rows: z20.number().int().min(1).max(256)
10885
+ resize: protectedProcedure.input(z21.object({
10886
+ agentId: z21.string().max(128),
10887
+ sessionId: z21.string().max(128),
10888
+ cols: z21.number().int().min(1).max(512),
10889
+ rows: z21.number().int().min(1).max(256)
10377
10890
  })).mutation(async ({ input }) => {
10378
10891
  await agentManager.agentMutation(
10379
10892
  input.agentId,
@@ -10383,9 +10896,9 @@ var terminalRouter = router({
10383
10896
  return { ok: true };
10384
10897
  }),
10385
10898
  /** Destroy a PTY session */
10386
- destroy: protectedProcedure.input(z20.object({
10387
- agentId: z20.string().max(128),
10388
- sessionId: z20.string().max(128)
10899
+ destroy: protectedProcedure.input(z21.object({
10900
+ agentId: z21.string().max(128),
10901
+ sessionId: z21.string().max(128)
10389
10902
  })).mutation(async ({ input }) => {
10390
10903
  await agentManager.agentMutation(
10391
10904
  input.agentId,
@@ -10395,12 +10908,12 @@ var terminalRouter = router({
10395
10908
  return { ok: true };
10396
10909
  }),
10397
10910
  /** Stream PTY output — proxied via AgentWSBridge dynamic subscription */
10398
- onData: protectedProcedure.input(z20.object({
10399
- agentId: z20.string().max(128),
10400
- sessionId: z20.string().max(128)
10911
+ onData: protectedProcedure.input(z21.object({
10912
+ agentId: z21.string().max(128),
10913
+ sessionId: z21.string().max(128)
10401
10914
  })).subscription(async function* ({ input }) {
10402
10915
  const queue = [];
10403
- let resolve5 = null;
10916
+ let resolve6 = null;
10404
10917
  let done = false;
10405
10918
  let unsubscribe;
10406
10919
  try {
@@ -10410,16 +10923,16 @@ var terminalRouter = router({
10410
10923
  { sessionId: input.sessionId },
10411
10924
  (data) => {
10412
10925
  if (typeof data === "string") queue.push(data);
10413
- if (resolve5) {
10414
- resolve5();
10415
- resolve5 = null;
10926
+ if (resolve6) {
10927
+ resolve6();
10928
+ resolve6 = null;
10416
10929
  }
10417
10930
  },
10418
10931
  () => {
10419
10932
  done = true;
10420
- if (resolve5) {
10421
- resolve5();
10422
- resolve5 = null;
10933
+ if (resolve6) {
10934
+ resolve6();
10935
+ resolve6 = null;
10423
10936
  }
10424
10937
  }
10425
10938
  );
@@ -10435,7 +10948,7 @@ var terminalRouter = router({
10435
10948
  yield queue.shift();
10436
10949
  } else {
10437
10950
  await new Promise((r) => {
10438
- resolve5 = r;
10951
+ resolve6 = r;
10439
10952
  });
10440
10953
  }
10441
10954
  }
@@ -10446,27 +10959,27 @@ var terminalRouter = router({
10446
10959
  });
10447
10960
 
10448
10961
  // ../server/src/trpc/routers/utility.ts
10449
- import { z as z21 } from "zod";
10962
+ import { z as z22 } from "zod";
10450
10963
  var MAX_WAIT_MS = 5 * 60 * 1e3;
10451
10964
  var utilityRouter = router({
10452
- wait: protectedProcedure.input(z21.object({
10453
- ms: z21.number().int().min(0).max(MAX_WAIT_MS)
10965
+ wait: protectedProcedure.input(z22.object({
10966
+ ms: z22.number().int().min(0).max(MAX_WAIT_MS)
10454
10967
  })).mutation(async ({ input }) => {
10455
- await new Promise((resolve5) => setTimeout(resolve5, input.ms));
10968
+ await new Promise((resolve6) => setTimeout(resolve6, input.ms));
10456
10969
  return { waited: input.ms };
10457
10970
  })
10458
10971
  });
10459
10972
 
10460
10973
  // ../server/src/trpc/routers/wiki.ts
10461
- import { z as z22 } from "zod";
10974
+ import { z as z23 } from "zod";
10462
10975
  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";
10465
- var SCOPE_SCHEMA = z22.union([
10466
- z22.literal("global"),
10467
- z22.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
10976
+ 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";
10977
+ import { resolve as resolvePath, join as join16, relative, sep as sep3, dirname as dirname3 } from "path";
10978
+ var SCOPE_SCHEMA = z23.union([
10979
+ z23.literal("global"),
10980
+ z23.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
10468
10981
  ]);
10469
- var REL_PATH_SCHEMA = z22.string().min(1).max(512).regex(/^[A-Za-z0-9_./-]+$/).refine((p) => !p.includes("..") && !p.startsWith("/"), {
10982
+ var REL_PATH_SCHEMA = z23.string().min(1).max(512).regex(/^[A-Za-z0-9_./-]+$/).refine((p) => !p.includes("..") && !p.startsWith("/"), {
10470
10983
  message: "Path must be a relative path without traversal"
10471
10984
  });
10472
10985
  var MAX_FILES = 500;
@@ -10486,9 +10999,9 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
10486
10999
  "target",
10487
11000
  "out"
10488
11001
  ]);
10489
- var DocsEntrySchema = z22.lazy(() => z22.union([
10490
- z22.object({ type: z22.literal("file"), name: z22.string(), path: z22.string() }),
10491
- z22.object({ type: z22.literal("dir"), name: z22.string(), path: z22.string(), children: z22.array(DocsEntrySchema) })
11002
+ var DocsEntrySchema = z23.lazy(() => z23.union([
11003
+ z23.object({ type: z23.literal("file"), name: z23.string(), path: z23.string() }),
11004
+ z23.object({ type: z23.literal("dir"), name: z23.string(), path: z23.string(), children: z23.array(DocsEntrySchema) })
10492
11005
  ]));
10493
11006
  async function resolveScope(scope) {
10494
11007
  if (scope === "global") {
@@ -10498,13 +11011,13 @@ async function resolveScope(scope) {
10498
11011
  throw new TRPCError18({ code: "BAD_REQUEST", message: "Sous-chemin invalide" });
10499
11012
  }
10500
11013
  const dataDirAbs = resolvePath(env.DATA_DIR);
10501
- await mkdir13(dataDirAbs, { recursive: true });
11014
+ await mkdir14(dataDirAbs, { recursive: true });
10502
11015
  const base = dirname3(await realpath(dataDirAbs));
10503
11016
  const target = resolvePath(base, sub);
10504
11017
  if (target !== base && !(target + sep3).startsWith(base + sep3)) {
10505
11018
  throw new TRPCError18({ code: "FORBIDDEN", message: "Hors du dossier de donn\xE9es du serveur" });
10506
11019
  }
10507
- await mkdir13(target, { recursive: true });
11020
+ await mkdir14(target, { recursive: true });
10508
11021
  const root = await realpath(target);
10509
11022
  if (root !== base && !(root + sep3).startsWith(base + sep3)) {
10510
11023
  throw new TRPCError18({ code: "FORBIDDEN", message: "Le dossier wiki r\xE9sout hors du dossier de donn\xE9es" });
@@ -10531,7 +11044,7 @@ async function walkLocal(root, dir, depth, counter) {
10531
11044
  for (const e of entries) {
10532
11045
  if (counter.n >= MAX_FILES) break;
10533
11046
  if (e.name.startsWith(".") && e.name !== ".") continue;
10534
- const abs = join15(dir, e.name);
11047
+ const abs = join16(dir, e.name);
10535
11048
  const rel = relative(root, abs).split(sep3).join("/");
10536
11049
  if (e.isDirectory()) {
10537
11050
  if (IGNORED_DIRS.has(e.name)) continue;
@@ -10578,7 +11091,7 @@ async function resolveSafeLocal(root, relPath, requireExist) {
10578
11091
  return resolvePath(realParent, target.slice(parent.length).replace(/^[\\/]+/, ""));
10579
11092
  }
10580
11093
  var wikiRouter = router({
10581
- list: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA })).output(z22.object({ root: z22.string(), entries: z22.array(DocsEntrySchema), truncated: z22.boolean() })).query(async ({ input }) => {
11094
+ list: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA })).output(z23.object({ root: z23.string(), entries: z23.array(DocsEntrySchema), truncated: z23.boolean() })).query(async ({ input }) => {
10582
11095
  const target = await resolveScope(input.scope);
10583
11096
  if (target.mode === "agent") {
10584
11097
  return agentManager.agentQuery(target.agentId, "wiki.listRepoMarkdown", { cwd: target.cwd });
@@ -10587,7 +11100,7 @@ var wikiRouter = router({
10587
11100
  const entries = await walkLocal(target.root, target.root, 0, counter);
10588
11101
  return { root: target.root, entries, truncated: counter.n >= MAX_FILES };
10589
11102
  }),
10590
- read: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).output(z22.object({ path: z22.string(), content: z22.string() })).query(async ({ input }) => {
11103
+ read: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).output(z23.object({ path: z23.string(), content: z23.string() })).query(async ({ input }) => {
10591
11104
  if (!/\.(md|markdown)$/i.test(input.path)) {
10592
11105
  throw new TRPCError18({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
10593
11106
  }
@@ -10599,10 +11112,10 @@ var wikiRouter = router({
10599
11112
  const s = await stat3(file);
10600
11113
  if (!s.isFile()) throw new TRPCError18({ code: "BAD_REQUEST", message: "Pas un fichier" });
10601
11114
  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");
11115
+ const content = await readFile13(file, "utf-8");
10603
11116
  return { path: input.path, content };
10604
11117
  }),
10605
- write: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA, content: z22.string().max(5e6) })).mutation(async ({ input }) => {
11118
+ write: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA, content: z23.string().max(5e6) })).mutation(async ({ input }) => {
10606
11119
  if (!/\.(md|markdown)$/i.test(input.path)) {
10607
11120
  throw new TRPCError18({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
10608
11121
  }
@@ -10611,11 +11124,11 @@ var wikiRouter = router({
10611
11124
  return agentManager.agentMutation(target.agentId, "wiki.writeMarkdown", { cwd: target.cwd, path: input.path, content: input.content });
10612
11125
  }
10613
11126
  const file = await resolveSafeLocal(target.root, input.path, false);
10614
- await mkdir13(dirname3(file), { recursive: true });
10615
- await writeFile10(file, input.content, "utf-8");
11127
+ await mkdir14(dirname3(file), { recursive: true });
11128
+ await writeFile11(file, input.content, "utf-8");
10616
11129
  return { path: input.path, ok: true };
10617
11130
  }),
10618
- createFile: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
11131
+ createFile: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
10619
11132
  if (!/\.(md|markdown)$/i.test(input.path)) {
10620
11133
  throw new TRPCError18({ code: "BAD_REQUEST", message: "Seuls les .md/.markdown sont autoris\xE9s" });
10621
11134
  }
@@ -10630,20 +11143,20 @@ var wikiRouter = router({
10630
11143
  } catch (err) {
10631
11144
  if (err instanceof TRPCError18) throw err;
10632
11145
  }
10633
- await mkdir13(dirname3(file), { recursive: true });
10634
- await writeFile10(file, "", "utf-8");
11146
+ await mkdir14(dirname3(file), { recursive: true });
11147
+ await writeFile11(file, "", "utf-8");
10635
11148
  return { path: input.path, ok: true };
10636
11149
  }),
10637
- createDir: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
11150
+ createDir: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
10638
11151
  const target = await resolveScope(input.scope);
10639
11152
  if (target.mode === "agent") {
10640
11153
  return agentManager.agentMutation(target.agentId, "wiki.createDir", { cwd: target.cwd, path: input.path });
10641
11154
  }
10642
11155
  const dir = await resolveSafeLocal(target.root, input.path, false);
10643
- await mkdir13(dir, { recursive: true });
11156
+ await mkdir14(dir, { recursive: true });
10644
11157
  return { path: input.path, ok: true };
10645
11158
  }),
10646
- rename: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, fromPath: REL_PATH_SCHEMA, toPath: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
11159
+ rename: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, fromPath: REL_PATH_SCHEMA, toPath: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
10647
11160
  const target = await resolveScope(input.scope);
10648
11161
  if (target.mode === "agent") {
10649
11162
  return agentManager.agentMutation(target.agentId, "wiki.renamePath", { cwd: target.cwd, fromPath: input.fromPath, toPath: input.toPath });
@@ -10656,11 +11169,11 @@ var wikiRouter = router({
10656
11169
  } catch (err) {
10657
11170
  if (err instanceof TRPCError18) throw err;
10658
11171
  }
10659
- await mkdir13(dirname3(to), { recursive: true });
10660
- await rename9(from, to);
11172
+ await mkdir14(dirname3(to), { recursive: true });
11173
+ await rename10(from, to);
10661
11174
  return { fromPath: input.fromPath, toPath: input.toPath, ok: true };
10662
11175
  }),
10663
- ensureRoot: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA })).mutation(async ({ input }) => {
11176
+ ensureRoot: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA })).mutation(async ({ input }) => {
10664
11177
  if (input.scope === "global") {
10665
11178
  await resolveScope("global");
10666
11179
  return { ok: true };
@@ -10673,7 +11186,7 @@ var wikiRouter = router({
10673
11186
  const cwd = (service.docsPath?.trim() || service.cwd || ".").toString();
10674
11187
  return agentManager.agentMutation(agentId, "wiki.ensureRoot", { cwd });
10675
11188
  }),
10676
- delete: protectedProcedure.input(z22.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
11189
+ delete: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
10677
11190
  const target = await resolveScope(input.scope);
10678
11191
  if (target.mode === "agent") {
10679
11192
  return agentManager.agentMutation(target.agentId, "wiki.deletePath", { cwd: target.cwd, path: input.path });
@@ -10686,83 +11199,24 @@ var wikiRouter = router({
10686
11199
  });
10687
11200
 
10688
11201
  // ../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();
11202
+ var configRouter = router({
11203
+ cloud: publicProcedure.query(() => ({
11204
+ apiUrl: env.CLOUD_URL,
11205
+ appUrl: env.CLOUD_APP_URL
11206
+ }))
11207
+ });
10750
11208
 
10751
- // ../server/src/services/cloud-sync-bridge.ts
10752
- import { randomUUID as randomUUID10 } from "crypto";
10753
- import * as Y from "yjs";
11209
+ // ../server/src/trpc/routers/cloud-sync.ts
11210
+ import { z as z24 } from "zod";
11211
+ import { TRPCError as TRPCError19 } from "@trpc/server";
10754
11212
 
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";
11213
+ // ../server/src/services/cloud-sync-store.ts
11214
+ import { readFile as readFile14, writeFile as writeFile12, rename as rename11, mkdir as mkdir15, chmod as chmod9 } from "fs/promises";
10758
11215
  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;
11216
+ var FILENAME5 = "cloud-sync.json";
11217
+ var CloudSyncStore = class {
11218
+ links = /* @__PURE__ */ new Map();
10763
11219
  loaded = false;
10764
- /** Pending login states (CSRF binding between `start` and the callback). */
10765
- pending = /* @__PURE__ */ new Map();
10766
11220
  getFilePath() {
10767
11221
  return join17(env.DATA_DIR, FILENAME5);
10768
11222
  }
@@ -10770,86 +11224,46 @@ var CloudSessionStore = class {
10770
11224
  if (this.loaded) return;
10771
11225
  try {
10772
11226
  const raw = await readFile14(this.getFilePath(), "utf-8");
10773
- this.session = JSON.parse(raw);
11227
+ const data = JSON.parse(raw);
11228
+ for (const l of data) this.links.set(l.orgId, { orgId: l.orgId, cloudUrl: l.cloudUrl });
10774
11229
  } catch {
10775
11230
  }
10776
11231
  this.loaded = true;
10777
11232
  }
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
11233
  async save() {
10813
11234
  await mkdir15(env.DATA_DIR, { recursive: true });
10814
11235
  const filePath = this.getFilePath();
10815
11236
  const tmpPath = filePath + ".tmp";
10816
- await writeFile12(tmpPath, JSON.stringify(this.session, null, 2), "utf-8");
11237
+ const data = JSON.stringify(Array.from(this.links.values()), null, 2);
11238
+ await writeFile12(tmpPath, data, "utf-8");
10817
11239
  await rename11(tmpPath, filePath);
10818
11240
  await chmod9(filePath, 384);
10819
11241
  }
10820
- /** Store the session token obtained from the one-time-token exchange. */
10821
- async set(cloudUrl, token) {
11242
+ /** Store (or replace) the sync link for an organization. */
11243
+ async upsert(orgId, cloudUrl) {
10822
11244
  await this.load();
10823
- this.session = { cloudUrl, encryptedToken: encryptValue(token) };
11245
+ this.links.set(orgId, { orgId, cloudUrl });
10824
11246
  await this.save();
10825
11247
  }
10826
- /** The decrypted session token, or null when not logged in. */
10827
- async getToken() {
11248
+ async remove(orgId) {
10828
11249
  await this.load();
10829
- if (!this.session) return null;
10830
- try {
10831
- return decryptValue(this.session.encryptedToken);
10832
- } catch {
10833
- return null;
10834
- }
11250
+ if (this.links.delete(orgId)) await this.save();
10835
11251
  }
10836
- async isLinked() {
10837
- return await this.getToken() !== null;
11252
+ /** All sync links (for the bridge on startup). */
11253
+ async list() {
11254
+ await this.load();
11255
+ return Array.from(this.links.values());
10838
11256
  }
10839
- /** Forget the session (sign-out). */
10840
- async clear() {
11257
+ async get(orgId) {
10841
11258
  await this.load();
10842
- this.session = null;
10843
- this.pending.clear();
10844
- try {
10845
- await unlink3(this.getFilePath());
10846
- } catch {
10847
- }
11259
+ return this.links.get(orgId) ?? null;
10848
11260
  }
10849
11261
  };
10850
- var cloudSessionStore = new CloudSessionStore();
11262
+ var cloudSyncStore = new CloudSyncStore();
10851
11263
 
10852
11264
  // ../server/src/services/cloud-sync-bridge.ts
11265
+ import { randomUUID as randomUUID11 } from "crypto";
11266
+ import * as Y from "yjs";
10853
11267
  var LOCAL_ORIGIN = "local-bridge";
10854
11268
  var REMOTE_ORIGIN = "remote";
10855
11269
  var RECONNECT_DELAY_MS = 3e3;
@@ -10865,7 +11279,7 @@ var OrgSyncConnection = class {
10865
11279
  projects = getCollection(this.doc, "projects");
10866
11280
  services = getCollection(this.doc, "services");
10867
11281
  environments = getCollection(this.doc, "environments");
10868
- conn = randomUUID10();
11282
+ conn = randomUUID11();
10869
11283
  link;
10870
11284
  stopped = false;
10871
11285
  initializing = true;
@@ -10965,7 +11379,7 @@ var OrgSyncConnection = class {
10965
11379
  await this.pushUpdate(diff);
10966
11380
  } catch (err) {
10967
11381
  if (err instanceof SyncAuthError) throw err;
10968
- console.error(`[cloud-sync] initial push failed for org ${this.link.orgId}:`, err.message);
11382
+ console.error("[cloud-sync] initial push failed for org %s:", this.link.orgId, err.message);
10969
11383
  }
10970
11384
  }
10971
11385
  async pushUpdate(update) {
@@ -10983,7 +11397,7 @@ var OrgSyncConnection = class {
10983
11397
  this.abort?.abort();
10984
11398
  return;
10985
11399
  }
10986
- if (!this.stopped) console.error(`[cloud-sync] push failed for org ${this.link.orgId}:`, err.message);
11400
+ if (!this.stopped) console.error("[cloud-sync] push failed for org %s:", this.link.orgId, err.message);
10987
11401
  }
10988
11402
  }
10989
11403
  /**
@@ -11005,7 +11419,7 @@ var OrgSyncConnection = class {
11005
11419
  await this.delay(UNAUTHENTICATED_RETRY_MS);
11006
11420
  continue;
11007
11421
  }
11008
- if (!this.stopped) console.error(`[cloud-sync] stream error for org ${this.link.orgId}:`, err.message);
11422
+ if (!this.stopped) console.error("[cloud-sync] stream error for org %s:", this.link.orgId, err.message);
11009
11423
  }
11010
11424
  await this.delay(RECONNECT_DELAY_MS);
11011
11425
  }
@@ -11095,7 +11509,7 @@ var OrgSyncConnection = class {
11095
11509
  });
11096
11510
  await projectStore.addEnvironment(projectId, localEnv.id);
11097
11511
  } catch (err) {
11098
- console.error(`[cloud-sync] env bootstrap failed for project ${projectId}:`, err.message);
11512
+ console.error("[cloud-sync] env bootstrap failed for project %s:", projectId, err.message);
11099
11513
  }
11100
11514
  }
11101
11515
  /**
@@ -11198,7 +11612,7 @@ var CloudSyncBridge = class {
11198
11612
  try {
11199
11613
  await conn.start();
11200
11614
  } catch (err) {
11201
- console.error(`[cloud-sync] failed to start org ${link.orgId}:`, err.message);
11615
+ console.error("[cloud-sync] failed to start org %s:", link.orgId, err.message);
11202
11616
  }
11203
11617
  }
11204
11618
  disconnect(orgId) {
@@ -11215,113 +11629,6 @@ var CloudSyncBridge = class {
11215
11629
  };
11216
11630
  var cloudSyncBridge = new CloudSyncBridge();
11217
11631
 
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
11632
  // ../server/src/services/master-password.ts
11326
11633
  import { randomBytes as randomBytes4, createCipheriv, createDecipheriv } from "crypto";
11327
11634
  import { readFile as readFile15, writeFile as writeFile13, chmod as chmod10, stat as stat4, rename as rename12 } from "fs/promises";
@@ -11492,25 +11799,25 @@ async function hasEncryptedKeyFile(dataDir) {
11492
11799
  async function reconcileOrgCanary(orgId) {
11493
11800
  const key = getEncryptionKey();
11494
11801
  if (!key) {
11495
- throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "Encryption key not loaded." });
11802
+ throw new TRPCError19({ code: "PRECONDITION_FAILED", message: "Encryption key not loaded." });
11496
11803
  }
11497
11804
  const local = await readVerifyFile(env.DATA_DIR);
11498
11805
  if (!local) {
11499
- throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "This machine has no key canary yet." });
11806
+ throw new TRPCError19({ code: "PRECONDITION_FAILED", message: "This machine has no key canary yet." });
11500
11807
  }
11501
11808
  const res = await cloudFetch("/api/auth/organization/canary", {
11502
11809
  method: "POST",
11503
11810
  body: { organizationId: orgId, canary: local }
11504
11811
  });
11505
11812
  if (!res.ok) {
11506
- throw new TRPCError20({ code: "BAD_GATEWAY", message: "Could not reach the organization on the cloud." });
11813
+ throw new TRPCError19({ code: "BAD_GATEWAY", message: "Could not reach the organization on the cloud." });
11507
11814
  }
11508
11815
  const { canary } = await res.json();
11509
11816
  if (typeof canary !== "string") {
11510
- throw new TRPCError20({ code: "BAD_GATEWAY", message: "The cloud returned no key canary." });
11817
+ throw new TRPCError19({ code: "BAD_GATEWAY", message: "The cloud returned no key canary." });
11511
11818
  }
11512
11819
  if (!matchesCanary(canary, key)) {
11513
- throw new TRPCError20({
11820
+ throw new TRPCError19({
11514
11821
  code: "FORBIDDEN",
11515
11822
  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
11823
  });
@@ -11572,8 +11879,8 @@ var appRouter = router({
11572
11879
  scenario: scenarioRouter,
11573
11880
  git: gitRouter,
11574
11881
  debug: debugRouter,
11575
- kanban: kanbanRouter,
11576
11882
  kanbanQueue: kanbanQueueRouter,
11883
+ kanban: kanbanRouter,
11577
11884
  terminal: terminalRouter,
11578
11885
  utility: utilityRouter,
11579
11886
  wiki: wikiRouter,
@@ -11689,7 +11996,7 @@ async function ensureDataGitignore(dataDir) {
11689
11996
  // ../server/src/migrations/migrate-from-legacy.ts
11690
11997
  import { createRequire } from "module";
11691
11998
  import { randomBytes as randomBytes5, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
11692
- import { randomUUID as randomUUID11 } from "crypto";
11999
+ import { randomUUID as randomUUID12 } from "crypto";
11693
12000
  import {
11694
12001
  readFile as readFile16,
11695
12002
  writeFile as writeFile15,
@@ -11831,8 +12138,8 @@ async function atomicWrite(path, data) {
11831
12138
  await rename13(tmp, path);
11832
12139
  }
11833
12140
  async function runLegacyMigration(dataDir) {
11834
- const { resolve: resolve5 } = await import("path");
11835
- const abCwd = resolve5(process.cwd());
12141
+ const { resolve: resolve6 } = await import("path");
12142
+ const abCwd = resolve6(process.cwd());
11836
12143
  const legacyRoot = join21(abCwd, env.DATA_DIR);
11837
12144
  const legacyKeyPath = join21(legacyRoot, "dbs", "encryption-key.json");
11838
12145
  try {
@@ -11901,11 +12208,11 @@ async function runLegacyMigration(dataDir) {
11901
12208
  const data = await readEncrypted(join21(parsersDir, file));
11902
12209
  if (data?.id) legacyParsers.push(data);
11903
12210
  }
11904
- const projectMonoId = randomUUID11();
12211
+ const projectMonoId = randomUUID12();
11905
12212
  const envIdMap = {};
11906
12213
  const labelToKey = {};
11907
12214
  for (const [name, legacyEnv] of Object.entries(legacyEnvs)) {
11908
- envIdMap[name] = randomUUID11();
12215
+ envIdMap[name] = randomUUID12();
11909
12216
  if (legacyEnv?.label) labelToKey[legacyEnv.label] = name;
11910
12217
  }
11911
12218
  const runeEnvs = [];
@@ -11930,7 +12237,7 @@ async function runLegacyMigration(dataDir) {
11930
12237
  if (hasInvalidInterpolation(value)) continue;
11931
12238
  const cleaned = cleanVarKey(key);
11932
12239
  if (inheritedVars[cleaned] === value) continue;
11933
- const varId = randomUUID11();
12240
+ const varId = randomUUID12();
11934
12241
  variables[varId] = makeVariable(cleaned, value);
11935
12242
  varIdLookup.set(`${envIdMap[name]}:${cleaned}`, varId);
11936
12243
  }
@@ -11951,7 +12258,7 @@ async function runLegacyMigration(dataDir) {
11951
12258
  const serviceIdMap = {};
11952
12259
  const runeServices = [];
11953
12260
  for (const [name, legacy] of Object.entries(legacyServices)) {
11954
- const id = randomUUID11();
12261
+ const id = randomUUID12();
11955
12262
  serviceIdMap[name] = id;
11956
12263
  const rootPath = legacy.rootPath || "";
11957
12264
  const rootPathCwd = rootPath && rootPath !== "." ? rootPath : "";
@@ -11960,7 +12267,7 @@ async function runLegacyMigration(dataDir) {
11960
12267
  const parts = [cmd.spawnCmd, ...cmd.spawnArgs || []].filter(Boolean);
11961
12268
  if (!parts.length) continue;
11962
12269
  commands.push({
11963
- id: cmd.id || randomUUID11(),
12270
+ id: cmd.id || randomUUID12(),
11964
12271
  label: "Launch",
11965
12272
  command: parts.join(" "),
11966
12273
  cwd: cmd.spawnOptions?.cwd || "",
@@ -12021,7 +12328,7 @@ async function runLegacyMigration(dataDir) {
12021
12328
  const legacyBootstrapCmds = container.bootstrap?.commands || [];
12022
12329
  if (legacyBootstrapCmds.length > 0) {
12023
12330
  dockerConfig.bootstrapCommands = legacyBootstrapCmds.filter((bs) => bs.cmd || bs.entrypoint).map((bs, idx) => ({
12024
- id: bs.id || randomUUID11(),
12331
+ id: bs.id || randomUUID12(),
12025
12332
  label: bs.label || `Bootstrap ${idx + 1}`,
12026
12333
  command: bs.cmd || "",
12027
12334
  ...bs.entrypoint ? { entrypoint: bs.entrypoint } : {},
@@ -12069,7 +12376,7 @@ async function runLegacyMigration(dataDir) {
12069
12376
  cwd: serviceCwd,
12070
12377
  runner: dockerConfig ? "docker" : "native",
12071
12378
  shortcuts: (legacy.shortcuts || []).filter((s) => s.spawnCmd || s.label).map((s) => ({
12072
- id: s.id || randomUUID11(),
12379
+ id: s.id || randomUUID12(),
12073
12380
  label: s.label || s.spawnCmd || "",
12074
12381
  command: [s.spawnCmd, ...s.spawnArgs || []].filter(Boolean).join(" "),
12075
12382
  ...s.spawnOptions?.cwd ? { cwd: s.spawnOptions.cwd } : {}
@@ -12109,7 +12416,7 @@ async function runLegacyMigration(dataDir) {
12109
12416
  );
12110
12417
  if (!svcScopedEnv) {
12111
12418
  svcScopedEnv = {
12112
- id: randomUUID11(),
12419
+ id: randomUUID12(),
12113
12420
  projectId: projectMonoId,
12114
12421
  name: `${service?.name || name} (${globalEnv.name})`,
12115
12422
  scope: "service",
@@ -12133,7 +12440,7 @@ async function runLegacyMigration(dataDir) {
12133
12440
  const varPost = typeof varData === "object" ? String(varData.suffix ?? "") : "";
12134
12441
  if (!varKey || existingKeys.has(cleanVarKey(varKey))) continue;
12135
12442
  const cleaned = cleanVarKey(varKey);
12136
- const varId = randomUUID11();
12443
+ const varId = randomUUID12();
12137
12444
  svcScopedEnv.variables[varId] = makeVariable(cleaned, varValue, varPre, varPost);
12138
12445
  existingKeys.add(cleaned);
12139
12446
  }
@@ -12153,7 +12460,7 @@ async function runLegacyMigration(dataDir) {
12153
12460
  );
12154
12461
  if (!svcScopedEnv) {
12155
12462
  svcScopedEnv = {
12156
- id: randomUUID11(),
12463
+ id: randomUUID12(),
12157
12464
  projectId: projectMonoId,
12158
12465
  name: `${service?.name || serviceName} (${globalEnv.name})`,
12159
12466
  scope: "service",
@@ -12174,7 +12481,7 @@ async function runLegacyMigration(dataDir) {
12174
12481
  const baseEntry = Object.entries(svcScopedEnv.variables).find(([, v]) => v.key === cleaned);
12175
12482
  let varId = baseEntry?.[0];
12176
12483
  if (!varId) {
12177
- varId = randomUUID11();
12484
+ varId = randomUUID12();
12178
12485
  svcScopedEnv.variables[varId] = {
12179
12486
  key: cleaned,
12180
12487
  value: { value: "", pre: "", post: "", isSecret: false, injectIntoAi: false }
@@ -12204,13 +12511,13 @@ async function runLegacyMigration(dataDir) {
12204
12511
  flatOverrides[varId] = makeVariable(cleaned, value);
12205
12512
  }
12206
12513
  } else {
12207
- flatOverrides[randomUUID11()] = makeVariable(cleaned, value);
12514
+ flatOverrides[randomUUID12()] = makeVariable(cleaned, value);
12208
12515
  }
12209
12516
  } else {
12210
12517
  const cleaned = cleanVarKey(key);
12211
12518
  const existingVarId = Object.entries(env2.variables).find(([, v]) => v.key === cleaned)?.[0];
12212
12519
  if (!existingVarId) {
12213
- const varId = randomUUID11();
12520
+ const varId = randomUUID12();
12214
12521
  env2.variables[varId] = makeVariable(cleaned, value);
12215
12522
  varIdLookup.set(`${envId}:${cleaned}`, varId);
12216
12523
  }
@@ -12298,9 +12605,9 @@ async function findLegacyKeyInBackup(dataDir) {
12298
12605
  return null;
12299
12606
  }
12300
12607
  async function runOverrideOnlyMigration(dataDir) {
12301
- const { resolve: resolve5 } = await import("path");
12302
- const abDataDir = resolve5(dataDir);
12303
- const abCwd = resolve5(process.cwd());
12608
+ const { resolve: resolve6 } = await import("path");
12609
+ const abDataDir = resolve6(dataDir);
12610
+ const abCwd = resolve6(process.cwd());
12304
12611
  const candidateDirs = [
12305
12612
  join21(abCwd, ".runeya", "dbs", "overrides"),
12306
12613
  join21(abDataDir, "dbs", "overrides")
@@ -12395,7 +12702,7 @@ async function runOverrideOnlyMigration(dataDir) {
12395
12702
  if (isSameAsBase(value, baseVar)) continue;
12396
12703
  flatOverrides[varId] = makeVariable(cleaned, value);
12397
12704
  } else {
12398
- flatOverrides[randomUUID11()] = makeVariable(cleaned, value);
12705
+ flatOverrides[randomUUID12()] = makeVariable(cleaned, value);
12399
12706
  }
12400
12707
  }
12401
12708
  }
@@ -12432,7 +12739,7 @@ async function runOverrideOnlyMigration(dataDir) {
12432
12739
  );
12433
12740
  if (!svcScopedEnv) {
12434
12741
  svcScopedEnv = {
12435
- id: randomUUID11(),
12742
+ id: randomUUID12(),
12436
12743
  projectId: globalEnv["projectId"],
12437
12744
  name: `${service?.["name"] || serviceName} (${globalEnv["name"]})`,
12438
12745
  scope: "service",
@@ -12454,7 +12761,7 @@ async function runOverrideOnlyMigration(dataDir) {
12454
12761
  const baseEntry = Object.entries(svcScopedEnv["variables"]).find(([, v]) => v.key === cleaned);
12455
12762
  let varId = baseEntry?.[0];
12456
12763
  if (!varId) {
12457
- varId = randomUUID11();
12764
+ varId = randomUUID12();
12458
12765
  svcScopedEnv["variables"][varId] = {
12459
12766
  key: cleaned,
12460
12767
  value: { value: "", pre: "", post: "", isSecret: false, injectIntoAi: false }
@@ -12903,7 +13210,7 @@ async function createLocalServer() {
12903
13210
  }
12904
13211
  }
12905
13212
  if (!existsSync2(env.DATA_DIR)) await mkdir17(env.DATA_DIR);
12906
- const absoluteDataDir = resolve4(env.DATA_DIR);
13213
+ const absoluteDataDir = resolve5(env.DATA_DIR);
12907
13214
  console.log(`[server] Data directory: ${absoluteDataDir}`);
12908
13215
  await ensureDataGitignore(env.DATA_DIR);
12909
13216
  await settingsManager.getSettings();
@@ -13012,7 +13319,102 @@ async function createLocalServer() {
13012
13319
  res.status(502).send("Could not reach the Runeya cloud.");
13013
13320
  }
13014
13321
  });
13015
- app.get("/api/cloud/chat/stream", async (req, res) => {
13322
+ function proxyCloudStream(localPath, upstreamPath) {
13323
+ app.get(localPath, async (req, res) => {
13324
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13325
+ try {
13326
+ await verifyJwt(jwt);
13327
+ } catch {
13328
+ res.status(401).end();
13329
+ return;
13330
+ }
13331
+ const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13332
+ if (!organizationId) {
13333
+ res.status(400).end();
13334
+ return;
13335
+ }
13336
+ const token = await cloudSessionStore.getToken();
13337
+ if (!token) {
13338
+ res.status(401).end();
13339
+ return;
13340
+ }
13341
+ const upstreamAbort = new AbortController();
13342
+ req.on("close", () => upstreamAbort.abort());
13343
+ try {
13344
+ const upstream = await fetch(
13345
+ `${getCloudApiUrl()}${upstreamPath}?organizationId=${encodeURIComponent(organizationId)}`,
13346
+ {
13347
+ headers: { Authorization: `Bearer ${token}`, Accept: "text/event-stream" },
13348
+ signal: upstreamAbort.signal
13349
+ }
13350
+ );
13351
+ if (!upstream.ok || !upstream.body) {
13352
+ res.status(upstream.status === 401 ? 401 : 502).end();
13353
+ return;
13354
+ }
13355
+ res.writeHead(200, {
13356
+ "Content-Type": "text/event-stream",
13357
+ "Cache-Control": "no-cache",
13358
+ Connection: "keep-alive",
13359
+ "X-Accel-Buffering": "no"
13360
+ });
13361
+ const reader = upstream.body.getReader();
13362
+ for (; ; ) {
13363
+ const { done, value } = await reader.read();
13364
+ if (done) break;
13365
+ res.write(value);
13366
+ }
13367
+ res.end();
13368
+ } catch {
13369
+ if (!res.headersSent) res.status(502).end();
13370
+ else res.end();
13371
+ }
13372
+ });
13373
+ }
13374
+ app.post(
13375
+ "/api/cloud/kanban/image",
13376
+ express.raw({ type: () => true, limit: "64mb" }),
13377
+ async (req, res) => {
13378
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13379
+ try {
13380
+ await verifyJwt(jwt);
13381
+ } catch {
13382
+ res.status(401).end();
13383
+ return;
13384
+ }
13385
+ const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13386
+ if (!organizationId) {
13387
+ res.status(400).end();
13388
+ return;
13389
+ }
13390
+ const token = await cloudSessionStore.getToken();
13391
+ if (!token) {
13392
+ res.status(401).end();
13393
+ return;
13394
+ }
13395
+ const upstreamQuery = new URLSearchParams({ organizationId });
13396
+ if (typeof req.query["name"] === "string") upstreamQuery.set("name", req.query["name"]);
13397
+ try {
13398
+ const upstream = await fetch(
13399
+ `${getCloudApiUrl()}/api/kanban/image?${upstreamQuery.toString()}`,
13400
+ {
13401
+ method: "POST",
13402
+ headers: {
13403
+ Authorization: `Bearer ${token}`,
13404
+ "Content-Type": String(req.headers["content-type"] ?? "application/octet-stream")
13405
+ },
13406
+ body: req.body,
13407
+ // A 55 MB body over a home upstream link takes minutes, not seconds.
13408
+ signal: AbortSignal.timeout(3e5)
13409
+ }
13410
+ );
13411
+ res.status(upstream.status).json(await upstream.json().catch(() => ({})));
13412
+ } catch {
13413
+ res.status(502).json({ error: "upload-failed" });
13414
+ }
13415
+ }
13416
+ );
13417
+ app.delete("/api/cloud/kanban/file/:id", async (req, res) => {
13016
13418
  const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13017
13419
  try {
13018
13420
  await verifyJwt(jwt);
@@ -13020,9 +13422,27 @@ async function createLocalServer() {
13020
13422
  res.status(401).end();
13021
13423
  return;
13022
13424
  }
13023
- const organizationId = typeof req.query["organizationId"] === "string" ? req.query["organizationId"] : "";
13024
- if (!organizationId) {
13025
- res.status(400).end();
13425
+ const token = await cloudSessionStore.getToken();
13426
+ if (!token) {
13427
+ res.status(401).end();
13428
+ return;
13429
+ }
13430
+ try {
13431
+ const upstream = await fetch(
13432
+ `${getCloudApiUrl()}/api/kanban/file/${encodeURIComponent(req.params.id)}`,
13433
+ { method: "DELETE", headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(15e3) }
13434
+ );
13435
+ res.status(upstream.status).end();
13436
+ } catch {
13437
+ res.status(502).end();
13438
+ }
13439
+ });
13440
+ app.get("/api/cloud/kanban/file/:id", async (req, res) => {
13441
+ const jwt = typeof req.query["token"] === "string" ? req.query["token"] : "";
13442
+ try {
13443
+ await verifyJwt(jwt);
13444
+ } catch {
13445
+ res.status(401).end();
13026
13446
  return;
13027
13447
  }
13028
13448
  const token = await cloudSessionStore.getToken();
@@ -13030,38 +13450,27 @@ async function createLocalServer() {
13030
13450
  res.status(401).end();
13031
13451
  return;
13032
13452
  }
13033
- const upstreamAbort = new AbortController();
13034
- req.on("close", () => upstreamAbort.abort());
13035
13453
  try {
13036
13454
  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
- }
13455
+ `${getCloudApiUrl()}/api/kanban/file/${encodeURIComponent(req.params.id)}`,
13456
+ { headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(3e5) }
13042
13457
  );
13043
- if (!upstream.ok || !upstream.body) {
13044
- res.status(upstream.status === 401 ? 401 : 502).end();
13458
+ if (!upstream.ok) {
13459
+ res.status(upstream.status).end();
13045
13460
  return;
13046
13461
  }
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();
13462
+ res.set("Content-Type", upstream.headers.get("content-type") ?? "application/octet-stream");
13463
+ const disposition = upstream.headers.get("content-disposition");
13464
+ if (disposition) res.set("Content-Disposition", disposition);
13465
+ res.set("X-Content-Type-Options", "nosniff");
13466
+ res.set("Content-Security-Policy", "default-src 'none'; sandbox");
13467
+ res.send(Buffer.from(await upstream.arrayBuffer()));
13060
13468
  } catch {
13061
- if (!res.headersSent) res.status(502).end();
13062
- else res.end();
13469
+ res.status(502).end();
13063
13470
  }
13064
13471
  });
13472
+ proxyCloudStream("/api/cloud/chat/stream", "/api/chat/stream");
13473
+ proxyCloudStream("/api/cloud/kanban/stream", "/api/kanban/stream");
13065
13474
  app.post("/api/unlock", authLimiter, csrfMiddleware, express.json({ limit: "10kb" }), async (req, res) => {
13066
13475
  try {
13067
13476
  const body = req.body;
@@ -13242,7 +13651,18 @@ async function createLocalServer() {
13242
13651
  });
13243
13652
  const CONV_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
13244
13653
  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) => {
13654
+ const uploadSingleImage = (req, res, next) => {
13655
+ upload.single("image")(req, res, (err) => {
13656
+ if (err) {
13657
+ const isTooLarge = err.code === "LIMIT_FILE_SIZE";
13658
+ const message = err instanceof Error ? err.message : "Upload failed";
13659
+ res.status(isTooLarge ? 413 : 400).json({ error: message });
13660
+ return;
13661
+ }
13662
+ next();
13663
+ });
13664
+ };
13665
+ app.post("/api/images/upload", uploadSingleImage, async (req, res) => {
13246
13666
  try {
13247
13667
  if (!req.file) {
13248
13668
  res.status(400).json({ error: "No image file provided" });
@@ -13280,7 +13700,7 @@ async function createLocalServer() {
13280
13700
  res.status(400).json({ error: "Invalid imageId" });
13281
13701
  return;
13282
13702
  }
13283
- const result = await imageStorageService.getImagePath(imageId);
13703
+ const result = await imageStorageService.getImagePath(imageId, conversationId);
13284
13704
  if (!result) {
13285
13705
  res.status(404).json({ error: "Image not found" });
13286
13706
  return;
@@ -13362,6 +13782,7 @@ async function createLocalServer() {
13362
13782
  const shutdown = async () => {
13363
13783
  console.log("[server] Shutting down...");
13364
13784
  clearInterval(wsTickInterval);
13785
+ stopClaudeTranscriptWatcher();
13365
13786
  imageCleanupService.stop();
13366
13787
  cloudSyncBridge.stopAll();
13367
13788
  await Promise.all([
@@ -13370,12 +13791,12 @@ async function createLocalServer() {
13370
13791
  ]);
13371
13792
  server.closeAllConnections();
13372
13793
  await Promise.race([
13373
- new Promise((resolve5) => wss.close(() => resolve5())),
13374
- new Promise((resolve5) => setTimeout(resolve5, 2e3))
13794
+ new Promise((resolve6) => wss.close(() => resolve6())),
13795
+ new Promise((resolve6) => setTimeout(resolve6, 2e3))
13375
13796
  ]);
13376
13797
  await Promise.race([
13377
- new Promise((resolve5) => server.close(() => resolve5())),
13378
- new Promise((resolve5) => setTimeout(resolve5, 2e3))
13798
+ new Promise((resolve6) => server.close(() => resolve6())),
13799
+ new Promise((resolve6) => setTimeout(resolve6, 2e3))
13379
13800
  ]);
13380
13801
  console.log("[server] Shutdown complete");
13381
13802
  };
@@ -13396,7 +13817,7 @@ var PullEnvError = class extends Error {
13396
13817
  };
13397
13818
  async function promptPassword() {
13398
13819
  process.stderr.write("Master password: ");
13399
- return new Promise((resolve5) => {
13820
+ return new Promise((resolve6) => {
13400
13821
  const stdin = process.stdin;
13401
13822
  const wasRaw = stdin.isRaw;
13402
13823
  if (stdin.isTTY) stdin.setRawMode(true);
@@ -13409,7 +13830,7 @@ async function promptPassword() {
13409
13830
  stdin.pause();
13410
13831
  stdin.removeListener("data", onData);
13411
13832
  process.stderr.write("\n");
13412
- resolve5(input);
13833
+ resolve6(input);
13413
13834
  } else if (ch === "") {
13414
13835
  process.exit(1);
13415
13836
  } else if (ch === "\x7F" || ch === "\b") {
@@ -13466,13 +13887,13 @@ async function pullEnv(opts) {
13466
13887
 
13467
13888
  // ../server/src/index.ts
13468
13889
  dotenv.config({ quiet: true });
13469
- if (process.env["DATA_DIR"]) {
13470
- const resolved = resolvePath2(process.env["DATA_DIR"], "..");
13890
+ if (process.env["RUNEYA_DATA_DIR"]) {
13891
+ const resolved = resolvePath2(process.env["RUNEYA_DATA_DIR"], "..");
13471
13892
  process.chdir(resolved);
13472
- console.log(`[server] Working directory: ${resolved} (from DATA_DIR)`);
13893
+ console.log(`[server] Working directory: ${resolved} (from RUNEYA_DATA_DIR)`);
13473
13894
  }
13474
13895
  function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500) {
13475
- return new Promise((resolve5, reject) => {
13896
+ return new Promise((resolve6, reject) => {
13476
13897
  let attempts = 0;
13477
13898
  const wssErrorHandler = () => {
13478
13899
  };
@@ -13493,7 +13914,7 @@ function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500)
13493
13914
  server.listen(port, host, () => {
13494
13915
  server.removeListener("error", onError);
13495
13916
  wss?.removeListener("error", wssErrorHandler);
13496
- resolve5();
13917
+ resolve6();
13497
13918
  });
13498
13919
  };
13499
13920
  tryListen();
@@ -13540,4 +13961,4 @@ export {
13540
13961
  createLocalServer,
13541
13962
  pullEnv
13542
13963
  };
13543
- //# sourceMappingURL=src-K32WI425.js.map
13964
+ //# sourceMappingURL=src-EGBDYHL5.js.map