@runeya/runeya 2.0.10 → 2.0.13

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 (278) hide show
  1. package/agent/index.js +434 -28
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-3DHUF2DC-KN6O7WSA.js +8 -0
  4. package/{chunk-DODGBBTE.js → chunk-AJW7LDO5.js} +28 -8
  5. package/chunk-AJW7LDO5.js.map +1 -0
  6. package/{dist-HZBWBT6S.js → dist-HGCFHXO4.js} +1308 -408
  7. package/dist-HGCFHXO4.js.map +1 -0
  8. package/index.js +5 -1
  9. package/index.js.map +1 -1
  10. package/mcp-server/index.js +139 -1
  11. package/mcp-server/index.js.map +1 -1
  12. package/package.json +1 -1
  13. package/web/assets/{AddressBar.vue-D_8vQPIF.js → AddressBar.vue-B6BOLYmO.js} +1 -1
  14. package/web/assets/AgentScalarChatInterface.vue-DKSmjtvV.js +13 -0
  15. package/web/assets/AgentTerminalPage-BZ6jaRAs.js +1 -0
  16. package/web/assets/AgentTerminalPage-anYDkj7H.css +1 -0
  17. package/web/assets/AiChatPage-C1r42kX7.js +1 -0
  18. package/web/assets/AppLayout-B7-y0tBI.css +1 -0
  19. package/web/assets/AppLayout-DnMCPCO8.js +1 -0
  20. package/web/assets/AuditPage-CTdEvLE-.js +1 -0
  21. package/web/assets/{CodeInput.vue-nIhSM0FX.js → CodeInput.vue-Sp2dM8hl.js} +1 -1
  22. package/web/assets/{Collection.vue-CkwnxBu4.js → Collection.vue-CdPjaC9d.js} +1 -1
  23. package/web/assets/{CollectionAuthentication.vue-CiZinGpr.js → CollectionAuthentication.vue-DMPykUah.js} +1 -1
  24. package/web/assets/CollectionCookies.vue-DqNgVtd-.js +1 -0
  25. package/web/assets/{CollectionEnvironment.vue-3bWtuG1B.js → CollectionEnvironment.vue-DKab6xJJ.js} +1 -1
  26. package/web/assets/{CollectionOverview.vue-BheZFfD3.js → CollectionOverview.vue-DlODh8Vk.js} +1 -1
  27. package/web/assets/CollectionScripts.vue-B2gkfo4d.js +1 -0
  28. package/web/assets/{CollectionServers.vue-Ja8mYDaK.js → CollectionServers.vue-Br-QackN.js} +1 -1
  29. package/web/assets/{CollectionSettings.vue-DOB9JUg8.js → CollectionSettings.vue-Cm6NQbDm.js} +1 -1
  30. package/web/assets/CollectionSync.vue-bB7sdahP.js +1 -0
  31. package/web/assets/{CommandActionInput.vue-BBijVqe7.js → CommandActionInput.vue-CZ3-rqSL.js} +1 -1
  32. package/web/assets/{Cookies.vue-DgVzSolJ.js → Cookies.vue-Duz-a-ga.js} +1 -1
  33. package/web/assets/DashboardPage-hIc9wP_Q.js +1 -0
  34. package/web/assets/{DataTable.vue-DXTWz2Vi.js → DataTable.vue-Dorr-r7J.js} +1 -1
  35. package/web/assets/{DataTableCheckbox.vue-BA5yHmxd.js → DataTableCheckbox.vue-CATn-YYp.js} +1 -1
  36. package/web/assets/{DataTableHeader.vue-U2le7KBO.js → DataTableHeader.vue-C3SUSaR8.js} +1 -1
  37. package/web/assets/{DataTableInput.vue-DpeNUUXc.js → DataTableInput.vue-_06pwPUA.js} +1 -1
  38. package/web/assets/{DeleteSidebarListElement.vue-C2SV2geU.js → DeleteSidebarListElement.vue-DoqRnMXx.js} +1 -1
  39. package/web/assets/{EditSidebarListElement.vue-DvvYjTMO.js → EditSidebarListElement.vue-BqTw6Tho.js} +1 -1
  40. package/web/assets/{EditorIcon-BPEaZrXC.js → EditorIcon-CbpIT0A1.js} +1 -1
  41. package/web/assets/{EmptyState.vue-Be8_w2rR.js → EmptyState.vue-Bur5L0a4.js} +1 -1
  42. package/web/assets/{Environment.vue-D0PdeVIp.js → Environment.vue-C50lH9cP.js} +1 -1
  43. package/web/assets/{EnvironmentForm-Dm3dFrSU.js → EnvironmentForm-BDfzXuHx.js} +2 -2
  44. package/web/assets/EnvironmentFormPage-CLylRN6B.js +1 -0
  45. package/web/assets/EnvironmentListPage-DlUT6LnX.js +1 -0
  46. package/web/assets/{EnvironmentModal.vue-Bssf0U5X.js → EnvironmentModal.vue-Dz5CArRX.js} +1 -1
  47. package/web/assets/EnvironmentSelector-DHoYUCTH.js +1 -0
  48. package/web/assets/{Form.vue-Dv4a31zh.js → Form.vue-BzZCMgN1.js} +1 -1
  49. package/web/assets/{IconSelector.vue-BsYvff6K.js → IconSelector.vue-BCmeqlrs.js} +1 -1
  50. package/web/assets/KanbanBoardPage-DoXgFMvo.js +141 -0
  51. package/web/assets/{KanbanBoardPage-7nOYpwHn.css → KanbanBoardPage-hqNojI5j.css} +1 -1
  52. package/web/assets/KanbanListPage-BLdNz80q.js +1 -0
  53. package/web/assets/LaunchProjectPage-DjRA2D3h.js +1 -0
  54. package/web/assets/LaunchServicesPage-CKaGcu3z.js +1 -0
  55. package/web/assets/LogViewer-CtX_6uFM.js +3 -0
  56. package/web/assets/{ProcessControls-DfFxf6jZ.js → ProcessControls-BY-_9zo7.js} +1 -1
  57. package/web/assets/ProcessDetailPage-Bqis1qMA.js +1 -0
  58. package/web/assets/{ProcessMetrics-CLa8mdh2.js → ProcessMetrics-DQybvwcA.js} +2 -2
  59. package/web/assets/ProjectDetailPage-FBPonwd5.js +1 -0
  60. package/web/assets/ProjectListPage-xwlVXN8A.js +1 -0
  61. package/web/assets/{RandomJobTestPage-D2OB5pqD.js → RandomJobTestPage-0pqne-FD.js} +1 -1
  62. package/web/assets/{Request.vue-DHQd9eyp.js → Request.vue-Db4XEPKt.js} +1 -1
  63. package/web/assets/{RequestAuth.vue-CEVOgzEk.js → RequestAuth.vue-CK1A6ySt.js} +1 -1
  64. package/web/assets/RequestRoot.vue-BLF0GGT5.js +19 -0
  65. package/web/assets/{ScalarAsciiArt.vue-CyYmv9BB.js → ScalarAsciiArt.vue-BPdUsXWj.js} +1 -1
  66. package/web/assets/{ScalarIconEyeSlash.vue-qR90sHHe.js → ScalarIconEyeSlash.vue-BBDBShRO.js} +1 -1
  67. package/web/assets/{ScalarIconTrash.vue-Ll6Zw6_W.js → ScalarIconTrash.vue-Tpr15aNY.js} +1 -1
  68. package/web/assets/ScenarioEditorPage-DNuo-2Gi.js +14 -0
  69. package/web/assets/{ServerVariablesForm.vue-D9sjD4AT.js → ServerVariablesForm.vue-XLQ145gw.js} +1 -1
  70. package/web/assets/ServiceControlsBar-CCTGcA1t.js +1 -0
  71. package/web/assets/{ServiceDetailPage-znbWajpt.css → ServiceDetailPage-BELECRic.css} +1 -1
  72. package/web/assets/ServiceDetailPage-UVg73scN.js +2 -0
  73. package/web/assets/ServiceFormPage-F3boRKs-.js +1 -0
  74. package/web/assets/{Settings.vue-DWPIjz57.js → Settings.vue-D_1yQu6i.js} +1 -1
  75. package/web/assets/SettingsPage-Cg78lQZF.js +225 -0
  76. package/web/assets/SetupKeyPage-BcaSoLjU.js +1 -0
  77. package/web/assets/SetupPage-mCVAKLtm.js +1 -0
  78. package/web/assets/{Sidebar.vue-DVHNacfA.js → Sidebar.vue-De4YlW0c.js} +1 -1
  79. package/web/assets/{SidebarButton.vue-C4QDgSym.js → SidebarButton.vue-CctYiTRp.js} +1 -1
  80. package/web/assets/{SidebarListElement.vue-Ch1aI_Bz.js → SidebarListElement.vue-Cv7jUZIM.js} +1 -1
  81. package/web/assets/UnlockPage-BSpzyZnd.js +1 -0
  82. package/web/assets/VariablesTable-Dn3IvplD.js +1 -0
  83. package/web/assets/{VariablesTable-BtdK5olT.css → VariablesTable-W4rxlL35.css} +1 -1
  84. package/web/assets/{ViewLayout.vue-svdAMKVP.js → ViewLayout.vue-fiagi8zc.js} +1 -1
  85. package/web/assets/{ViewLayoutContent.vue-CAixUN8B.js → ViewLayoutContent.vue-BehGrsED.js} +1 -1
  86. package/web/assets/{ViewLayoutSection.vue-C9VAyihF.js → ViewLayoutSection.vue-D4icCgQI.js} +1 -1
  87. package/web/assets/{active-entities-Dpd4ny38.js → active-entities-CkBGjwu9.js} +1 -1
  88. package/web/assets/api-reference-D1EfYtPj.js +1 -0
  89. package/web/assets/{autocomplete-Do-EC8XW.js → autocomplete-CjBHTJWh.js} +4 -4
  90. package/web/assets/{card-CeTOcGyS.js → card-CsmU2iiY.js} +1 -1
  91. package/web/assets/{checkbox-C2hHZbMO.js → checkbox-B5XZqi1U.js} +3 -3
  92. package/web/assets/{chip-CD3EONN7.js → chip-C4pSomPm.js} +1 -1
  93. package/web/assets/column-CR3Hix4J.js +763 -0
  94. package/web/assets/{core-Dq8cnTTd.js → core-CtGUajLi.js} +1 -1
  95. package/web/assets/cssMode-nuznbveH.js +4 -0
  96. package/web/assets/{dist-CQoPOD_o.js → dist--ZlcDulO.js} +11 -11
  97. package/web/assets/{dist-BmCpMB7W.js → dist-CXkMrHNP.js} +1 -1
  98. package/web/assets/{dist-US_vG96F.js → dist-DH_zJr7W.js} +1 -1
  99. package/web/assets/{dist-CiBpb7F_.js → dist-WjGUyWSO.js} +1 -1
  100. package/web/assets/{divider-C7F2jaMI.js → divider-CnJlD7qO.js} +1 -1
  101. package/web/assets/{fieldset-kJDAoVaz.js → fieldset-D9zY6R6s.js} +1 -1
  102. package/web/assets/{freemarker2-qyf3DfQl.js → freemarker2-Bw43sOne.js} +1 -1
  103. package/web/assets/handlebars-C8qgW7u6.js +1 -0
  104. package/web/assets/html-Dy9PKnkr.js +1 -0
  105. package/web/assets/htmlMode-o8EU0tqo.js +4 -0
  106. package/web/assets/index-9i25DNQ4.css +1 -0
  107. package/web/assets/index-CIcE6DNv.js +4308 -0
  108. package/web/assets/{javascript-Nwj2LVeE.js → javascript-CuRngcUR.js} +1 -1
  109. package/web/assets/{jsonMode-xQ2R28Fq.js → jsonMode-CsUIxwQq.js} +1 -1
  110. package/web/assets/{keyboard-CidI6qyD.js → keyboard-DhkINir5.js} +1 -1
  111. package/web/assets/{lib-Bpt_o5V9.js → lib-E9M4L9vE.js} +1 -1
  112. package/web/assets/{library-CcEqwrWR.js → library-BXoSag-V.js} +1 -1
  113. package/web/assets/liquid-CraMnqaK.js +1 -0
  114. package/web/assets/mdx-DPSW5q1A.js +1 -0
  115. package/web/assets/{mediaTypes-pLZCmXiT.js → mediaTypes-DDACekzr.js} +1 -1
  116. package/web/assets/{message-B6M86BRU.js → message-DKOCajc6.js} +1 -1
  117. package/web/assets/{minus-wesZvWq4.js → minus-CtlH9Syh.js} +1 -1
  118. package/web/assets/{multiselect-CHEmtEFg.js → multiselect-sshNnjnF.js} +3 -3
  119. package/web/assets/{password-ISam7EYg.js → password-Dv9g39jb.js} +4 -4
  120. package/web/assets/{plus-BBMmFC_c.js → plus-CG5Ui-l-.js} +6 -6
  121. package/web/assets/{pretty-ms-Bj2IaA2r.js → pretty-ms-D2BVTeMx.js} +1 -1
  122. package/web/assets/process.store-CutdTiYB.js +1 -0
  123. package/web/assets/project.store-CVbXSH5d.js +1 -0
  124. package/web/assets/python-BLq8fVkV.js +1 -0
  125. package/web/assets/{radiobutton-DwoOAEAF.js → radiobutton-Bz_ltBfM.js} +2 -2
  126. package/web/assets/razor-CVrBU-Fk.js +1 -0
  127. package/web/assets/router-CuxdGfLF.js +1 -0
  128. package/web/assets/{settings.store-BGScNTnu.js → settings.store-vM-8jbAF.js} +1 -1
  129. package/web/assets/store-CTKZgNiD.js +1840 -0
  130. package/web/assets/{tabpanel-DBInUEl5.js → tabpanel-DaZzgdrv.js} +3 -3
  131. package/web/assets/trpc-BIjF6nTt.js +1 -0
  132. package/web/assets/{tsMode-C7uad5vV.js → tsMode-DO5zEzzD.js} +1 -1
  133. package/web/assets/typescript-Do-K_3Jj.js +1 -0
  134. package/web/assets/{urls-BuNgEu0U.js → urls-BlVUwVFM.js} +1 -1
  135. package/web/assets/{use-tree-walker-CJHEPXgB.js → use-tree-walker-xoEX554A.js} +1 -1
  136. package/web/assets/useAiChatDrawer-BJugMTj0.js +1 -0
  137. package/web/assets/{useClipboard-DtO-z5Ls.js → useClipboard-B3J6cGK-.js} +1 -1
  138. package/web/assets/{useLayout-D-FCdkqa.js → useLayout-DHrumlpr.js} +1 -1
  139. package/web/assets/{usePluginManager-CPTBi3rk.js → usePluginManager-BEbBxZnF.js} +1 -1
  140. package/web/assets/{useSidebar-BoK12gD1.js → useSidebar-BCBwVF2x.js} +1 -1
  141. package/web/assets/{vue-router-DSROI5EH.js → vue-router-CuFIegGa.js} +1 -1
  142. package/web/assets/xml-BolCsFLY.js +1 -0
  143. package/web/assets/yaml-5iZTkCV3.js +1 -0
  144. package/web/index.html +2 -2
  145. package/agent-manager-A4E5CXFT-6PJDFN7J.js +0 -8
  146. package/chunk-DODGBBTE.js.map +0 -1
  147. package/dist-HZBWBT6S.js.map +0 -1
  148. package/web/assets/AgentScalarChatInterface.vue-Be-LbA03.js +0 -13
  149. package/web/assets/AgentTerminalPage-BZmmd1H1.js +0 -36
  150. package/web/assets/AgentTerminalPage-EX_n6P3x.css +0 -1
  151. package/web/assets/AiChatPage-kTk6yC6d.js +0 -1
  152. package/web/assets/AppLayout-CDtyYNWp.css +0 -1
  153. package/web/assets/AppLayout-jbNML_m4.js +0 -1
  154. package/web/assets/AuditPage-S4kDjrZ5.js +0 -1
  155. package/web/assets/CollectionCookies.vue-BLEJqAhX.js +0 -1
  156. package/web/assets/CollectionScripts.vue-CsY6tWxk.js +0 -1
  157. package/web/assets/CollectionSync.vue-B-uKwdTU.js +0 -1
  158. package/web/assets/DashboardPage-BwZPpURp.js +0 -1
  159. package/web/assets/EnvironmentFormPage-ByVELlly.js +0 -1
  160. package/web/assets/EnvironmentListPage-DvaTK4NF.js +0 -1
  161. package/web/assets/EnvironmentSelector-4kpQpg5e.js +0 -1
  162. package/web/assets/KanbanBoardPage-7rLK-ucb.js +0 -141
  163. package/web/assets/KanbanListPage-Dn35VrDc.js +0 -1
  164. package/web/assets/LaunchProjectPage-Byemtd9C.js +0 -1
  165. package/web/assets/LaunchServicesPage-CHiV6T5Y.js +0 -1
  166. package/web/assets/LogViewer-KtrKiUZw.js +0 -3
  167. package/web/assets/ProcessDetailPage-BOI85Xo7.js +0 -1
  168. package/web/assets/ProjectDetailPage-CTEt0DSq.js +0 -1
  169. package/web/assets/ProjectListPage-BrTIxp9K.js +0 -1
  170. package/web/assets/RequestRoot.vue-BEtC7IEP.js +0 -19
  171. package/web/assets/ScenarioEditorPage-xCx9aW1X.js +0 -14
  172. package/web/assets/ServiceControlsBar-DKYIgScL.js +0 -1
  173. package/web/assets/ServiceDetailPage-B5qBCc9A.js +0 -2
  174. package/web/assets/ServiceFormPage-BNpWmyj_.js +0 -1
  175. package/web/assets/SettingsPage-C5lWJT0Z.js +0 -225
  176. package/web/assets/SetupKeyPage-BiqO4PQr.js +0 -1
  177. package/web/assets/SetupPage-FEUuQSQ2.js +0 -1
  178. package/web/assets/UnlockPage-Dhg0hoP-.js +0 -1
  179. package/web/assets/VariablesTable-DFJHJ9DF.js +0 -1
  180. package/web/assets/api-reference-C9jD1TQ9.js +0 -1
  181. package/web/assets/column-xfOef4Rh.js +0 -763
  182. package/web/assets/cssMode-BzsFOrHs.js +0 -4
  183. package/web/assets/editor.store-Dzhoqquh.js +0 -1
  184. package/web/assets/handlebars-Dt_j38Ok.js +0 -1
  185. package/web/assets/html-BM6JBSDe.js +0 -1
  186. package/web/assets/htmlMode-BWrj-eTf.js +0 -4
  187. package/web/assets/index-DXutsd9M.css +0 -1
  188. package/web/assets/index-Dviyssba.js +0 -4220
  189. package/web/assets/liquid-CNc1r-Ln.js +0 -1
  190. package/web/assets/mdx-D5yTipSh.js +0 -1
  191. package/web/assets/process.store-cvjJzD3H.js +0 -1
  192. package/web/assets/project.store-C94-7i6c.js +0 -1
  193. package/web/assets/python-BOLsEvIQ.js +0 -1
  194. package/web/assets/razor-JsBDv4HY.js +0 -1
  195. package/web/assets/router-BgHCboHx.js +0 -1
  196. package/web/assets/store-a88gBDkh.js +0 -1840
  197. package/web/assets/tag-CQlI4fJK.js +0 -54
  198. package/web/assets/trpc-CgAubgRH.js +0 -1
  199. package/web/assets/typescript-C2IozRX9.js +0 -1
  200. package/web/assets/useAiChatDrawer-D6TRHaNu.js +0 -1
  201. package/web/assets/xml-DtJwg-5S.js +0 -1
  202. package/web/assets/yaml-sPGHMYDm.js +0 -1
  203. /package/{agent-manager-A4E5CXFT-6PJDFN7J.js.map → agent-manager-3DHUF2DC-KN6O7WSA.js.map} +0 -0
  204. /package/web/assets/{_plugin-vue_export-helper-BVSTY9ge.js → _plugin-vue_export-helper-C08BWUE4.js} +0 -0
  205. /package/web/assets/{abap-BF8nUS5p.js → abap-BMPR0rjj.js} +0 -0
  206. /package/web/assets/{apex-Bbj_Fak2.js → apex-DYC4uYYf.js} +0 -0
  207. /package/web/assets/{azcli-B8w3JS7J.js → azcli-GZlxEjpa.js} +0 -0
  208. /package/web/assets/{base64-CG47Jc3Y.js → base64-VLYHbiMn.js} +0 -0
  209. /package/web/assets/{bat-CaNuqacB.js → bat-Cv8OxItG.js} +0 -0
  210. /package/web/assets/{bicep-DAvnLugW.js → bicep-CuCbE6Ue.js} +0 -0
  211. /package/web/assets/{cameligo-BlI8LCmP.js → cameligo-BEPX3585.js} +0 -0
  212. /package/web/assets/{clojure-CxxhrBQl.js → clojure--6rbXIPY.js} +0 -0
  213. /package/web/assets/{coffee-SkbnTBhO.js → coffee-ByvVcDGX.js} +0 -0
  214. /package/web/assets/{cpp-Dm2HJxj0.js → cpp-CECtPnZp.js} +0 -0
  215. /package/web/assets/{csharp-BOGHPGcq.js → csharp-DyYyZ8UX.js} +0 -0
  216. /package/web/assets/{csp-CMA_7tOs.js → csp-B9apvcI9.js} +0 -0
  217. /package/web/assets/{css-DU8LSGcn.js → css-CfdBNbB0.js} +0 -0
  218. /package/web/assets/{cypher-BZPsEDmT.js → cypher-BPqQq4XX.js} +0 -0
  219. /package/web/assets/{dart-C1tIJhFk.js → dart-BLToxiYU.js} +0 -0
  220. /package/web/assets/{dist-BPIMeATj.js → dist-COg0nRYK.js} +0 -0
  221. /package/web/assets/{dockerfile-z4dmGi5q.js → dockerfile-CKo5jN7V.js} +0 -0
  222. /package/web/assets/{ecl-Rv9JwhOI.js → ecl-qo8pi1Ty.js} +0 -0
  223. /package/web/assets/{elixir-DL6xFSed.js → elixir-Dv1YAMPJ.js} +0 -0
  224. /package/web/assets/{flow9-DFZ52-2d.js → flow9-BKTKs1nN.js} +0 -0
  225. /package/web/assets/{fsharp-RvVOB9Dm.js → fsharp-BPoxgmtD.js} +0 -0
  226. /package/web/assets/{go-B2USMDMv.js → go-DgJ-2ryf.js} +0 -0
  227. /package/web/assets/{graphql-CsHpCYeJ.js → graphql-YTK8zpFe.js} +0 -0
  228. /package/web/assets/{hcl-CK021J-J.js → hcl-tYOvwaJ3.js} +0 -0
  229. /package/web/assets/{ini-v1EoSUxR.js → ini-BTZ3lNbM.js} +0 -0
  230. /package/web/assets/{java-CUXpSPxY.js → java-boRe0CIQ.js} +0 -0
  231. /package/web/assets/{julia-DwKveiZg.js → julia-K6BtoWYX.js} +0 -0
  232. /package/web/assets/{kotlin-SsG_4ZBt.js → kotlin-CglG00z4.js} +0 -0
  233. /package/web/assets/{less-BviquM8A.js → less-DlnIYk4g.js} +0 -0
  234. /package/web/assets/{lexon-C8G_qWst.js → lexon-D4aKXJ0J.js} +0 -0
  235. /package/web/assets/{lua-xqYje_o5.js → lua-DeQCQ0IN.js} +0 -0
  236. /package/web/assets/{m3-B5PetinG.js → m3-yCjRJP_i.js} +0 -0
  237. /package/web/assets/{markdown-BksexNux.js → markdown-C2IT79AR.js} +0 -0
  238. /package/web/assets/{mips-DKSTezUQ.js → mips-BAnhwv_D.js} +0 -0
  239. /package/web/assets/{msdax-SeFNE86e.js → msdax-D1jHDTih.js} +0 -0
  240. /package/web/assets/{mysql-5TQWJDdB.js → mysql-CTJU-jL7.js} +0 -0
  241. /package/web/assets/{objective-c-DeaYl08g.js → objective-c-CwxzUA28.js} +0 -0
  242. /package/web/assets/{pascal-CGBtIfZg.js → pascal-DpSQp86i.js} +0 -0
  243. /package/web/assets/{pascaligo-B85wuk0w.js → pascaligo-C4F0Gc12.js} +0 -0
  244. /package/web/assets/{perl-Bl61zsEQ.js → perl-87HvGh3u.js} +0 -0
  245. /package/web/assets/{pgsql-KgXv5Cq7.js → pgsql-DUB3YVHd.js} +0 -0
  246. /package/web/assets/{php-KRRf0kxu.js → php-gpgbnwXh.js} +0 -0
  247. /package/web/assets/{pla-CFpO6lSz.js → pla-9VH1h0qA.js} +0 -0
  248. /package/web/assets/{postiats-B0jQ-DXO.js → postiats-DYNil0RB.js} +0 -0
  249. /package/web/assets/{powerquery-T9Ge5xUx.js → powerquery-Cyj_QMWN.js} +0 -0
  250. /package/web/assets/{powershell-rmYjKoE4.js → powershell-BxsWzjwy.js} +0 -0
  251. /package/web/assets/{protobuf-B--9ME3E.js → protobuf-CpB35fc2.js} +0 -0
  252. /package/web/assets/{pug-y0-ppnAV.js → pug-CQ3BHXrf.js} +0 -0
  253. /package/web/assets/{qsharp-DobgOAC8.js → qsharp-hvdiA16n.js} +0 -0
  254. /package/web/assets/{r-BHQZIThn.js → r-BcpSD-1e.js} +0 -0
  255. /package/web/assets/{redis-DMHMVIni.js → redis-B2JGUU7L.js} +0 -0
  256. /package/web/assets/{redshift-ilRjY5HF.js → redshift-DtV7K8A1.js} +0 -0
  257. /package/web/assets/{restructuredtext-jr4YYJbv.js → restructuredtext-avYcqBCA.js} +0 -0
  258. /package/web/assets/{ruby-D-h_0NIs.js → ruby-B75_Tp8C.js} +0 -0
  259. /package/web/assets/{rust-VxeTSQU2.js → rust-CHon-ZXH.js} +0 -0
  260. /package/web/assets/{sb-BlhKufrP.js → sb-iehvyQKY.js} +0 -0
  261. /package/web/assets/{scala-IAk6nSCU.js → scala-LBw1ennX.js} +0 -0
  262. /package/web/assets/{scheme-Dggdm5LS.js → scheme-BRK_sKgt.js} +0 -0
  263. /package/web/assets/{scss-Bzj9Cp1q.js → scss-CzbUsG5V.js} +0 -0
  264. /package/web/assets/{shell-CSwYJdGJ.js → shell-DdtWj1FH.js} +0 -0
  265. /package/web/assets/{solidity-DnWqIXnk.js → solidity-CBYPsj6K.js} +0 -0
  266. /package/web/assets/{sophia-ClaYC_5N.js → sophia-BAmZLhFu.js} +0 -0
  267. /package/web/assets/{sparql-CfjIZAcz.js → sparql-DzxQz_25.js} +0 -0
  268. /package/web/assets/{sql-DxdJ7snd.js → sql-CtZa8xg5.js} +0 -0
  269. /package/web/assets/{st-DJgQE1uj.js → st-3yAXYt2f.js} +0 -0
  270. /package/web/assets/{swift-5cz3wIvv.js → swift-nwo4k0Qx.js} +0 -0
  271. /package/web/assets/{systemverilog-C8-7QuIt.js → systemverilog-38mUSYaO.js} +0 -0
  272. /package/web/assets/{tcl-C99u42o5.js → tcl-DfkL3MZ2.js} +0 -0
  273. /package/web/assets/{twig-Cv9kOcBK.js → twig-C2_DTvv-.js} +0 -0
  274. /package/web/assets/{typespec-DF1s-ehc.js → typespec-Dn7A_7aO.js} +0 -0
  275. /package/web/assets/{uuid-6HQMUkhT.js → uuid-H0yY5A03.js} +0 -0
  276. /package/web/assets/{vb-4mpz9ITX.js → vb-ByZEqbuQ.js} +0 -0
  277. /package/web/assets/{w3c-keyname-emj7Fjc1.js → w3c-keyname-CXRaQfIQ.js} +0 -0
  278. /package/web/assets/{wgsl-xExw6Z08.js → wgsl-BqCLRRs9.js} +0 -0
@@ -34,7 +34,7 @@ import {
34
34
  nativeParsers,
35
35
  serviceStore,
36
36
  traefikManager
37
- } from "./chunk-DODGBBTE.js";
37
+ } from "./chunk-AJW7LDO5.js";
38
38
  import {
39
39
  generateKanbanPopulatePrompt,
40
40
  generateKanbanSystemPrompt,
@@ -121,27 +121,34 @@ import OpenAI2 from "openai";
121
121
  import { execFile as execFile2 } from "child_process";
122
122
  import { promisify } from "util";
123
123
  import { fileURLToPath } from "url";
124
- import { TRPCError as TRPCError10 } from "@trpc/server";
125
- import { z as z13 } from "zod";
124
+ import { readdir as readdir2, access, readFile as readFile8 } from "fs/promises";
125
+ import { watch } from "fs";
126
+ import { homedir as homedir3 } from "os";
127
+ import { join as join10 } from "path";
126
128
  import { randomUUID as randomUUID3 } from "crypto";
127
129
  import { readFile as readFile7, writeFile as writeFile7, rename as rename7, mkdir as mkdir8, chmod as chmod6 } from "fs/promises";
128
130
  import { join as join9 } from "path";
129
131
  import { EventEmitter } from "events";
132
+ import { TRPCError as TRPCError10 } from "@trpc/server";
133
+ import { z as z13 } from "zod";
130
134
  import pty from "node-pty";
131
135
  import { execSync } from "child_process";
132
136
  import pty2 from "node-pty";
133
- import { mkdir as mkdir10, rm, readFile as readFile9 } from "fs/promises";
134
- import { join as join10 } from "path";
137
+ import { mkdir as mkdir10, rm, readFile as readFile10 } from "fs/promises";
138
+ import { join as join11 } from "path";
135
139
  import { chmod as chmod7, mkdir as mkdir9, rename as rename8, writeFile as writeFile8 } from "fs/promises";
136
140
  import { dirname as dirname2, resolve as resolve2, sep } from "path";
137
- import { readFile as readFile8 } from "fs/promises";
141
+ import { readFile as readFile9 } from "fs/promises";
138
142
  import Anthropic from "@anthropic-ai/sdk";
139
143
  import OpenAI from "openai";
140
144
  import { randomUUID as randomUUID4 } from "crypto";
141
- import { mkdir as mkdir11, writeFile as writeFile9, readFile as readFile10, unlink as unlink2, readdir as readdir2 } from "fs/promises";
145
+ import { mkdir as mkdir11, writeFile as writeFile9, readFile as readFile11, unlink as unlink2, readdir as readdir3 } from "fs/promises";
142
146
  import { existsSync } from "fs";
143
- import { join as join11, extname, resolve as resolve3, sep as sep2 } from "path";
147
+ import { join as join12, extname, resolve as resolve3, sep as sep2 } from "path";
144
148
  import { spawn as spawn2 } from "child_process";
149
+ import { randomUUID as randomUUID5 } from "crypto";
150
+ import { open } from "fs/promises";
151
+ import { watch as watch2 } from "fs";
145
152
  import { spawn as spawn3 } from "child_process";
146
153
  import { createInterface } from "readline";
147
154
  import { EventEmitter as EventEmitter2 } from "events";
@@ -149,7 +156,7 @@ import { z as z15 } from "zod";
149
156
  import { TRPCError as TRPCError11 } from "@trpc/server";
150
157
  import Anthropic3 from "@anthropic-ai/sdk";
151
158
  import OpenAI3 from "openai";
152
- import { randomUUID as randomUUID6 } from "crypto";
159
+ import { randomUUID as randomUUID7 } from "crypto";
153
160
  import { TRPCError as TRPCError14 } from "@trpc/server";
154
161
  import { z as z16 } from "zod";
155
162
  import { TRPCError as TRPCError12 } from "@trpc/server";
@@ -157,46 +164,47 @@ import { TRPCError as TRPCError13 } from "@trpc/server";
157
164
  import Anthropic4 from "@anthropic-ai/sdk";
158
165
  import OpenAI4 from "openai";
159
166
  import { mkdir as mkdir12 } from "fs/promises";
160
- import { randomUUID as randomUUID5 } from "crypto";
161
- import { join as join12 } from "path";
162
- import { randomUUID as randomUUID7 } from "crypto";
167
+ import { randomUUID as randomUUID6 } from "crypto";
168
+ import { join as join13 } from "path";
169
+ import { randomUUID as randomUUID8 } from "crypto";
163
170
  import { TRPCError as TRPCError15 } from "@trpc/server";
164
171
  import { z as z17 } from "zod";
165
172
  import { z as z18 } from "zod";
166
173
  import { TRPCError as TRPCError16 } from "@trpc/server";
167
174
  import { z as z19 } from "zod";
168
- import { randomUUID as randomUUID8 } from "crypto";
175
+ import { randomUUID as randomUUID9 } from "crypto";
169
176
  import { EventEmitter as EventEmitter3 } from "events";
170
177
  import { z as z20 } from "zod";
171
178
  import { TRPCError as TRPCError17 } from "@trpc/server";
172
179
  import { z as z21 } from "zod";
173
180
  import { chmod as chmod8, stat as stat3 } from "fs/promises";
174
- import { join as join13 } from "path";
175
- import { writeFile as writeFile10 } from "fs/promises";
176
181
  import { join as join14 } from "path";
182
+ import { writeFile as writeFile10 } from "fs/promises";
183
+ import { join as join15 } from "path";
177
184
  import { createRequire } from "module";
178
185
  import { randomBytes as randomBytes3, createCipheriv, createDecipheriv } from "crypto";
179
- import { randomUUID as randomUUID9 } from "crypto";
186
+ import { randomUUID as randomUUID10 } from "crypto";
180
187
  import {
181
- readFile as readFile11,
188
+ readFile as readFile12,
182
189
  writeFile as writeFile11,
183
190
  rename as rename9,
184
191
  mkdir as mkdir13,
185
- access,
186
- readdir as readdir3
192
+ access as access2,
193
+ readdir as readdir4
187
194
  } from "fs/promises";
188
- import { join as join15 } from "path";
189
- import { randomBytes as randomBytes4, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
190
- import { readFile as readFile12, writeFile as writeFile12, chmod as chmod9, stat as stat4, rename as rename10 } from "fs/promises";
191
195
  import { join as join16 } from "path";
196
+ import { randomBytes as randomBytes4, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
197
+ import { readFile as readFile13, writeFile as writeFile12, chmod as chmod9, stat as stat4, rename as rename10 } from "fs/promises";
198
+ import { join as join17 } from "path";
192
199
  import { Entry } from "@napi-rs/keyring";
193
200
  import { hashRaw as argon2HashRaw } from "@node-rs/argon2";
194
- import { join as join17 } from "path";
201
+ import { join as join18 } from "path";
195
202
  import { stat as stat5 } from "fs/promises";
196
203
  import rateLimit from "express-rate-limit";
197
204
  import { existsSync as existsSync2, createReadStream } from "fs";
198
205
  import { mkdir as mkdir14 } from "fs/promises";
199
206
  import multer from "multer";
207
+ import { resolve as resolvePath } from "path";
200
208
  import dotenv from "dotenv";
201
209
  var jwtSecret = null;
202
210
  var jwtSecretGeneratedAt = 0;
@@ -400,7 +408,7 @@ function requireEnvAction(ctx, environmentId, action, serviceId) {
400
408
  });
401
409
  }
402
410
  var startedAt = Date.now();
403
- var appVersion = process.env["RUNEYA_VERSION"] ?? (true ? "2.0.10" : "dev");
411
+ var appVersion = process.env["RUNEYA_VERSION"] ?? (true ? "2.0.11" : "dev");
404
412
  var healthRouter = router({
405
413
  check: publicProcedure.meta({ openapi: { method: "GET", path: "/health", tags: ["health"], summary: "Health check" } }).output(z.object({ status: z.literal("ok"), uptime: z.number(), version: z.string() })).query(() => {
406
414
  return {
@@ -431,6 +439,7 @@ var ProjectStore = class {
431
439
  this.projects.set(p.id, {
432
440
  ...p,
433
441
  environmentIds: p.environmentIds ?? [],
442
+ groupOrder: p.groupOrder ?? [],
434
443
  // Prefer the env marked default, fall back to first env
435
444
  activeEnvironmentId: defaultEnv?.id ?? p.environmentIds?.[0]
436
445
  });
@@ -466,7 +475,8 @@ var ProjectStore = class {
466
475
  description: input.description ?? "",
467
476
  serviceIds: input.serviceIds ?? [],
468
477
  environmentIds: input.environmentIds ?? [],
469
- activeEnvironmentId: input.activeEnvironmentId
478
+ activeEnvironmentId: input.activeEnvironmentId,
479
+ groupOrder: input.groupOrder ?? []
470
480
  };
471
481
  this.projects.set(project.id, project);
472
482
  await this.save();
@@ -483,7 +493,8 @@ var ProjectStore = class {
483
493
  ...input.serviceIds !== void 0 && { serviceIds: input.serviceIds },
484
494
  ...input.environmentIds !== void 0 && { environmentIds: input.environmentIds },
485
495
  ...input.activeEnvironmentId !== void 0 && { activeEnvironmentId: input.activeEnvironmentId },
486
- ...input.orgId !== void 0 && { orgId: input.orgId }
496
+ ...input.orgId !== void 0 && { orgId: input.orgId },
497
+ ...input.groupOrder !== void 0 && { groupOrder: input.groupOrder }
487
498
  };
488
499
  this.projects.set(input.id, updated);
489
500
  await this.save();
@@ -573,7 +584,6 @@ var EnvironmentOverridesStore = class {
573
584
  const raw = await readFile3(this.getFilePath(), "utf-8");
574
585
  const parsed = JSON.parse(raw);
575
586
  if (!getEncryptionKey()) {
576
- this.loaded = true;
577
587
  return;
578
588
  }
579
589
  this.diskEncryptedSlots = extractEncryptedVariableSlots(
@@ -1030,7 +1040,8 @@ var projectRouter = router({
1030
1040
  name: z2.string().min(1).max(200).optional(),
1031
1041
  description: z2.string().max(1e4).optional(),
1032
1042
  orgId: z2.string().max(128).nullable().optional(),
1033
- serviceIds: z2.array(z2.string().max(128)).max(1e4).optional()
1043
+ serviceIds: z2.array(z2.string().max(128)).max(1e4).optional(),
1044
+ groupOrder: z2.array(z2.string().max(200)).max(100).optional()
1034
1045
  })
1035
1046
  ).mutation(async ({ input }) => {
1036
1047
  const project = await projectStore.update(input);
@@ -2982,200 +2993,6 @@ var apiKeysRouter = router({
2982
2993
  return { path: input.relativePath };
2983
2994
  })
2984
2995
  });
2985
- var Id128Schema = z13.string().max(128);
2986
- var ConversationIdSchema = z13.string().max(128);
2987
- var NameSchema = z13.string().min(1).max(200);
2988
- var ShortString200Schema = z13.string().max(200);
2989
- var DescriptionSchema = z13.string().max(2e3);
2990
- var OptionalProviderIdSchema = Id128Schema.optional();
2991
- function generateAdminLlmsTxt(services, jwt) {
2992
- const baseUrl = `http://localhost:${env.PORT}`;
2993
- const trpcBase = `${baseUrl}/api/trpc`;
2994
- const lines = [];
2995
- lines.push("# Runeya \u2014 Service Management API");
2996
- lines.push("");
2997
- lines.push("> You are operating as an authenticated admin with full access to all services.");
2998
- lines.push(`> Base URL: ${trpcBase}`);
2999
- if (jwt) {
3000
- lines.push("> Authentication: pass `Authorization: Bearer $RUNEYA_API_TOKEN` on all tRPC requests.");
3001
- }
3002
- lines.push("");
3003
- if (services.length === 0) {
3004
- lines.push("No services configured.");
3005
- return lines.join("\n");
3006
- }
3007
- lines.push("## Services");
3008
- lines.push("");
3009
- for (const svc of services) {
3010
- lines.push(`### ${svc.name}`);
3011
- lines.push(`- **ID**: \`${svc.id}\``);
3012
- if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3013
- if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3014
- if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3015
- lines.push("- **Permitted actions**: all");
3016
- lines.push("");
3017
- }
3018
- lines.push("## MCP Tools");
3019
- lines.push("");
3020
- lines.push("You have access to Runeya MCP tools to manage services. **Always prefer MCP tools** over raw HTTP calls.");
3021
- lines.push("Available tools: `list_services`, `get_service_status`, `start_service`, `stop_service`, `restart_service`, `get_logs`.");
3022
- lines.push("");
3023
- if (jwt) {
3024
- lines.push("**Important**: Pass the `authToken` parameter with value `$RUNEYA_API_TOKEN` (from environment) on every MCP tool call for authentication.");
3025
- lines.push("");
3026
- }
3027
- lines.push("Use the service IDs and agent IDs listed above when calling these tools.");
3028
- lines.push("");
3029
- lines.push("## API Reference (fallback)");
3030
- lines.push("");
3031
- lines.push("Only use raw HTTP calls if MCP tools are unavailable.");
3032
- lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3033
- lines.push("");
3034
- lines.push("**tRPC v11 request format:**");
3035
- lines.push('- **GET queries**: `?input={"id":"abc"}`');
3036
- lines.push('- **POST mutations**: JSON body `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3037
- lines.push("");
3038
- lines.push(`### List services
3039
- \`GET ${trpcBase}/service.list\``);
3040
- lines.push("");
3041
- lines.push(`### Get service
3042
- \`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3043
- lines.push("");
3044
- lines.push(`### Read logs
3045
- \`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3046
- lines.push("");
3047
- lines.push(`### Start service
3048
- \`POST ${trpcBase}/process.start\`
3049
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3050
- lines.push("");
3051
- lines.push(`### Stop service
3052
- \`POST ${trpcBase}/process.stop\`
3053
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3054
- lines.push("");
3055
- lines.push(`### Restart service
3056
- \`POST ${trpcBase}/process.restart\`
3057
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3058
- lines.push("");
3059
- lines.push(`### Get service status
3060
- \`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3061
- lines.push("");
3062
- return lines.join("\n");
3063
- }
3064
- function generateLlmsTxt(apiKey, services) {
3065
- const { scopes } = apiKey;
3066
- const baseUrl = `http://localhost:${env.PORT}`;
3067
- const trpcBase = `${baseUrl}/api/trpc`;
3068
- const lines = [];
3069
- lines.push("# Runeya \u2014 Service Management API");
3070
- lines.push("");
3071
- lines.push("> This file describes the API surface available for this key.");
3072
- lines.push(`> Base URL: ${trpcBase}`);
3073
- lines.push(`> Authentication: pass this key as \`Authorization: Bearer ${apiKey.key}\` on all tRPC requests.`);
3074
- lines.push("");
3075
- if (services.length === 0) {
3076
- lines.push("No services accessible with this key.");
3077
- return lines.join("\n");
3078
- }
3079
- const permByServiceId = new Map(
3080
- scopes.services.map((p) => [p.serviceId, p])
3081
- );
3082
- lines.push("## Services");
3083
- lines.push("");
3084
- for (const svc of services) {
3085
- const perm = permByServiceId.get(svc.id);
3086
- const actions = perm?.actions ?? [];
3087
- lines.push(`### ${svc.name}`);
3088
- lines.push(`- **ID**: \`${svc.id}\``);
3089
- if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3090
- if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3091
- if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3092
- lines.push("- **Permitted actions**:");
3093
- if (actions.length === 0) {
3094
- lines.push(" - _(none)_");
3095
- } else {
3096
- for (const a of actions) lines.push(` - \`${a}\``);
3097
- }
3098
- const envPerms = perm?.environments ?? [];
3099
- if (envPerms.length > 0) {
3100
- lines.push("- **Environment access**:");
3101
- for (const ep of envPerms) {
3102
- const envActions = ep.actions.join(", ");
3103
- const secrets = ep.includeSecrets ? " _(includes secrets)_" : "";
3104
- lines.push(` - \`${ep.environmentId}\`: ${envActions}${secrets}`);
3105
- }
3106
- }
3107
- lines.push("");
3108
- }
3109
- lines.push("## API Reference");
3110
- lines.push("");
3111
- lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3112
- lines.push("");
3113
- lines.push("**Important \u2014 tRPC v11 request format:**");
3114
- lines.push('- **GET queries**: pass input as a plain JSON-encoded string in the `input` query param (no `{"json":...}` wrapper).');
3115
- lines.push(' Example: `?input={"id":"abc"}`');
3116
- lines.push('- **POST mutations**: send the input object as a plain JSON body (no `{"json":...}` wrapper).');
3117
- lines.push(' Example body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3118
- lines.push("- The `agentId` is listed per service above.");
3119
- lines.push("- The `processId` is the same as the service `ID` listed above.");
3120
- lines.push("");
3121
- const allActions = new Set(scopes.services.flatMap((p) => p.actions));
3122
- const hasEnvRead = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:read")));
3123
- const hasEnvWrite = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:write")));
3124
- if (allActions.has("service:read")) {
3125
- lines.push("### List services");
3126
- lines.push(`\`GET ${trpcBase}/service.list\``);
3127
- lines.push("");
3128
- lines.push("### Get service");
3129
- lines.push(`\`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3130
- lines.push("");
3131
- }
3132
- if (allActions.has("logs:read")) {
3133
- lines.push("### Read logs");
3134
- lines.push(`\`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3135
- lines.push("");
3136
- }
3137
- if (allActions.has("service:start")) {
3138
- lines.push("### Start service");
3139
- lines.push(`\`POST ${trpcBase}/process.start\``);
3140
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3141
- lines.push("");
3142
- }
3143
- if (allActions.has("service:stop")) {
3144
- lines.push("### Stop service");
3145
- lines.push(`\`POST ${trpcBase}/process.stop\``);
3146
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3147
- lines.push("");
3148
- }
3149
- if (allActions.has("service:restart")) {
3150
- lines.push("### Restart service");
3151
- lines.push(`\`POST ${trpcBase}/process.restart\``);
3152
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3153
- lines.push("");
3154
- }
3155
- if (allActions.has("service:status")) {
3156
- lines.push("### Get service status");
3157
- lines.push(`\`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3158
- lines.push("");
3159
- }
3160
- if (allActions.has("service:healthcheck")) {
3161
- lines.push("### Get service health");
3162
- lines.push(`\`GET ${trpcBase}/process.health?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3163
- lines.push("");
3164
- }
3165
- if (hasEnvRead) {
3166
- lines.push("### Read environment variables (flat KEY=VALUE \u2014 secrets shown as *****)");
3167
- lines.push(`\`GET ${trpcBase}/environment.resolveFlat?input={"environmentId":"<environmentId>"}\``);
3168
- lines.push("Returns a flat `Record<string, string>`. Secret variables you are not authorized to see are shown as `*****`.");
3169
- lines.push("");
3170
- }
3171
- if (hasEnvWrite) {
3172
- lines.push("### Write environment variable");
3173
- lines.push(`\`POST ${trpcBase}/environment.upsertVariable\``);
3174
- lines.push('Body: `{"environmentId":"<environmentId>","key":"<key>","value":"<value>"}`');
3175
- lines.push("");
3176
- }
3177
- return lines.join("\n");
3178
- }
3179
2996
  var RUNEYA_CAPABILITIES = [
3180
2997
  {
3181
2998
  name: "list_services",
@@ -3410,43 +3227,8 @@ async function callTrpc(baseUrl, route, method, input, authToken) {
3410
3227
  const result = json["result"];
3411
3228
  return result?.["data"] ?? result ?? json;
3412
3229
  }
3413
- var CHAT_TOOLS = buildAnthropicTools(RUNEYA_CAPABILITIES);
3414
- var CHAT_TOOLS_OPENAI = buildOpenAITools(RUNEYA_CAPABILITIES);
3415
- var TOOL_SAFETY_INSTRUCTION = buildToolSafetyInstruction(RUNEYA_CAPABILITIES);
3416
- function hasServiceAction(apiKey, serviceId, action) {
3417
- if (!apiKey) return true;
3418
- const perm = apiKey.scopes.services.find((p) => p.serviceId === serviceId);
3419
- return perm?.actions.includes(action) ?? false;
3420
- }
3421
- var KNOWN_TOOL_NAMES = RUNEYA_CAPABILITIES.map((c) => c.name);
3422
- function normalizeToolName(raw) {
3423
- if (KNOWN_TOOL_NAMES.includes(raw)) return raw;
3424
- const match = KNOWN_TOOL_NAMES.find((t2) => raw.startsWith(t2));
3425
- return match ?? raw;
3426
- }
3427
- async function executeTool(rawName, input, apiKey, authToken) {
3428
- const name = normalizeToolName(rawName);
3429
- const cap = RUNEYA_CAPABILITIES.find((c) => c.name === name);
3430
- if (!cap) throw new Error(`Unknown tool: ${name}`);
3431
- const serviceId = input["serviceId"] ?? input["id"] ?? input["processId"];
3432
- if (serviceId) {
3433
- const actionMap = {
3434
- get_service_status: "service:status",
3435
- get_logs: "logs:read",
3436
- start_service: "service:start",
3437
- stop_service: "service:stop",
3438
- restart_service: "service:restart"
3439
- };
3440
- const action = actionMap[name];
3441
- if (action && !hasServiceAction(apiKey, serviceId, action)) {
3442
- throw new Error(`No '${action}' permission for service ${serviceId}`);
3443
- }
3444
- }
3445
- const params = Object.fromEntries(
3446
- Object.entries(input).map(([k, v]) => [k, v != null ? String(v) : void 0])
3447
- );
3448
- const baseUrl = `http://localhost:${env.PORT}`;
3449
- return callTrpc(baseUrl, cap.route, cap.method, cap.buildInput(params), authToken);
3230
+ function buildApiDocs(apiBaseUrl, apiToken) {
3231
+ return buildPromptDocs(apiBaseUrl, apiToken, RUNEYA_CAPABILITIES);
3450
3232
  }
3451
3233
  var FILENAME3 = "conversations.private.json";
3452
3234
  var ConversationStore = class {
@@ -3512,7 +3294,6 @@ var ConversationStore = class {
3512
3294
  async load() {
3513
3295
  if (this.loaded) return;
3514
3296
  if (!getEncryptionKey()) {
3515
- this.loaded = true;
3516
3297
  return;
3517
3298
  }
3518
3299
  try {
@@ -3584,7 +3365,7 @@ var ConversationStore = class {
3584
3365
  if (patch.workflowId !== void 0) conv.workflowId = patch.workflowId;
3585
3366
  if (patch.archived !== void 0) conv.archived = patch.archived;
3586
3367
  if (patch.codexThreadId !== void 0) conv.codexThreadId = patch.codexThreadId;
3587
- conv.updatedAt = Date.now();
3368
+ if (options?.touch !== false) conv.updatedAt = Date.now();
3588
3369
  await this.save();
3589
3370
  if (!options?.silent) {
3590
3371
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv) });
@@ -3622,14 +3403,430 @@ var ConversationStore = class {
3622
3403
  this.events.emit("changed", { type: "deleted", id });
3623
3404
  }
3624
3405
  }
3625
- onChanged(handler) {
3626
- this.events.on("changed", handler);
3406
+ onChanged(handler) {
3407
+ this.events.on("changed", handler);
3408
+ }
3409
+ offChanged(handler) {
3410
+ this.events.off("changed", handler);
3411
+ }
3412
+ };
3413
+ var conversationStore = new ConversationStore();
3414
+ var PROJECTS_DIR = join10(homedir3(), ".claude", "projects");
3415
+ var DEFAULT_TITLES = /* @__PURE__ */ new Set(["", "New conversation", "Nouvelle conversation"]);
3416
+ async function findClaudeSessionFile(sessionId) {
3417
+ let entries;
3418
+ try {
3419
+ entries = await readdir2(PROJECTS_DIR);
3420
+ } catch {
3421
+ return null;
3422
+ }
3423
+ const filename = `${sessionId}.jsonl`;
3424
+ for (const dir of entries) {
3425
+ const candidate = join10(PROJECTS_DIR, dir, filename);
3426
+ try {
3427
+ await access(candidate);
3428
+ return candidate;
3429
+ } catch {
3430
+ }
3431
+ }
3432
+ return null;
3433
+ }
3434
+ async function claudeSessionExists(sessionId) {
3435
+ return await findClaudeSessionFile(sessionId) !== null;
3436
+ }
3437
+ async function readClaudeTranscript(sessionId) {
3438
+ const path = await findClaudeSessionFile(sessionId);
3439
+ if (!path) return { found: false, path: null, entries: [] };
3440
+ const raw = await readFile8(path, "utf-8");
3441
+ const entries = [];
3442
+ for (const line of raw.split("\n")) {
3443
+ if (!line.trim()) continue;
3444
+ try {
3445
+ entries.push(JSON.parse(line));
3446
+ } catch {
3447
+ }
3448
+ }
3449
+ return { found: true, path, entries };
3450
+ }
3451
+ function claudeEntriesToMessages(entries) {
3452
+ const messages = [];
3453
+ const toolPartById = /* @__PURE__ */ new Map();
3454
+ for (const entry of entries) {
3455
+ if (entry.type !== "user" && entry.type !== "assistant") continue;
3456
+ const msg = entry.message;
3457
+ if (!msg) continue;
3458
+ const role = msg.role === "user" || msg.role === "assistant" ? msg.role : entry.type;
3459
+ const content = msg.content;
3460
+ const texts = [];
3461
+ const parts = [];
3462
+ let consumedByToolResult = false;
3463
+ if (typeof content === "string") {
3464
+ texts.push(content);
3465
+ } else if (Array.isArray(content)) {
3466
+ for (const block of content) {
3467
+ if (typeof block === "string") {
3468
+ texts.push(block);
3469
+ continue;
3470
+ }
3471
+ if (!block || typeof block !== "object") continue;
3472
+ const b = block;
3473
+ if (b.type === "text" && typeof b.text === "string") {
3474
+ texts.push(b.text);
3475
+ parts.push({ type: "text", text: b.text });
3476
+ } else if (b.type === "tool_use" && b.id) {
3477
+ const part = {
3478
+ type: "tool",
3479
+ id: b.id,
3480
+ name: b.name ?? "tool",
3481
+ input: b.input,
3482
+ pending: true
3483
+ };
3484
+ parts.push(part);
3485
+ toolPartById.set(b.id, part);
3486
+ } else if (b.type === "tool_result" && b.tool_use_id) {
3487
+ const target = toolPartById.get(b.tool_use_id);
3488
+ if (target) {
3489
+ target.result = b.content;
3490
+ target.isError = b.is_error;
3491
+ target.pending = false;
3492
+ }
3493
+ consumedByToolResult = true;
3494
+ }
3495
+ }
3496
+ }
3497
+ if (role === "user" && consumedByToolResult && texts.length === 0) continue;
3498
+ const last = messages[messages.length - 1];
3499
+ if (last && last.role === role) {
3500
+ const joinedText = [last.content, texts.join("\n").trim()].filter(Boolean).join("\n");
3501
+ last.content = joinedText;
3502
+ if (parts.length > 0) {
3503
+ last.parts = [...last.parts ?? [], ...parts];
3504
+ }
3505
+ continue;
3506
+ }
3507
+ messages.push({
3508
+ role,
3509
+ content: texts.join("\n").trim(),
3510
+ parts: parts.length > 0 ? parts : void 0
3511
+ });
3512
+ }
3513
+ return messages;
3514
+ }
3515
+ async function syncConversationFromClaudeTranscript(conversationId) {
3516
+ const conv = await conversationStore.get(conversationId);
3517
+ if (!conv) return false;
3518
+ const transcript = await readClaudeTranscript(conversationId);
3519
+ if (!transcript.found) return false;
3520
+ const messages = claudeEntriesToMessages(transcript.entries);
3521
+ const isDefaultTitle = !conv.title || DEFAULT_TITLES.has(conv.title);
3522
+ const sameLength = messages.length === conv.messages.length;
3523
+ const sameLastContent = sameLength && (messages.at(-1)?.content ?? "") === (conv.messages.at(-1)?.content ?? "");
3524
+ if (sameLength && sameLastContent && !isDefaultTitle) {
3525
+ return false;
3526
+ }
3527
+ const patch = { messages };
3528
+ if (isDefaultTitle) {
3529
+ const firstUserMsg = messages.find((m) => m.role === "user" && m.content.trim().length > 0);
3530
+ if (firstUserMsg) {
3531
+ const firstLine = firstUserMsg.content.split("\n")[0].trim();
3532
+ patch.title = firstLine.length > 80 ? firstLine.slice(0, 80) + "\u2026" : firstLine;
3533
+ }
3534
+ }
3535
+ await conversationStore.update(conversationId, patch, { touch: false });
3536
+ return true;
3537
+ }
3538
+ var lastTranscriptActivity = /* @__PURE__ */ new Map();
3539
+ function getLastTranscriptActivity(sessionId) {
3540
+ return lastTranscriptActivity.get(sessionId) ?? null;
3541
+ }
3542
+ var started = false;
3543
+ var watchers = [];
3544
+ var pendingResyncs = /* @__PURE__ */ new Map();
3545
+ var RESYNC_DEBOUNCE_MS = 250;
3546
+ function scheduleResync(sessionId) {
3547
+ const existing = pendingResyncs.get(sessionId);
3548
+ if (existing) clearTimeout(existing);
3549
+ pendingResyncs.set(
3550
+ sessionId,
3551
+ setTimeout(() => {
3552
+ pendingResyncs.delete(sessionId);
3553
+ syncConversationFromClaudeTranscript(sessionId).catch((err) => {
3554
+ console.warn("[claude-transcript-sync] resync failed for", sessionId, err);
3555
+ });
3556
+ }, RESYNC_DEBOUNCE_MS)
3557
+ );
3558
+ }
3559
+ function handleFsEvent(filename) {
3560
+ if (!filename) return;
3561
+ if (!filename.endsWith(".jsonl")) return;
3562
+ const base = filename.split("/").pop();
3563
+ const sessionId = base.slice(0, -".jsonl".length);
3564
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId)) return;
3565
+ lastTranscriptActivity.set(sessionId, Date.now());
3566
+ scheduleResync(sessionId);
3567
+ }
3568
+ async function startClaudeTranscriptWatcher() {
3569
+ if (started) return;
3570
+ started = true;
3571
+ let topLevel;
3572
+ try {
3573
+ topLevel = await readdir2(PROJECTS_DIR);
3574
+ } catch {
3575
+ return;
3576
+ }
3577
+ try {
3578
+ const w = watch(PROJECTS_DIR, { recursive: true }, (_event, filename) => {
3579
+ handleFsEvent(filename ? filename.toString() : null);
3580
+ });
3581
+ w.on("error", (err) => console.warn("[claude-transcript-sync] watcher error:", err));
3582
+ watchers.push(w);
3583
+ return;
3584
+ } catch {
3585
+ }
3586
+ for (const dir of topLevel) {
3587
+ try {
3588
+ const w = watch(join10(PROJECTS_DIR, dir), (_event, filename) => {
3589
+ handleFsEvent(filename ? filename.toString() : null);
3590
+ });
3591
+ w.on("error", () => {
3592
+ });
3593
+ watchers.push(w);
3594
+ } catch {
3595
+ }
3596
+ }
3597
+ }
3598
+ var Id128Schema = z13.string().max(128);
3599
+ var ConversationIdSchema = z13.string().max(128);
3600
+ var NameSchema = z13.string().min(1).max(200);
3601
+ var ShortString200Schema = z13.string().max(200);
3602
+ var DescriptionSchema = z13.string().max(2e3);
3603
+ var OptionalProviderIdSchema = Id128Schema.optional();
3604
+ function generateAdminLlmsTxt(services, jwt) {
3605
+ const baseUrl = `http://localhost:${env.PORT}`;
3606
+ const trpcBase = `${baseUrl}/api/trpc`;
3607
+ const lines = [];
3608
+ lines.push("# Runeya \u2014 Service Management API");
3609
+ lines.push("");
3610
+ lines.push("> You are operating as an authenticated admin with full access to all services.");
3611
+ lines.push(`> Base URL: ${trpcBase}`);
3612
+ if (jwt) {
3613
+ lines.push("> Authentication: pass `Authorization: Bearer $RUNEYA_API_TOKEN` on all tRPC requests.");
3614
+ }
3615
+ lines.push("");
3616
+ if (services.length === 0) {
3617
+ lines.push("No services configured.");
3618
+ return lines.join("\n");
3619
+ }
3620
+ lines.push("## Services");
3621
+ lines.push("");
3622
+ for (const svc of services) {
3623
+ lines.push(`### ${svc.name}`);
3624
+ lines.push(`- **ID**: \`${svc.id}\``);
3625
+ if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3626
+ if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3627
+ if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3628
+ lines.push("- **Permitted actions**: all");
3629
+ lines.push("");
3630
+ }
3631
+ lines.push("## MCP Tools");
3632
+ lines.push("");
3633
+ lines.push("You have access to Runeya MCP tools to manage services. **Always prefer MCP tools** over raw HTTP calls.");
3634
+ lines.push("Available tools: `list_services`, `get_service_status`, `start_service`, `stop_service`, `restart_service`, `get_logs`.");
3635
+ lines.push("");
3636
+ if (jwt) {
3637
+ lines.push("**Important**: Pass the `authToken` parameter with value `$RUNEYA_API_TOKEN` (from environment) on every MCP tool call for authentication.");
3638
+ lines.push("");
3639
+ }
3640
+ lines.push("Use the service IDs and agent IDs listed above when calling these tools.");
3641
+ lines.push("");
3642
+ lines.push("## API Reference (fallback)");
3643
+ lines.push("");
3644
+ lines.push("Only use raw HTTP calls if MCP tools are unavailable.");
3645
+ lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3646
+ lines.push("");
3647
+ lines.push("**tRPC v11 request format:**");
3648
+ lines.push('- **GET queries**: `?input={"id":"abc"}`');
3649
+ lines.push('- **POST mutations**: JSON body `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3650
+ lines.push("");
3651
+ lines.push(`### List services
3652
+ \`GET ${trpcBase}/service.list\``);
3653
+ lines.push("");
3654
+ lines.push(`### Get service
3655
+ \`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3656
+ lines.push("");
3657
+ lines.push(`### Read logs
3658
+ \`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3659
+ lines.push("");
3660
+ lines.push(`### Start service
3661
+ \`POST ${trpcBase}/process.start\`
3662
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3663
+ lines.push("");
3664
+ lines.push(`### Stop service
3665
+ \`POST ${trpcBase}/process.stop\`
3666
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3667
+ lines.push("");
3668
+ lines.push(`### Restart service
3669
+ \`POST ${trpcBase}/process.restart\`
3670
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3671
+ lines.push("");
3672
+ lines.push(`### Get service status
3673
+ \`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3674
+ lines.push("");
3675
+ return lines.join("\n");
3676
+ }
3677
+ function generateLlmsTxt(apiKey, services) {
3678
+ const { scopes } = apiKey;
3679
+ const baseUrl = `http://localhost:${env.PORT}`;
3680
+ const trpcBase = `${baseUrl}/api/trpc`;
3681
+ const lines = [];
3682
+ lines.push("# Runeya \u2014 Service Management API");
3683
+ lines.push("");
3684
+ lines.push("> This file describes the API surface available for this key.");
3685
+ lines.push(`> Base URL: ${trpcBase}`);
3686
+ lines.push(`> Authentication: pass this key as \`Authorization: Bearer ${apiKey.key}\` on all tRPC requests.`);
3687
+ lines.push("");
3688
+ if (services.length === 0) {
3689
+ lines.push("No services accessible with this key.");
3690
+ return lines.join("\n");
3691
+ }
3692
+ const permByServiceId = new Map(
3693
+ scopes.services.map((p) => [p.serviceId, p])
3694
+ );
3695
+ lines.push("## Services");
3696
+ lines.push("");
3697
+ for (const svc of services) {
3698
+ const perm = permByServiceId.get(svc.id);
3699
+ const actions = perm?.actions ?? [];
3700
+ lines.push(`### ${svc.name}`);
3701
+ lines.push(`- **ID**: \`${svc.id}\``);
3702
+ if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3703
+ if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3704
+ if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3705
+ lines.push("- **Permitted actions**:");
3706
+ if (actions.length === 0) {
3707
+ lines.push(" - _(none)_");
3708
+ } else {
3709
+ for (const a of actions) lines.push(` - \`${a}\``);
3710
+ }
3711
+ const envPerms = perm?.environments ?? [];
3712
+ if (envPerms.length > 0) {
3713
+ lines.push("- **Environment access**:");
3714
+ for (const ep of envPerms) {
3715
+ const envActions = ep.actions.join(", ");
3716
+ const secrets = ep.includeSecrets ? " _(includes secrets)_" : "";
3717
+ lines.push(` - \`${ep.environmentId}\`: ${envActions}${secrets}`);
3718
+ }
3719
+ }
3720
+ lines.push("");
3721
+ }
3722
+ lines.push("## API Reference");
3723
+ lines.push("");
3724
+ lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3725
+ lines.push("");
3726
+ lines.push("**Important \u2014 tRPC v11 request format:**");
3727
+ lines.push('- **GET queries**: pass input as a plain JSON-encoded string in the `input` query param (no `{"json":...}` wrapper).');
3728
+ lines.push(' Example: `?input={"id":"abc"}`');
3729
+ lines.push('- **POST mutations**: send the input object as a plain JSON body (no `{"json":...}` wrapper).');
3730
+ lines.push(' Example body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3731
+ lines.push("- The `agentId` is listed per service above.");
3732
+ lines.push("- The `processId` is the same as the service `ID` listed above.");
3733
+ lines.push("");
3734
+ const allActions = new Set(scopes.services.flatMap((p) => p.actions));
3735
+ const hasEnvRead = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:read")));
3736
+ const hasEnvWrite = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:write")));
3737
+ if (allActions.has("service:read")) {
3738
+ lines.push("### List services");
3739
+ lines.push(`\`GET ${trpcBase}/service.list\``);
3740
+ lines.push("");
3741
+ lines.push("### Get service");
3742
+ lines.push(`\`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3743
+ lines.push("");
3744
+ }
3745
+ if (allActions.has("logs:read")) {
3746
+ lines.push("### Read logs");
3747
+ lines.push(`\`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3748
+ lines.push("");
3749
+ }
3750
+ if (allActions.has("service:start")) {
3751
+ lines.push("### Start service");
3752
+ lines.push(`\`POST ${trpcBase}/process.start\``);
3753
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3754
+ lines.push("");
3755
+ }
3756
+ if (allActions.has("service:stop")) {
3757
+ lines.push("### Stop service");
3758
+ lines.push(`\`POST ${trpcBase}/process.stop\``);
3759
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3760
+ lines.push("");
3761
+ }
3762
+ if (allActions.has("service:restart")) {
3763
+ lines.push("### Restart service");
3764
+ lines.push(`\`POST ${trpcBase}/process.restart\``);
3765
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3766
+ lines.push("");
3767
+ }
3768
+ if (allActions.has("service:status")) {
3769
+ lines.push("### Get service status");
3770
+ lines.push(`\`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3771
+ lines.push("");
3772
+ }
3773
+ if (allActions.has("service:healthcheck")) {
3774
+ lines.push("### Get service health");
3775
+ lines.push(`\`GET ${trpcBase}/process.health?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3776
+ lines.push("");
3777
+ }
3778
+ if (hasEnvRead) {
3779
+ lines.push("### Read environment variables (flat KEY=VALUE \u2014 secrets shown as *****)");
3780
+ lines.push(`\`GET ${trpcBase}/environment.resolveFlat?input={"environmentId":"<environmentId>"}\``);
3781
+ lines.push("Returns a flat `Record<string, string>`. Secret variables you are not authorized to see are shown as `*****`.");
3782
+ lines.push("");
3783
+ }
3784
+ if (hasEnvWrite) {
3785
+ lines.push("### Write environment variable");
3786
+ lines.push(`\`POST ${trpcBase}/environment.upsertVariable\``);
3787
+ lines.push('Body: `{"environmentId":"<environmentId>","key":"<key>","value":"<value>"}`');
3788
+ lines.push("");
3627
3789
  }
3628
- offChanged(handler) {
3629
- this.events.off("changed", handler);
3790
+ return lines.join("\n");
3791
+ }
3792
+ var CHAT_TOOLS = buildAnthropicTools(RUNEYA_CAPABILITIES);
3793
+ var CHAT_TOOLS_OPENAI = buildOpenAITools(RUNEYA_CAPABILITIES);
3794
+ var TOOL_SAFETY_INSTRUCTION = buildToolSafetyInstruction(RUNEYA_CAPABILITIES);
3795
+ function hasServiceAction(apiKey, serviceId, action) {
3796
+ if (!apiKey) return true;
3797
+ const perm = apiKey.scopes.services.find((p) => p.serviceId === serviceId);
3798
+ return perm?.actions.includes(action) ?? false;
3799
+ }
3800
+ var KNOWN_TOOL_NAMES = RUNEYA_CAPABILITIES.map((c) => c.name);
3801
+ function normalizeToolName(raw) {
3802
+ if (KNOWN_TOOL_NAMES.includes(raw)) return raw;
3803
+ const match = KNOWN_TOOL_NAMES.find((t2) => raw.startsWith(t2));
3804
+ return match ?? raw;
3805
+ }
3806
+ async function executeTool(rawName, input, apiKey, authToken) {
3807
+ const name = normalizeToolName(rawName);
3808
+ const cap = RUNEYA_CAPABILITIES.find((c) => c.name === name);
3809
+ if (!cap) throw new Error(`Unknown tool: ${name}`);
3810
+ const serviceId = input["serviceId"] ?? input["id"] ?? input["processId"];
3811
+ if (serviceId) {
3812
+ const actionMap = {
3813
+ get_service_status: "service:status",
3814
+ get_logs: "logs:read",
3815
+ start_service: "service:start",
3816
+ stop_service: "service:stop",
3817
+ restart_service: "service:restart"
3818
+ };
3819
+ const action = actionMap[name];
3820
+ if (action && !hasServiceAction(apiKey, serviceId, action)) {
3821
+ throw new Error(`No '${action}' permission for service ${serviceId}`);
3822
+ }
3630
3823
  }
3631
- };
3632
- var conversationStore = new ConversationStore();
3824
+ const params = Object.fromEntries(
3825
+ Object.entries(input).map(([k, v]) => [k, v != null ? String(v) : void 0])
3826
+ );
3827
+ const baseUrl = `http://localhost:${env.PORT}`;
3828
+ return callTrpc(baseUrl, cap.route, cap.method, cap.buildInput(params), authToken);
3829
+ }
3633
3830
  var MONTHS = {
3634
3831
  jan: 0,
3635
3832
  feb: 1,
@@ -4341,7 +4538,7 @@ async function atomicWriteJsonSecure(filePath, data, pretty = false) {
4341
4538
  }
4342
4539
  async function readJsonFileSafe(filePath) {
4343
4540
  try {
4344
- const raw = await readFile8(filePath, "utf-8");
4541
+ const raw = await readFile9(filePath, "utf-8");
4345
4542
  return JSON.parse(raw);
4346
4543
  } catch {
4347
4544
  return null;
@@ -4387,8 +4584,8 @@ var ScenarioRunStore = class _ScenarioRunStore {
4387
4584
  "- Use read-only references from linked upstream files as context.",
4388
4585
  ""
4389
4586
  ].join("\n");
4390
- await atomicWriteSecure(join10(dir, "README.md"), overview);
4391
- await atomicWriteSecure(join10(dir, "USER_REQUEST.md"), `# User request
4587
+ await atomicWriteSecure(join11(dir, "README.md"), overview);
4588
+ await atomicWriteSecure(join11(dir, "USER_REQUEST.md"), `# User request
4392
4589
 
4393
4590
  ${userInput.trim() || "(empty)"}
4394
4591
  `);
@@ -4414,7 +4611,7 @@ ${userInput.trim() || "(empty)"}
4414
4611
  "- Waiting for assignment.",
4415
4612
  ""
4416
4613
  ].join("\n");
4417
- await atomicWriteSecure(join10(dir, fileName), content);
4614
+ await atomicWriteSecure(join11(dir, fileName), content);
4418
4615
  }
4419
4616
  }
4420
4617
  async deleteRunDir(conversationId) {
@@ -4424,11 +4621,32 @@ ${userInput.trim() || "(empty)"}
4424
4621
  ensureRunState(conversationId) {
4425
4622
  let runState = this.activeRuns.get(conversationId);
4426
4623
  if (!runState) {
4427
- runState = { nodeStates: {}, nodeOutputs: {}, waitingInputs: {} };
4624
+ runState = { nodeStates: {}, nodeOutputs: {}, nodeSessionIds: {}, nodeAgentIds: {}, waitingInputs: {} };
4428
4625
  this.activeRuns.set(conversationId, runState);
4429
4626
  }
4430
4627
  return runState;
4431
4628
  }
4629
+ setNodeSession(conversationId, nodeId, sessionId, agentId) {
4630
+ const rs = this.ensureRunState(conversationId);
4631
+ rs.nodeSessionIds[nodeId] = sessionId;
4632
+ rs.nodeAgentIds[nodeId] = agentId;
4633
+ }
4634
+ getNodeSession(conversationId, nodeId) {
4635
+ const rs = this.activeRuns.get(conversationId);
4636
+ const sessionId = rs?.nodeSessionIds[nodeId];
4637
+ const agentId = rs?.nodeAgentIds[nodeId];
4638
+ if (sessionId && agentId) return { sessionId, agentId };
4639
+ return null;
4640
+ }
4641
+ async getNodeSessionPersistent(conversationId, nodeId) {
4642
+ const live = this.getNodeSession(conversationId, nodeId);
4643
+ if (live) return live;
4644
+ const persisted = await this.loadPersistedRunState(conversationId);
4645
+ const sessionId = persisted?.nodeSessionIds?.[nodeId];
4646
+ const agentId = persisted?.nodeAgentIds?.[nodeId];
4647
+ if (sessionId && agentId) return { sessionId, agentId };
4648
+ return null;
4649
+ }
4432
4650
  setNodeState(conversationId, nodeId, state) {
4433
4651
  this.ensureRunState(conversationId).nodeStates[nodeId] = state;
4434
4652
  }
@@ -4457,20 +4675,22 @@ ${userInput.trim() || "(empty)"}
4457
4675
  const data = {
4458
4676
  nodeStates: { ...runState.nodeStates },
4459
4677
  nodeOutputs: { ...runState.nodeOutputs },
4678
+ ...Object.keys(runState.nodeSessionIds).length > 0 ? { nodeSessionIds: { ...runState.nodeSessionIds } } : {},
4679
+ ...Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {},
4460
4680
  ...Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}
4461
4681
  };
4462
- await atomicWriteJsonSecure(join10(dir, "run-state.json"), data);
4682
+ await atomicWriteJsonSecure(join11(dir, "run-state.json"), data);
4463
4683
  } catch {
4464
4684
  }
4465
4685
  }
4466
4686
  async loadPersistedRunState(conversationId) {
4467
4687
  const dir = this.getRunDir(conversationId);
4468
- return readJsonFileSafe(join10(dir, "run-state.json"));
4688
+ return readJsonFileSafe(join11(dir, "run-state.json"));
4469
4689
  }
4470
4690
  async readUserRequest(conversationId) {
4471
4691
  try {
4472
4692
  const dir = this.getRunDir(conversationId);
4473
- const content = await readFile9(join10(dir, "USER_REQUEST.md"), "utf-8");
4693
+ const content = await readFile10(join11(dir, "USER_REQUEST.md"), "utf-8");
4474
4694
  return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
4475
4695
  } catch {
4476
4696
  return null;
@@ -4479,7 +4699,7 @@ ${userInput.trim() || "(empty)"}
4479
4699
  async readReadme(conversationId) {
4480
4700
  try {
4481
4701
  const dir = this.getRunDir(conversationId);
4482
- return await readFile9(join10(dir, "README.md"), "utf-8");
4702
+ return await readFile10(join11(dir, "README.md"), "utf-8");
4483
4703
  } catch {
4484
4704
  return null;
4485
4705
  }
@@ -4488,14 +4708,14 @@ ${userInput.trim() || "(empty)"}
4488
4708
  const dir = this.getRunDir(conversationId);
4489
4709
  await mkdir10(dir, { recursive: true });
4490
4710
  await atomicWriteJsonSecure(
4491
- join10(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
4711
+ join11(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
4492
4712
  scenario,
4493
4713
  true
4494
4714
  );
4495
4715
  }
4496
4716
  async loadScenarioSnapshot(conversationId) {
4497
4717
  const dir = this.getRunDir(conversationId);
4498
- return readJsonFileSafe(join10(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
4718
+ return readJsonFileSafe(join11(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
4499
4719
  }
4500
4720
  };
4501
4721
  var scenarioRunStore = new ScenarioRunStore();
@@ -4570,7 +4790,7 @@ var ImageStorageService = class {
4570
4790
  const imageId = randomUUID4();
4571
4791
  const safeExt = ext.replace(/[^a-z.]/g, "");
4572
4792
  const fileName = `${imageId}${safeExt}`;
4573
- const filePath = join11(this.dir, fileName);
4793
+ const filePath = join12(this.dir, fileName);
4574
4794
  const resolvedPath = resolve3(filePath);
4575
4795
  const resolvedDir = resolve3(this.dir);
4576
4796
  if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep2)) {
@@ -4584,10 +4804,10 @@ var ImageStorageService = class {
4584
4804
  async getImagePath(imageId) {
4585
4805
  if (!UUID_RE.test(imageId)) return null;
4586
4806
  try {
4587
- const files = await readdir2(this.dir);
4807
+ const files = await readdir3(this.dir);
4588
4808
  const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
4589
4809
  if (!metaFile) return null;
4590
- const raw = JSON.parse(await readFile10(join11(this.dir, metaFile), "utf-8"));
4810
+ const raw = JSON.parse(await readFile11(join12(this.dir, metaFile), "utf-8"));
4591
4811
  if (!existsSync(raw.filePath)) return null;
4592
4812
  return { filePath: raw.filePath, mimeType: raw.mimeType };
4593
4813
  } catch {
@@ -4605,14 +4825,14 @@ var ImageStorageService = class {
4605
4825
  async deleteByContextId(contextId) {
4606
4826
  let files;
4607
4827
  try {
4608
- files = await readdir2(this.dir);
4828
+ files = await readdir3(this.dir);
4609
4829
  } catch {
4610
4830
  return;
4611
4831
  }
4612
4832
  await Promise.all(
4613
4833
  files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
4614
4834
  try {
4615
- const raw = JSON.parse(await readFile10(join11(this.dir, file), "utf-8"));
4835
+ const raw = JSON.parse(await readFile11(join12(this.dir, file), "utf-8"));
4616
4836
  if (raw.contextId === contextId) {
4617
4837
  await unlink2(raw.filePath).catch(() => {
4618
4838
  });
@@ -4627,14 +4847,14 @@ var ImageStorageService = class {
4627
4847
  async readImageAsBase64(imageId) {
4628
4848
  const result = await this.getImagePath(imageId);
4629
4849
  if (!result) return null;
4630
- const buffer = await readFile10(result.filePath);
4850
+ const buffer = await readFile11(result.filePath);
4631
4851
  return { base64: buffer.toString("base64"), mimeType: result.mimeType };
4632
4852
  }
4633
4853
  async listExpiredImages(ttlMs = IMAGE_TTL_MS) {
4634
4854
  const expired = [];
4635
4855
  let files;
4636
4856
  try {
4637
- files = await readdir2(this.dir);
4857
+ files = await readdir3(this.dir);
4638
4858
  } catch {
4639
4859
  return [];
4640
4860
  }
@@ -4642,7 +4862,7 @@ var ImageStorageService = class {
4642
4862
  for (const file of files) {
4643
4863
  if (!file.endsWith(".meta.json")) continue;
4644
4864
  try {
4645
- const raw = JSON.parse(await readFile10(join11(this.dir, file), "utf-8"));
4865
+ const raw = JSON.parse(await readFile11(join12(this.dir, file), "utf-8"));
4646
4866
  if (now - raw.createdAt > ttlMs) expired.push(raw);
4647
4867
  } catch {
4648
4868
  }
@@ -4702,9 +4922,6 @@ async function resolveImagesToBase64(images) {
4702
4922
  );
4703
4923
  return results.filter((r) => r !== null);
4704
4924
  }
4705
- function buildApiDocs(apiBaseUrl, apiToken) {
4706
- return buildPromptDocs(apiBaseUrl, apiToken, RUNEYA_CAPABILITIES);
4707
- }
4708
4925
  function parseEndToken(output) {
4709
4926
  const tail = output.trimEnd().slice(-2e3);
4710
4927
  const branchMatch = tail.match(/<pass\s+branch="(\d+)"\s*\/>/);
@@ -4736,8 +4953,297 @@ var TOKEN_INSTRUCTIONS = `
4736
4953
 
4737
4954
  Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9ponse.
4738
4955
  `.trim();
4956
+ async function waitForFile(sessionId, signal, timeoutMs) {
4957
+ const start = Date.now();
4958
+ while (Date.now() - start < timeoutMs) {
4959
+ if (signal.aborted) throw new Error("Aborted");
4960
+ const path = await findClaudeSessionFile(sessionId);
4961
+ if (path) return path;
4962
+ await new Promise((r) => setTimeout(r, 150));
4963
+ }
4964
+ throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);
4965
+ }
4966
+ function tailTranscript(opts) {
4967
+ const { sessionId, signal, onEntry, fileAppearTimeoutMs = 3e4 } = opts;
4968
+ let stopped = false;
4969
+ let watcher = null;
4970
+ let fh = null;
4971
+ let offset = 0;
4972
+ let pendingBuf = "";
4973
+ let reading = false;
4974
+ let queuedRead = false;
4975
+ const cleanup = async () => {
4976
+ stopped = true;
4977
+ if (watcher) {
4978
+ try {
4979
+ watcher.close();
4980
+ } catch {
4981
+ }
4982
+ watcher = null;
4983
+ }
4984
+ if (fh) {
4985
+ try {
4986
+ await fh.close();
4987
+ } catch {
4988
+ }
4989
+ fh = null;
4990
+ }
4991
+ };
4992
+ const drainBuffer = () => {
4993
+ let idx;
4994
+ while ((idx = pendingBuf.indexOf("\n")) >= 0) {
4995
+ const line = pendingBuf.slice(0, idx).trim();
4996
+ pendingBuf = pendingBuf.slice(idx + 1);
4997
+ if (!line) continue;
4998
+ try {
4999
+ const entry = JSON.parse(line);
5000
+ onEntry(entry);
5001
+ } catch {
5002
+ }
5003
+ }
5004
+ };
5005
+ const readMore = async () => {
5006
+ if (!fh || stopped) return;
5007
+ if (reading) {
5008
+ queuedRead = true;
5009
+ return;
5010
+ }
5011
+ reading = true;
5012
+ try {
5013
+ while (!stopped) {
5014
+ const st = await fh.stat();
5015
+ if (st.size <= offset) break;
5016
+ const len = st.size - offset;
5017
+ const buf = Buffer.alloc(len);
5018
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
5019
+ if (bytesRead <= 0) break;
5020
+ offset += bytesRead;
5021
+ pendingBuf += buf.slice(0, bytesRead).toString("utf-8");
5022
+ drainBuffer();
5023
+ }
5024
+ } finally {
5025
+ reading = false;
5026
+ if (queuedRead && !stopped) {
5027
+ queuedRead = false;
5028
+ void readMore();
5029
+ }
5030
+ }
5031
+ };
5032
+ const whenDone = (async () => {
5033
+ const onAbort = () => {
5034
+ void cleanup();
5035
+ };
5036
+ signal.addEventListener("abort", onAbort, { once: true });
5037
+ try {
5038
+ const path = await waitForFile(sessionId, signal, fileAppearTimeoutMs);
5039
+ if (stopped) return;
5040
+ fh = await open(path, "r");
5041
+ await readMore();
5042
+ if (stopped) return;
5043
+ watcher = watch2(path, { persistent: false }, () => {
5044
+ void readMore();
5045
+ });
5046
+ watcher.on("error", () => {
5047
+ });
5048
+ const poll = setInterval(() => {
5049
+ void readMore();
5050
+ }, 500);
5051
+ await new Promise((resolve5) => {
5052
+ const tick = setInterval(() => {
5053
+ if (stopped) {
5054
+ clearInterval(tick);
5055
+ clearInterval(poll);
5056
+ resolve5();
5057
+ }
5058
+ }, 100);
5059
+ });
5060
+ } finally {
5061
+ signal.removeEventListener("abort", onAbort);
5062
+ await cleanup();
5063
+ }
5064
+ })();
5065
+ return {
5066
+ stop: () => {
5067
+ void cleanup();
5068
+ },
5069
+ whenDone
5070
+ };
5071
+ }
4739
5072
  async function getAgentManager() {
4740
- const { agentManager: agentManager2 } = await import("./agent-manager-A4E5CXFT-6PJDFN7J.js");
5073
+ const { agentManager: agentManager2 } = await import("./agent-manager-3DHUF2DC-KN6O7WSA.js");
5074
+ return agentManager2;
5075
+ }
5076
+ function buildPtyArgs(params, sessionId, opts) {
5077
+ const { isResume, systemContext, apiBaseUrl, apiToken, model, mcpConfig, skipTokenInstructions, prompt } = params;
5078
+ const sessionArg = isResume ? ["--resume", sessionId] : ["--session-id", sessionId];
5079
+ const apiDocs = buildApiDocs(apiBaseUrl, apiToken);
5080
+ const fullSystemContext = apiDocs ? `${systemContext}
5081
+
5082
+ ${apiDocs}` : systemContext;
5083
+ const systemPrompt = skipTokenInstructions ? fullSystemContext : `${fullSystemContext}
5084
+
5085
+ ${TOKEN_INSTRUCTIONS}`;
5086
+ const mcpArgs = mcpConfig && !opts.excludeMcp ? ["--mcp-config", mcpConfig] : [];
5087
+ const args = [
5088
+ ...sessionArg,
5089
+ "--dangerously-skip-permissions",
5090
+ "--model",
5091
+ model,
5092
+ "--append-system-prompt",
5093
+ systemPrompt,
5094
+ ...mcpArgs,
5095
+ prompt
5096
+ ];
5097
+ return args;
5098
+ }
5099
+ function dispatchEntry(entry, state, cbs) {
5100
+ if (entry.type !== "user" && entry.type !== "assistant") return;
5101
+ const msg = entry.message;
5102
+ if (!msg) return;
5103
+ const role = msg.role ?? entry.type;
5104
+ const content = msg.content;
5105
+ if (typeof content === "string") {
5106
+ if (role === "assistant" && content) {
5107
+ state.assistantText += content;
5108
+ cbs.onStatus("");
5109
+ cbs.onDelta(content);
5110
+ }
5111
+ return;
5112
+ }
5113
+ if (!Array.isArray(content)) return;
5114
+ for (const raw of content) {
5115
+ if (!raw || typeof raw !== "object") continue;
5116
+ const block = raw;
5117
+ if (block.type === "text" && typeof block.text === "string" && role === "assistant") {
5118
+ state.assistantText += block.text;
5119
+ cbs.onStatus("");
5120
+ cbs.onDelta(block.text);
5121
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
5122
+ cbs.onStatus("\u{1F4AD} En r\xE9flexion\u2026");
5123
+ cbs.onThinking?.(block.thinking);
5124
+ } else if (block.type === "tool_use" && block.id) {
5125
+ const name = block.name ?? "tool";
5126
+ state.tools.set(block.id, { id: block.id, name });
5127
+ cbs.onStatus(`\u{1F527} ${name}`);
5128
+ cbs.onToolCall?.(block.id, name, block.input ?? {});
5129
+ } else if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
5130
+ const c = block.content;
5131
+ let result = "";
5132
+ if (typeof c === "string") result = c;
5133
+ else if (Array.isArray(c)) {
5134
+ result = c.filter((b) => b["type"] === "text").map((b) => b["text"] ?? "").join("");
5135
+ }
5136
+ cbs.onToolResult?.(block.tool_use_id, result, block.is_error === true);
5137
+ }
5138
+ }
5139
+ }
5140
+ async function runClaudeViaPty(params) {
5141
+ const { agentId, signal, skipTokenInstructions } = params;
5142
+ if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
5143
+ const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID5();
5144
+ const args = buildPtyArgs(params, sessionId, { excludeMcp: true });
5145
+ const agentMgr = await getAgentManager();
5146
+ const displayArgs = args.map((a) => {
5147
+ if (a === params.prompt) return "<prompt>";
5148
+ if (a.includes(TOKEN_INSTRUCTIONS)) return "<system-context>";
5149
+ return a;
5150
+ });
5151
+ params.onCommand?.(`claude ${displayArgs.join(" ")}`);
5152
+ return new Promise((resolve5, reject) => {
5153
+ let settled = false;
5154
+ let spawnId = null;
5155
+ const state = { assistantText: "", tools: /* @__PURE__ */ new Map() };
5156
+ const innerAbort = new AbortController();
5157
+ const onParentAbort = () => innerAbort.abort();
5158
+ signal.addEventListener("abort", onParentAbort, { once: true });
5159
+ const cleanup = () => {
5160
+ signal.removeEventListener("abort", onParentAbort);
5161
+ innerAbort.abort();
5162
+ };
5163
+ const killSpawn = async () => {
5164
+ if (!spawnId) return;
5165
+ try {
5166
+ await agentMgr.agentMutation(agentId, "ai.kill", { spawnId });
5167
+ } catch {
5168
+ }
5169
+ };
5170
+ const settle = (result) => {
5171
+ if (settled) return;
5172
+ settled = true;
5173
+ cleanup();
5174
+ void killSpawn();
5175
+ resolve5(result);
5176
+ };
5177
+ const fail = (err) => {
5178
+ if (settled) return;
5179
+ settled = true;
5180
+ cleanup();
5181
+ void killSpawn();
5182
+ reject(err);
5183
+ };
5184
+ const tail = tailTranscript({
5185
+ sessionId,
5186
+ signal: innerAbort.signal,
5187
+ onEntry: (entry) => {
5188
+ try {
5189
+ dispatchEntry(entry, state, {
5190
+ onDelta: params.onDelta,
5191
+ onStatus: params.onStatus,
5192
+ onThinking: params.onThinking,
5193
+ onToolCall: params.onToolCall,
5194
+ onToolResult: params.onToolResult
5195
+ });
5196
+ } catch {
5197
+ }
5198
+ if (!skipTokenInstructions) {
5199
+ const { status, message, branchChoice } = parseEndToken(state.assistantText);
5200
+ if (status !== "none") {
5201
+ settle({
5202
+ output: state.assistantText,
5203
+ tokenStatus: status,
5204
+ tokenMessage: message,
5205
+ branchChoice,
5206
+ actualSessionId: sessionId
5207
+ });
5208
+ }
5209
+ }
5210
+ }
5211
+ });
5212
+ agentMgr.agentMutation(agentId, "ai.spawnPty", {
5213
+ cli: "claude",
5214
+ args,
5215
+ cols: 120,
5216
+ rows: 30,
5217
+ sessionId,
5218
+ enableMcp: !!params.mcpConfig,
5219
+ mcpApiBaseUrl: params.apiBaseUrl || void 0
5220
+ }).then(({ spawnId: id }) => {
5221
+ spawnId = id;
5222
+ if (signal.aborted) {
5223
+ fail(new Error("Aborted"));
5224
+ return;
5225
+ }
5226
+ }).catch((err) => {
5227
+ fail(new Error(`Failed to spawn claude PTY: ${err.message}`));
5228
+ });
5229
+ signal.addEventListener("abort", () => fail(new Error("Aborted")), { once: true });
5230
+ void tail.whenDone.then(() => {
5231
+ if (settled) return;
5232
+ if (skipTokenInstructions) {
5233
+ settle({
5234
+ output: state.assistantText,
5235
+ tokenStatus: "none",
5236
+ tokenMessage: "",
5237
+ actualSessionId: sessionId
5238
+ });
5239
+ } else {
5240
+ fail(new Error("Claude PTY ended before producing an end token"));
5241
+ }
5242
+ });
5243
+ });
5244
+ }
5245
+ async function getAgentManager2() {
5246
+ const { agentManager: agentManager2 } = await import("./agent-manager-3DHUF2DC-KN6O7WSA.js");
4741
5247
  return agentManager2;
4742
5248
  }
4743
5249
  function createClaudeStreamState() {
@@ -4961,6 +5467,10 @@ function processClaudeStdoutLines(lines, streamState, params, ctx) {
4961
5467
  }
4962
5468
  var ClaudeCodeRunner = class {
4963
5469
  run(params) {
5470
+ const ptyEnabled = params.usePty ?? process.env["RUNEYA_CLAUDE_PTY"] === "1";
5471
+ if (ptyEnabled && params.agentId && !(params.images && params.images.length > 0)) {
5472
+ return runClaudeViaPty(params);
5473
+ }
4964
5474
  if (params.agentId) {
4965
5475
  return this.runViaAgent(params);
4966
5476
  }
@@ -4970,7 +5480,7 @@ var ClaudeCodeRunner = class {
4970
5480
  async runViaAgent(params) {
4971
5481
  const { agentId, signal, skipTokenInstructions } = params;
4972
5482
  const { args, env: env2, stdinData, sessionArg } = buildClaudeSpawnConfig(params, { excludeMcp: true });
4973
- const agentMgr = await getAgentManager();
5483
+ const agentMgr = await getAgentManager2();
4974
5484
  const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
4975
5485
  console.log(`[claude-code-runner] [agent:${agentId}] arg size: ${totalArgSize} bytes, session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
4976
5486
  const displayArgs = args.map((a) => {
@@ -5305,8 +5815,8 @@ async function shutdownCodexAppServer() {
5305
5815
  await client.shutdown();
5306
5816
  }
5307
5817
  }
5308
- async function getAgentManager2() {
5309
- const { agentManager: agentManager2 } = await import("./agent-manager-A4E5CXFT-6PJDFN7J.js");
5818
+ async function getAgentManager3() {
5819
+ const { agentManager: agentManager2 } = await import("./agent-manager-3DHUF2DC-KN6O7WSA.js");
5310
5820
  return agentManager2;
5311
5821
  }
5312
5822
  function parseRuneyaCurlCommand(command) {
@@ -5375,7 +5885,7 @@ var CodexRunner = class {
5375
5885
  // ── Run via agent (app-server JSON-RPC via agent tRPC routes) ───────────
5376
5886
  async runViaAgent(params) {
5377
5887
  const { agentId, prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken } = params;
5378
- const agentMgr = await getAgentManager2();
5888
+ const agentMgr = await getAgentManager3();
5379
5889
  if (isResume && params.sessionId && !this.lastThreadId) {
5380
5890
  this.lastThreadId = params.sessionId;
5381
5891
  }
@@ -6638,6 +7148,325 @@ ${input.prompt}` : input.prompt;
6638
7148
  };
6639
7149
  runStream();
6640
7150
  yield* drainSession(session, signal, false);
7151
+ }),
7152
+ // ── PTY passthrough (interactive Claude Code session) ────────────────────
7153
+ pty: router({
7154
+ /** Start a PTY session running `claude --resume <sessionId>` (or fresh). */
7155
+ start: protectedProcedure.input(z14.object({
7156
+ agentId: z14.string().max(128).optional(),
7157
+ sessionId: z14.string().max(128).optional(),
7158
+ resume: z14.boolean().default(false),
7159
+ model: z14.string().max(128).optional(),
7160
+ cwd: z14.string().max(4096).optional(),
7161
+ cols: z14.number().int().min(1).max(1e3).default(120),
7162
+ rows: z14.number().int().min(1).max(1e3).default(30),
7163
+ enableMcp: z14.boolean().default(false),
7164
+ apiBaseUrl: z14.string().max(512).optional(),
7165
+ attachedContexts: z14.array(z14.object({
7166
+ label: z14.string().max(200),
7167
+ text: z14.string().max(1e6)
7168
+ })).max(20).optional()
7169
+ })).mutation(async ({ input, ctx }) => {
7170
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7171
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7172
+ if (input.sessionId) {
7173
+ try {
7174
+ const existing = await agentManager.agentQuery(
7175
+ agentId,
7176
+ "ai.findPtyBySession",
7177
+ { sessionId: input.sessionId }
7178
+ );
7179
+ if (existing && existing.status === "running") {
7180
+ await agentManager.agentMutation(agentId, "ai.resizePty", {
7181
+ spawnId: existing.spawnId,
7182
+ cols: input.cols,
7183
+ rows: input.rows
7184
+ }).catch(() => {
7185
+ });
7186
+ return { spawnId: existing.spawnId, agentId, reattached: true };
7187
+ }
7188
+ } catch {
7189
+ }
7190
+ }
7191
+ const args = [];
7192
+ if (input.sessionId) {
7193
+ const sessionExists = await claudeSessionExists(input.sessionId);
7194
+ if (input.resume || sessionExists) {
7195
+ args.push("--resume", input.sessionId);
7196
+ } else {
7197
+ args.push("--session-id", input.sessionId);
7198
+ }
7199
+ }
7200
+ if (input.model) args.push("--model", input.model);
7201
+ args.push("--dangerously-skip-permissions");
7202
+ const jwt = ctx.authorization?.startsWith("Bearer ") ? ctx.authorization.slice(7) : void 0;
7203
+ if (input.sessionId) {
7204
+ try {
7205
+ const conv = await conversationStore.get(input.sessionId);
7206
+ if (conv) {
7207
+ const systemContext = await buildCliSystemContext(conv, jwt);
7208
+ const apiBase = input.apiBaseUrl ?? `http://localhost:${env.PORT}`;
7209
+ const apiDocs = buildApiDocs(apiBase, jwt ?? "");
7210
+ const fullSystemPrompt = apiDocs ? `${systemContext}
7211
+
7212
+ ${apiDocs}` : systemContext;
7213
+ if (fullSystemPrompt.trim().length > 0) {
7214
+ args.push("--append-system-prompt", fullSystemPrompt);
7215
+ }
7216
+ }
7217
+ } catch (err) {
7218
+ console.warn("[chat.pty.start] buildCliSystemContext failed:", err.message);
7219
+ }
7220
+ }
7221
+ const res = await agentManager.agentMutation(
7222
+ agentId,
7223
+ "ai.spawnPty",
7224
+ {
7225
+ cli: "claude",
7226
+ args,
7227
+ cwd: input.cwd,
7228
+ cols: input.cols,
7229
+ rows: input.rows,
7230
+ sessionId: input.sessionId,
7231
+ enableMcp: input.enableMcp,
7232
+ mcpApiBaseUrl: input.apiBaseUrl ?? `http://localhost:${env.PORT}`,
7233
+ mcpApiToken: jwt
7234
+ }
7235
+ );
7236
+ const contexts = input.attachedContexts ?? [];
7237
+ if (contexts.length > 0) {
7238
+ const body = contexts.map((c) => `### ${c.label}
7239
+ ${c.text}`).join("\n\n");
7240
+ const injection = `\x1B[200~---------
7241
+ Context:
7242
+ \x1B[201~\x1B[200~${body}\x1B[201~\x1B[200~
7243
+ ---------
7244
+ \x1B[201~`;
7245
+ setTimeout(() => {
7246
+ agentManager.agentMutation(agentId, "ai.writePty", { spawnId: res.spawnId, data: injection }).catch((err) => {
7247
+ console.warn("[chat.pty.start] deferred context writePty failed:", err.message);
7248
+ });
7249
+ }, 500);
7250
+ }
7251
+ return { spawnId: res.spawnId, agentId, reattached: false };
7252
+ }),
7253
+ /** Subscribe to PTY output stream (raw text with ANSI). */
7254
+ stream: protectedProcedure.input(z14.object({
7255
+ agentId: z14.string().max(128).optional(),
7256
+ spawnId: z14.string().max(128),
7257
+ fromOffset: z14.number().int().min(0).default(0)
7258
+ })).subscription(async function* ({ input, signal }) {
7259
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7260
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7261
+ const queue = [];
7262
+ let resolve5 = null;
7263
+ let stopped = false;
7264
+ const unsub = agentManager.agentSubscribe(
7265
+ agentId,
7266
+ "ai.stream",
7267
+ { spawnId: input.spawnId, fromOffset: input.fromOffset },
7268
+ (data) => {
7269
+ queue.push(data);
7270
+ if (resolve5) {
7271
+ resolve5();
7272
+ resolve5 = null;
7273
+ }
7274
+ },
7275
+ () => {
7276
+ stopped = true;
7277
+ if (resolve5) {
7278
+ resolve5();
7279
+ resolve5 = null;
7280
+ }
7281
+ }
7282
+ );
7283
+ try {
7284
+ while (!signal?.aborted && !stopped) {
7285
+ while (queue.length > 0) {
7286
+ const item = queue.shift();
7287
+ yield item;
7288
+ if (item.chunk.type === "exit") {
7289
+ stopped = true;
7290
+ break;
7291
+ }
7292
+ }
7293
+ if (stopped) break;
7294
+ await new Promise((r) => {
7295
+ resolve5 = r;
7296
+ });
7297
+ }
7298
+ } finally {
7299
+ unsub();
7300
+ }
7301
+ }),
7302
+ /** Write input to a PTY session (keystrokes from xterm). */
7303
+ write: protectedProcedure.input(z14.object({
7304
+ agentId: z14.string().max(128).optional(),
7305
+ spawnId: z14.string().max(128),
7306
+ data: z14.string().max(1e6)
7307
+ })).mutation(async ({ input }) => {
7308
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7309
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7310
+ return agentManager.agentMutation(agentId, "ai.writePty", {
7311
+ spawnId: input.spawnId,
7312
+ data: input.data
7313
+ });
7314
+ }),
7315
+ /** Resize a PTY session. */
7316
+ resize: protectedProcedure.input(z14.object({
7317
+ agentId: z14.string().max(128).optional(),
7318
+ spawnId: z14.string().max(128),
7319
+ cols: z14.number().int().min(1).max(1e3),
7320
+ rows: z14.number().int().min(1).max(1e3)
7321
+ })).mutation(async ({ input }) => {
7322
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7323
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7324
+ return agentManager.agentMutation(agentId, "ai.resizePty", {
7325
+ spawnId: input.spawnId,
7326
+ cols: input.cols,
7327
+ rows: input.rows
7328
+ });
7329
+ }),
7330
+ /**
7331
+ * Upload a pasted image to the agent. The agent saves it to a tmp file and
7332
+ * tries to populate its system clipboard so Claude's native paste detection
7333
+ * picks it up. Returns the tmp path and which clipboard method succeeded
7334
+ * (null if none — caller should inject `@<path>` instead of Ctrl+V).
7335
+ */
7336
+ pasteImage: protectedProcedure.input(z14.object({
7337
+ agentId: z14.string().max(128).optional(),
7338
+ sessionId: z14.string().max(128).optional(),
7339
+ mimeType: z14.string().max(64),
7340
+ base64: z14.string().max(15e6)
7341
+ })).mutation(async ({ input }) => {
7342
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7343
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7344
+ const res = await agentManager.agentMutation(agentId, "ai.pasteImage", {
7345
+ sessionId: input.sessionId,
7346
+ mimeType: input.mimeType,
7347
+ base64: input.base64
7348
+ });
7349
+ if (!res.clipboardMethod) {
7350
+ console.warn("[chat.pty.pasteImage] clipboard spoof failed, using @path fallback:", res.clipboardError);
7351
+ }
7352
+ return res;
7353
+ }),
7354
+ /**
7355
+ * Read the transcript Claude Code stores on disk for a given session.
7356
+ * Returns parsed JSONL entries (user / assistant messages, tool uses, etc.).
7357
+ */
7358
+ transcript: protectedProcedure.input(z14.object({ sessionId: z14.string().max(128) })).query(async ({ input }) => {
7359
+ return readClaudeTranscript(input.sessionId);
7360
+ }),
7361
+ /**
7362
+ * Lightweight liveness probe: is a PTY running for this session, and
7363
+ * when was the transcript JSONL last touched? Used by the non-PTY view
7364
+ * to show a "Claude is thinking…" indicator while output streams.
7365
+ */
7366
+ activity: protectedProcedure.input(z14.object({
7367
+ agentId: z14.string().max(128).optional(),
7368
+ sessionId: z14.string().max(128)
7369
+ })).query(async ({ input }) => {
7370
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7371
+ let running = false;
7372
+ let lastChunkAt = null;
7373
+ if (agentId) {
7374
+ try {
7375
+ const existing = await agentManager.agentQuery(
7376
+ agentId,
7377
+ "ai.findPtyBySession",
7378
+ { sessionId: input.sessionId }
7379
+ );
7380
+ running = existing?.status === "running";
7381
+ lastChunkAt = existing?.lastChunkAt ?? null;
7382
+ } catch {
7383
+ }
7384
+ }
7385
+ const transcriptActivityAt = getLastTranscriptActivity(input.sessionId);
7386
+ const lastActivityAt = lastChunkAt && transcriptActivityAt ? Math.max(lastChunkAt, transcriptActivityAt) : lastChunkAt ?? transcriptActivityAt;
7387
+ return { running, lastActivityAt };
7388
+ }),
7389
+ /**
7390
+ * Convert the on-disk Claude transcript into Runeya Message[] and persist
7391
+ * them on the conversation, so the non-PTY chat view stays in sync.
7392
+ * Uses the conversation id as session id (PTY sessions are spawned that way).
7393
+ */
7394
+ syncTranscript: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
7395
+ const conv = await conversationStore.get(input.conversationId);
7396
+ if (!conv) {
7397
+ throw new TRPCError10({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
7398
+ }
7399
+ const synced = await syncConversationFromClaudeTranscript(conv.id);
7400
+ const after = await conversationStore.get(conv.id);
7401
+ return { synced, messageCount: after?.messages.length ?? 0 };
7402
+ }),
7403
+ /**
7404
+ * Debug-only: spawn a PTY that emits random data at a chosen rate, tagged
7405
+ * with the given sessionId. Used to benchmark the PTY → tRPC → xterm.js
7406
+ * pipeline. Open the conversation in PTY mode to attach to it.
7407
+ */
7408
+ debugStress: protectedProcedure.input(z14.object({
7409
+ sessionId: z14.string().max(128),
7410
+ agentId: z14.string().max(128).optional(),
7411
+ bytesPerSec: z14.number().int().min(100).max(1e7).default(5e4),
7412
+ chunkSize: z14.number().int().min(1).max(65536).default(256),
7413
+ durationMs: z14.number().int().min(0).max(36e5).default(0),
7414
+ cols: z14.number().int().min(1).max(1e3).default(120),
7415
+ rows: z14.number().int().min(1).max(1e3).default(30)
7416
+ })).mutation(async ({ input }) => {
7417
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7418
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7419
+ const stressSessionId = `${input.sessionId}-stress-${Date.now().toString(36)}`;
7420
+ const { existsSync: existsSync3 } = await import("fs");
7421
+ const candidates = [
7422
+ new URL("../../../../scripts/pty-stress-gen.mjs", import.meta.url),
7423
+ new URL("../../../../../scripts/pty-stress-gen.mjs", import.meta.url),
7424
+ new URL("../../../scripts/pty-stress-gen.mjs", import.meta.url)
7425
+ ];
7426
+ let scriptPath = null;
7427
+ for (const c of candidates) {
7428
+ const p = fileURLToPath(c);
7429
+ if (existsSync3(p)) {
7430
+ scriptPath = p;
7431
+ break;
7432
+ }
7433
+ }
7434
+ if (!scriptPath) {
7435
+ throw new TRPCError10({
7436
+ code: "INTERNAL_SERVER_ERROR",
7437
+ message: `pty-stress-gen.mjs not found near ${fileURLToPath(import.meta.url)}`
7438
+ });
7439
+ }
7440
+ const res = await agentManager.agentMutation(
7441
+ agentId,
7442
+ "ai.spawnPty",
7443
+ {
7444
+ cli: "node",
7445
+ args: [scriptPath],
7446
+ cwd: process.cwd(),
7447
+ cols: input.cols,
7448
+ rows: input.rows,
7449
+ sessionId: stressSessionId,
7450
+ env: {
7451
+ BYTES_PER_SEC: String(input.bytesPerSec),
7452
+ CHUNK_SIZE: String(input.chunkSize),
7453
+ DURATION_MS: String(input.durationMs)
7454
+ }
7455
+ }
7456
+ );
7457
+ return { spawnId: res.spawnId, agentId, stressSessionId };
7458
+ }),
7459
+ /** Kill a PTY session. */
7460
+ kill: protectedProcedure.input(z14.object({
7461
+ agentId: z14.string().max(128).optional(),
7462
+ spawnId: z14.string().max(128)
7463
+ })).mutation(async ({ input }) => {
7464
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7465
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7466
+ return agentManager.agentMutation(agentId, "ai.kill", {
7467
+ spawnId: input.spawnId
7468
+ });
7469
+ })
6641
7470
  })
6642
7471
  });
6643
7472
  var SERVICE_ID_SCHEMA = z15.string().max(128);
@@ -6657,80 +7486,97 @@ async function resolveServiceContext(serviceId) {
6657
7486
  }
6658
7487
  return { agentId, cwd: service.cwd || "." };
6659
7488
  }
7489
+ var gitTargetShape = {
7490
+ serviceId: SERVICE_ID_SCHEMA.optional(),
7491
+ agentId: z15.string().max(128).optional()
7492
+ };
7493
+ async function resolveGitContext(input) {
7494
+ if (input.serviceId) return resolveServiceContext(input.serviceId);
7495
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7496
+ if (!agentId) {
7497
+ throw new TRPCError11({ code: "BAD_REQUEST", message: "No agent available" });
7498
+ }
7499
+ return { agentId, cwd: "." };
7500
+ }
6660
7501
  var gitRouter = router({
6661
7502
  /** Get full git status */
6662
- status: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).query(async ({ input }) => {
6663
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7503
+ status: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
7504
+ const { agentId, cwd } = await resolveGitContext(input);
6664
7505
  return agentManager.agentQuery(agentId, "git.status", { cwd });
6665
7506
  }),
6666
7507
  /** Get commit log */
6667
- log: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, limit: z15.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
6668
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7508
+ log: protectedProcedure.input(z15.object({ ...gitTargetShape, limit: z15.number().int().min(1).max(200).default(50) })).query(async ({ input }) => {
7509
+ const { agentId, cwd } = await resolveGitContext(input);
6669
7510
  return agentManager.agentQuery(agentId, "git.log", { cwd, limit: input.limit });
6670
7511
  }),
6671
7512
  /** List local and remote branches */
6672
- branches: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).query(async ({ input }) => {
6673
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7513
+ branches: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
7514
+ const { agentId, cwd } = await resolveGitContext(input);
6674
7515
  return agentManager.agentQuery(agentId, "git.branches", { cwd });
6675
7516
  }),
6676
7517
  /** Checkout a branch */
6677
- checkout: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, branch: BRANCH_SCHEMA, create: z15.boolean().default(false) })).mutation(async ({ input }) => {
6678
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7518
+ checkout: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, create: z15.boolean().default(false) })).mutation(async ({ input }) => {
7519
+ const { agentId, cwd } = await resolveGitContext(input);
6679
7520
  return agentManager.agentMutation(agentId, "git.checkout", { cwd, branch: input.branch, create: input.create });
6680
7521
  }),
6681
7522
  /** Stage files */
6682
- stage: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
6683
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7523
+ stage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7524
+ const { agentId, cwd } = await resolveGitContext(input);
6684
7525
  return agentManager.agentMutation(agentId, "git.stage", { cwd, paths: input.paths });
6685
7526
  }),
6686
7527
  /** Stage all files */
6687
- stageAll: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).mutation(async ({ input }) => {
6688
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7528
+ stageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7529
+ const { agentId, cwd } = await resolveGitContext(input);
6689
7530
  return agentManager.agentMutation(agentId, "git.stageAll", { cwd });
6690
7531
  }),
6691
7532
  /** Unstage files */
6692
- unstage: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
6693
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7533
+ unstage: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7534
+ const { agentId, cwd } = await resolveGitContext(input);
6694
7535
  return agentManager.agentMutation(agentId, "git.unstage", { cwd, paths: input.paths });
6695
7536
  }),
7537
+ /** Unstage all files */
7538
+ unstageAll: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7539
+ const { agentId, cwd } = await resolveGitContext(input);
7540
+ return agentManager.agentMutation(agentId, "git.unstageAll", { cwd });
7541
+ }),
6696
7542
  /** Commit staged changes */
6697
- commit: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, message: z15.string().min(1).max(4096) })).mutation(async ({ input }) => {
6698
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7543
+ commit: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().min(1).max(4096) })).mutation(async ({ input }) => {
7544
+ const { agentId, cwd } = await resolveGitContext(input);
6699
7545
  return agentManager.agentMutation(agentId, "git.commit", { cwd, message: input.message });
6700
7546
  }),
6701
7547
  /** Push to remote */
6702
- push: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z15.boolean().default(false) })).mutation(async ({ input }) => {
6703
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7548
+ push: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin"), setUpstream: z15.boolean().default(false) })).mutation(async ({ input }) => {
7549
+ const { agentId, cwd } = await resolveGitContext(input);
6704
7550
  return agentManager.agentMutation(agentId, "git.push", { cwd, remote: input.remote, setUpstream: input.setUpstream });
6705
7551
  }),
6706
7552
  /** Pull from remote */
6707
- pull: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
6708
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7553
+ pull: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7554
+ const { agentId, cwd } = await resolveGitContext(input);
6709
7555
  return agentManager.agentMutation(agentId, "git.pull", { cwd, remote: input.remote });
6710
7556
  }),
6711
7557
  /** Fetch from remote */
6712
- fetch: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
6713
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7558
+ fetch: protectedProcedure.input(z15.object({ ...gitTargetShape, remote: REMOTE_SCHEMA.default("origin") })).mutation(async ({ input }) => {
7559
+ const { agentId, cwd } = await resolveGitContext(input);
6714
7560
  return agentManager.agentMutation(agentId, "git.fetch", { cwd, remote: input.remote });
6715
7561
  }),
6716
7562
  /** Get diff for a file or the whole index */
6717
7563
  diff: protectedProcedure.input(z15.object({
6718
- serviceId: SERVICE_ID_SCHEMA,
7564
+ ...gitTargetShape,
6719
7565
  path: z15.string().max(4096).optional(),
6720
7566
  staged: z15.boolean().default(false),
6721
7567
  untracked: z15.boolean().default(false)
6722
7568
  })).query(async ({ input }) => {
6723
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7569
+ const { agentId, cwd } = await resolveGitContext(input);
6724
7570
  return agentManager.agentQuery(agentId, "git.diff", { cwd, path: input.path, staged: input.staged, untracked: input.untracked });
6725
7571
  }),
6726
7572
  /** Discard changes to working tree files */
6727
- discard: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
6728
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7573
+ discard: protectedProcedure.input(z15.object({ ...gitTargetShape, paths: z15.array(SAFE_PATH_SCHEMA).min(1).max(500) })).mutation(async ({ input }) => {
7574
+ const { agentId, cwd } = await resolveGitContext(input);
6729
7575
  return agentManager.agentMutation(agentId, "git.discard", { cwd, paths: input.paths });
6730
7576
  }),
6731
7577
  /** Generate a commit message from staged diffs using AI (diff fetched from agent, AI runs on server) */
6732
- generateCommitMessage: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).mutation(async ({ input }) => {
6733
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7578
+ generateCommitMessage: protectedProcedure.input(z15.object({ ...gitTargetShape })).mutation(async ({ input }) => {
7579
+ const { agentId, cwd } = await resolveGitContext(input);
6734
7580
  const { diff } = await agentManager.agentQuery(agentId, "git.stagedDiff", { cwd });
6735
7581
  if (!diff.trim()) {
6736
7582
  throw new TRPCError11({ code: "BAD_REQUEST", message: "No staged changes to generate a commit message from" });
@@ -6773,18 +7619,43 @@ Rules:
6773
7619
  throw new TRPCError11({ code: "BAD_REQUEST", message: "Unsupported AI provider" });
6774
7620
  }),
6775
7621
  /** Merge a branch into the current branch */
6776
- merge: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, branch: BRANCH_SCHEMA, noFf: z15.boolean().default(false) })).mutation(async ({ input }) => {
6777
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7622
+ merge: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA, noFf: z15.boolean().default(false) })).mutation(async ({ input }) => {
7623
+ const { agentId, cwd } = await resolveGitContext(input);
6778
7624
  return agentManager.agentMutation(agentId, "git.merge", { cwd, branch: input.branch, noFf: input.noFf });
6779
7625
  }),
6780
7626
  /** Rebase current branch onto another branch */
6781
- rebase: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
6782
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7627
+ rebase: protectedProcedure.input(z15.object({ ...gitTargetShape, branch: BRANCH_SCHEMA })).mutation(async ({ input }) => {
7628
+ const { agentId, cwd } = await resolveGitContext(input);
6783
7629
  return agentManager.agentMutation(agentId, "git.rebase", { cwd, branch: input.branch });
6784
7630
  }),
7631
+ /** List stash entries */
7632
+ stashList: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
7633
+ const { agentId, cwd } = await resolveGitContext(input);
7634
+ return agentManager.agentQuery(agentId, "git.stashList", { cwd });
7635
+ }),
7636
+ /** Stash current changes */
7637
+ stashPush: protectedProcedure.input(z15.object({ ...gitTargetShape, message: z15.string().max(4096).optional(), includeUntracked: z15.boolean().default(true) })).mutation(async ({ input }) => {
7638
+ const { agentId, cwd } = await resolveGitContext(input);
7639
+ return agentManager.agentMutation(agentId, "git.stashPush", { cwd, message: input.message, includeUntracked: input.includeUntracked });
7640
+ }),
7641
+ /** Pop a stash entry */
7642
+ stashPop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
7643
+ const { agentId, cwd } = await resolveGitContext(input);
7644
+ return agentManager.agentMutation(agentId, "git.stashPop", { cwd, index: input.index });
7645
+ }),
7646
+ /** Apply a stash entry without dropping it */
7647
+ stashApply: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
7648
+ const { agentId, cwd } = await resolveGitContext(input);
7649
+ return agentManager.agentMutation(agentId, "git.stashApply", { cwd, index: input.index });
7650
+ }),
7651
+ /** Drop a stash entry */
7652
+ stashDrop: protectedProcedure.input(z15.object({ ...gitTargetShape, index: z15.number().int().min(0).max(1e3).default(0) })).mutation(async ({ input }) => {
7653
+ const { agentId, cwd } = await resolveGitContext(input);
7654
+ return agentManager.agentMutation(agentId, "git.stashDrop", { cwd, index: input.index });
7655
+ }),
6785
7656
  /** Watch .git/ for changes and stream updated status */
6786
- onStatusChange: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).subscription(async function* ({ input }) {
6787
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7657
+ onStatusChange: protectedProcedure.input(z15.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
7658
+ const { agentId, cwd } = await resolveGitContext(input);
6788
7659
  const queue = [];
6789
7660
  let resolve5 = null;
6790
7661
  let done = false;
@@ -6830,8 +7701,8 @@ Rules:
6830
7701
  }
6831
7702
  }),
6832
7703
  /** List remotes */
6833
- remotes: protectedProcedure.input(z15.object({ serviceId: SERVICE_ID_SCHEMA })).query(async ({ input }) => {
6834
- const { agentId, cwd } = await resolveServiceContext(input.serviceId);
7704
+ remotes: protectedProcedure.input(z15.object({ ...gitTargetShape })).query(async ({ input }) => {
7705
+ const { agentId, cwd } = await resolveGitContext(input);
6835
7706
  return agentManager.agentQuery(agentId, "git.remotes", { cwd });
6836
7707
  })
6837
7708
  });
@@ -7270,7 +8141,7 @@ function applyHireDirectivesFromOutput(manager, scenario, output) {
7270
8141
  }
7271
8142
  const hired = [];
7272
8143
  for (const payload of hireDirectives) {
7273
- const id = `hire-${randomUUID5().slice(0, 8)}`;
8144
+ const id = `hire-${randomUUID6().slice(0, 8)}`;
7274
8145
  const position = computeHirePosition(manager, scenario.nodes);
7275
8146
  ensureGroupCanContainNode(scenario, manager.groupId ?? null, position);
7276
8147
  const node = {
@@ -7634,6 +8505,9 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
7634
8505
  model,
7635
8506
  effort: "normal",
7636
8507
  signal,
8508
+ // Scenarios default to interactive PTY to stay on subscription pricing.
8509
+ // Override with RUNEYA_CLAUDE_PTY=0 to fall back to headless `-p` mode.
8510
+ usePty: aiCli === "claude-code" && process.env["RUNEYA_CLAUDE_PTY"] !== "0",
7637
8511
  onDelta: (text) => {
7638
8512
  accumulated += text;
7639
8513
  onDelta(text);
@@ -7648,6 +8522,11 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
7648
8522
  flushPersist();
7649
8523
  if (result.actualSessionId) state.sessionId = result.actualSessionId;
7650
8524
  state.isResume = true;
8525
+ const agentForNode = runOverrides?.agentId ?? agentManager.getDefaultAgentId() ?? null;
8526
+ if (state.sessionId && agentForNode) {
8527
+ scenarioRunStore.setNodeSession(conversationId, node.id, state.sessionId, agentForNode);
8528
+ void scenarioRunStore.persistRunState(conversationId);
8529
+ }
7651
8530
  if (result.tokenStatus === "ask_user") {
7652
8531
  publish2({ type: "node_duration", nodeId: node.id, durationMs: Date.now() - turnStart });
7653
8532
  scenarioRunStore.setNodeState(conversationId, node.id, "waiting_input");
@@ -7698,7 +8577,7 @@ async function appendNodeOutput(conversationId, node, output) {
7698
8577
  if (!dir) return;
7699
8578
  const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
7700
8579
  const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
7701
- const filePath = join12(dir, `${slug}_${safeId}.md`);
8580
+ const filePath = join13(dir, `${slug}_${safeId}.md`);
7702
8581
  const content = `## Output
7703
8582
 
7704
8583
  ${output}
@@ -7713,7 +8592,7 @@ async function appendToReadme(conversationId, bus, text) {
7713
8592
  try {
7714
8593
  await mkdir12(dir, { recursive: true });
7715
8594
  const { appendFile: appendFile2 } = await import("fs/promises");
7716
- await appendFile2(join12(dir, "README.md"), line, "utf-8");
8595
+ await appendFile2(join13(dir, "README.md"), line, "utf-8");
7717
8596
  } catch {
7718
8597
  }
7719
8598
  publishToBus(bus, { type: "readme_append", text: line });
@@ -7733,7 +8612,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
7733
8612
 
7734
8613
  ${content}
7735
8614
  `;
7736
- const filePath = join12(dir, "FINAL.md");
8615
+ const filePath = join13(dir, "FINAL.md");
7737
8616
  await atomicWriteSecure(filePath, fullContent);
7738
8617
  await appendToReadme(
7739
8618
  conversationId,
@@ -8356,6 +9235,8 @@ ${out}`;
8356
9235
  ).catch(() => {
8357
9236
  });
8358
9237
  }
9238
+ await scenarioRunStore.persistRunState(conversationId).catch(() => {
9239
+ });
8359
9240
  publishToBus(bus, null);
8360
9241
  activeAbortControllers.delete(conversationId);
8361
9242
  }
@@ -8412,7 +9293,7 @@ var scenarioRouter = router({
8412
9293
  })).mutation(({ input }) => {
8413
9294
  const now = Date.now();
8414
9295
  return createOrBadRequest(() => scenarioStore.create({
8415
- id: randomUUID6(),
9296
+ id: randomUUID7(),
8416
9297
  name: input.name,
8417
9298
  icon: input.icon,
8418
9299
  description: input.description,
@@ -8533,6 +9414,17 @@ var scenarioRouter = router({
8533
9414
  }
8534
9415
  }
8535
9416
  }),
9417
+ /**
9418
+ * Returns the Claude Code session-id + agent-id for a node, so the UI can
9419
+ * open a debug PTY with `claude --resume <sessionId>` on the right agent.
9420
+ * Falls back to the persisted run-state for past nodes.
9421
+ */
9422
+ getNodeSession: protectedProcedure.input(z16.object({
9423
+ conversationId: ConversationIdSchema,
9424
+ nodeId: Id128Schema
9425
+ })).query(async ({ input }) => {
9426
+ return await scenarioRunStore.getNodeSessionPersistent(input.conversationId, input.nodeId);
9427
+ }),
8536
9428
  listPresets: protectedProcedure.query(() => listPresets()),
8537
9429
  getPreset: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => {
8538
9430
  const preset = getPreset(input.id);
@@ -8600,7 +9492,7 @@ var debugRouter = router({
8600
9492
  intervalMs: z17.number().int().min(100).max(5e3).optional(),
8601
9493
  maxTicks: z17.number().int().min(1).max(1e4).optional()
8602
9494
  }).optional()).mutation(({ input }) => {
8603
- const jobId = randomUUID7();
9495
+ const jobId = randomUUID8();
8604
9496
  const intervalMs = input?.intervalMs ?? 400;
8605
9497
  const maxTicks = input?.maxTicks ?? 500;
8606
9498
  const job = {
@@ -8786,7 +9678,7 @@ var KanbanQueueService = class extends EventEmitter3 {
8786
9678
  return { id: alreadyRunning.id, addedAt: alreadyRunning.startedAt, ...item };
8787
9679
  }
8788
9680
  const queueItem = {
8789
- id: randomUUID8(),
9681
+ id: randomUUID9(),
8790
9682
  ...item,
8791
9683
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
8792
9684
  };
@@ -9174,7 +10066,7 @@ var SENSITIVE_FILES = [
9174
10066
  var SECURE_MODE = 384;
9175
10067
  async function ensureFilePermissions() {
9176
10068
  for (const filename of SENSITIVE_FILES) {
9177
- const filepath = join13(env.DATA_DIR, filename);
10069
+ const filepath = join14(env.DATA_DIR, filename);
9178
10070
  try {
9179
10071
  const stats = await stat3(filepath);
9180
10072
  const mode = stats.mode & 511;
@@ -9222,7 +10114,7 @@ conversations.private.json
9222
10114
  *.log.*
9223
10115
  `;
9224
10116
  async function ensureDataGitignore(dataDir) {
9225
- const gitignorePath = join14(dataDir, ".gitignore");
10117
+ const gitignorePath = join15(dataDir, ".gitignore");
9226
10118
  await writeFile10(gitignorePath, GITIGNORE_CONTENT, "utf-8");
9227
10119
  }
9228
10120
  var _require = createRequire(import.meta.url);
@@ -9331,7 +10223,7 @@ async function getSodium() {
9331
10223
  return _sodium;
9332
10224
  }
9333
10225
  async function decryptLegacyFile(filePath, key, sodium) {
9334
- const raw = (await readFile11(filePath, "utf-8")).trim();
10226
+ const raw = (await readFile12(filePath, "utf-8")).trim();
9335
10227
  if (!raw || raw === "[]" || raw === "{}") {
9336
10228
  try {
9337
10229
  return JSON.parse(raw);
@@ -9357,29 +10249,29 @@ async function atomicWrite(path, data) {
9357
10249
  async function runLegacyMigration(dataDir) {
9358
10250
  const { resolve: resolve5 } = await import("path");
9359
10251
  const abCwd = resolve5(process.cwd());
9360
- const legacyRoot = join15(abCwd, env.DATA_DIR);
9361
- const legacyKeyPath = join15(legacyRoot, "dbs", "encryption-key.json");
10252
+ const legacyRoot = join16(abCwd, env.DATA_DIR);
10253
+ const legacyKeyPath = join16(legacyRoot, "dbs", "encryption-key.json");
9362
10254
  try {
9363
- await access(legacyKeyPath);
10255
+ await access2(legacyKeyPath);
9364
10256
  } catch {
9365
10257
  return false;
9366
10258
  }
9367
10259
  console.log("[legacy-migration] Legacy Runeya data detected, starting migration\u2026");
9368
10260
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
9369
- const legacyTmp = join15(abCwd, ".runeya-tmp");
10261
+ const legacyTmp = join16(abCwd, ".runeya-tmp");
9370
10262
  await rename9(legacyRoot, legacyTmp);
9371
10263
  await mkdir13(dataDir, { recursive: true });
9372
- const legacyOld = join15(dataDir, `backup-${timestamp}`);
10264
+ const legacyOld = join16(dataDir, `backup-${timestamp}`);
9373
10265
  await rename9(legacyTmp, legacyOld);
9374
10266
  console.log(`[legacy-migration] Legacy data backed up to ${legacyOld}`);
9375
- const keyJson = JSON.parse(await readFile11(join15(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
10267
+ const keyJson = JSON.parse(await readFile12(join16(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
9376
10268
  const sodium = await getSodium();
9377
10269
  const sodiumKey = sodium.from_base64(keyJson.encryptionKey);
9378
10270
  const keyBuffer = Buffer.from(sodiumKey);
9379
10271
  const runyeaKeyHex = keyBuffer.toString("hex");
9380
10272
  await setEncryptionKey(runyeaKeyHex);
9381
10273
  console.log("[legacy-migration] Encryption key loaded in memory (temporary)");
9382
- const dbsDir = join15(legacyOld, "dbs");
10274
+ const dbsDir = join16(legacyOld, "dbs");
9383
10275
  async function readEncrypted(filePath) {
9384
10276
  try {
9385
10277
  return await decryptLegacyFile(filePath, sodiumKey, sodium);
@@ -9388,29 +10280,29 @@ async function runLegacyMigration(dataDir) {
9388
10280
  return null;
9389
10281
  }
9390
10282
  }
9391
- const envsDir = join15(dbsDir, "envs");
9392
- const envFiles = (await readdir3(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10283
+ const envsDir = join16(dbsDir, "envs");
10284
+ const envFiles = (await readdir4(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9393
10285
  const legacyEnvs = {};
9394
10286
  for (const file of envFiles) {
9395
10287
  const name = file.replace(".encrypted.json", "");
9396
- legacyEnvs[name] = await readEncrypted(join15(envsDir, file));
10288
+ legacyEnvs[name] = await readEncrypted(join16(envsDir, file));
9397
10289
  }
9398
- const servicesDir = join15(dbsDir, "services");
9399
- const serviceFiles = (await readdir3(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10290
+ const servicesDir = join16(dbsDir, "services");
10291
+ const serviceFiles = (await readdir4(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9400
10292
  const legacyServices = {};
9401
10293
  for (const file of serviceFiles) {
9402
10294
  const name = file.replace(".encrypted.json", "");
9403
10295
  if (!name) continue;
9404
- const data = await readEncrypted(join15(servicesDir, file));
10296
+ const data = await readEncrypted(join16(servicesDir, file));
9405
10297
  if (data?.label) legacyServices[name] = data;
9406
10298
  }
9407
- const overridesDir = join15(dbsDir, "overrides");
9408
- const overrideFiles = (await readdir3(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10299
+ const overridesDir = join16(dbsDir, "overrides");
10300
+ const overrideFiles = (await readdir4(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9409
10301
  const legacyServiceOverrides = {};
9410
10302
  const legacyEnvOverrides = {};
9411
10303
  for (const file of overrideFiles) {
9412
10304
  const baseName = file.replace(".encrypted.json", "");
9413
- const data = await readEncrypted(join15(overridesDir, file));
10305
+ const data = await readEncrypted(join16(overridesDir, file));
9414
10306
  if (!data) continue;
9415
10307
  if (baseName.endsWith("-envs")) {
9416
10308
  legacyServiceOverrides[baseName.slice(0, -5)] = data;
@@ -9418,18 +10310,18 @@ async function runLegacyMigration(dataDir) {
9418
10310
  legacyEnvOverrides[baseName.slice(0, -12)] = data;
9419
10311
  }
9420
10312
  }
9421
- const parsersDir = join15(dbsDir, "parsers");
9422
- const parserFiles = (await readdir3(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10313
+ const parsersDir = join16(dbsDir, "parsers");
10314
+ const parserFiles = (await readdir4(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9423
10315
  const legacyParsers = [];
9424
10316
  for (const file of parserFiles) {
9425
- const data = await readEncrypted(join15(parsersDir, file));
10317
+ const data = await readEncrypted(join16(parsersDir, file));
9426
10318
  if (data?.id) legacyParsers.push(data);
9427
10319
  }
9428
- const projectMonoId = randomUUID9();
10320
+ const projectMonoId = randomUUID10();
9429
10321
  const envIdMap = {};
9430
10322
  const labelToKey = {};
9431
10323
  for (const [name, legacyEnv] of Object.entries(legacyEnvs)) {
9432
- envIdMap[name] = randomUUID9();
10324
+ envIdMap[name] = randomUUID10();
9433
10325
  if (legacyEnv?.label) labelToKey[legacyEnv.label] = name;
9434
10326
  }
9435
10327
  const runeEnvs = [];
@@ -9454,7 +10346,7 @@ async function runLegacyMigration(dataDir) {
9454
10346
  if (hasInvalidInterpolation(value)) continue;
9455
10347
  const cleaned = cleanVarKey(key);
9456
10348
  if (inheritedVars[cleaned] === value) continue;
9457
- const varId = randomUUID9();
10349
+ const varId = randomUUID10();
9458
10350
  variables[varId] = makeVariable(cleaned, value);
9459
10351
  varIdLookup.set(`${envIdMap[name]}:${cleaned}`, varId);
9460
10352
  }
@@ -9475,7 +10367,7 @@ async function runLegacyMigration(dataDir) {
9475
10367
  const serviceIdMap = {};
9476
10368
  const runeServices = [];
9477
10369
  for (const [name, legacy] of Object.entries(legacyServices)) {
9478
- const id = randomUUID9();
10370
+ const id = randomUUID10();
9479
10371
  serviceIdMap[name] = id;
9480
10372
  const rootPath = legacy.rootPath || "";
9481
10373
  const rootPathCwd = rootPath && rootPath !== "." ? rootPath : "";
@@ -9484,7 +10376,7 @@ async function runLegacyMigration(dataDir) {
9484
10376
  const parts = [cmd.spawnCmd, ...cmd.spawnArgs || []].filter(Boolean);
9485
10377
  if (!parts.length) continue;
9486
10378
  commands.push({
9487
- id: cmd.id || randomUUID9(),
10379
+ id: cmd.id || randomUUID10(),
9488
10380
  label: "Launch",
9489
10381
  command: parts.join(" "),
9490
10382
  cwd: cmd.spawnOptions?.cwd || "",
@@ -9545,7 +10437,7 @@ async function runLegacyMigration(dataDir) {
9545
10437
  const legacyBootstrapCmds = container.bootstrap?.commands || [];
9546
10438
  if (legacyBootstrapCmds.length > 0) {
9547
10439
  dockerConfig.bootstrapCommands = legacyBootstrapCmds.filter((bs) => bs.cmd || bs.entrypoint).map((bs, idx) => ({
9548
- id: bs.id || randomUUID9(),
10440
+ id: bs.id || randomUUID10(),
9549
10441
  label: bs.label || `Bootstrap ${idx + 1}`,
9550
10442
  command: bs.cmd || "",
9551
10443
  ...bs.entrypoint ? { entrypoint: bs.entrypoint } : {},
@@ -9593,7 +10485,7 @@ async function runLegacyMigration(dataDir) {
9593
10485
  cwd: serviceCwd,
9594
10486
  runner: dockerConfig ? "docker" : "native",
9595
10487
  shortcuts: (legacy.shortcuts || []).filter((s) => s.spawnCmd || s.label).map((s) => ({
9596
- id: s.id || randomUUID9(),
10488
+ id: s.id || randomUUID10(),
9597
10489
  label: s.label || s.spawnCmd || "",
9598
10490
  command: [s.spawnCmd, ...s.spawnArgs || []].filter(Boolean).join(" "),
9599
10491
  ...s.spawnOptions?.cwd ? { cwd: s.spawnOptions.cwd } : {}
@@ -9633,7 +10525,7 @@ async function runLegacyMigration(dataDir) {
9633
10525
  );
9634
10526
  if (!svcScopedEnv) {
9635
10527
  svcScopedEnv = {
9636
- id: randomUUID9(),
10528
+ id: randomUUID10(),
9637
10529
  projectId: projectMonoId,
9638
10530
  name: `${service?.name || name} (${globalEnv.name})`,
9639
10531
  scope: "service",
@@ -9657,7 +10549,7 @@ async function runLegacyMigration(dataDir) {
9657
10549
  const varPost = typeof varData === "object" ? String(varData.suffix ?? "") : "";
9658
10550
  if (!varKey || existingKeys.has(cleanVarKey(varKey))) continue;
9659
10551
  const cleaned = cleanVarKey(varKey);
9660
- const varId = randomUUID9();
10552
+ const varId = randomUUID10();
9661
10553
  svcScopedEnv.variables[varId] = makeVariable(cleaned, varValue, varPre, varPost);
9662
10554
  existingKeys.add(cleaned);
9663
10555
  }
@@ -9677,7 +10569,7 @@ async function runLegacyMigration(dataDir) {
9677
10569
  );
9678
10570
  if (!svcScopedEnv) {
9679
10571
  svcScopedEnv = {
9680
- id: randomUUID9(),
10572
+ id: randomUUID10(),
9681
10573
  projectId: projectMonoId,
9682
10574
  name: `${service?.name || serviceName} (${globalEnv.name})`,
9683
10575
  scope: "service",
@@ -9698,7 +10590,7 @@ async function runLegacyMigration(dataDir) {
9698
10590
  const baseEntry = Object.entries(svcScopedEnv.variables).find(([, v]) => v.key === cleaned);
9699
10591
  let varId = baseEntry?.[0];
9700
10592
  if (!varId) {
9701
- varId = randomUUID9();
10593
+ varId = randomUUID10();
9702
10594
  svcScopedEnv.variables[varId] = {
9703
10595
  key: cleaned,
9704
10596
  value: { value: "", pre: "", post: "", isSecret: false }
@@ -9728,13 +10620,13 @@ async function runLegacyMigration(dataDir) {
9728
10620
  flatOverrides[varId] = makeVariable(cleaned, value);
9729
10621
  }
9730
10622
  } else {
9731
- flatOverrides[randomUUID9()] = makeVariable(cleaned, value);
10623
+ flatOverrides[randomUUID10()] = makeVariable(cleaned, value);
9732
10624
  }
9733
10625
  } else {
9734
10626
  const cleaned = cleanVarKey(key);
9735
10627
  const existingVarId = Object.entries(env2.variables).find(([, v]) => v.key === cleaned)?.[0];
9736
10628
  if (!existingVarId) {
9737
- const varId = randomUUID9();
10629
+ const varId = randomUUID10();
9738
10630
  env2.variables[varId] = makeVariable(cleaned, value);
9739
10631
  varIdLookup.set(`${envId}:${cleaned}`, varId);
9740
10632
  }
@@ -9763,7 +10655,7 @@ async function runLegacyMigration(dataDir) {
9763
10655
  }
9764
10656
  let existingSettings = {};
9765
10657
  try {
9766
- existingSettings = JSON.parse(await readFile11(join15(dataDir, "settings.json"), "utf-8"));
10658
+ existingSettings = JSON.parse(await readFile12(join16(dataDir, "settings.json"), "utf-8"));
9767
10659
  } catch {
9768
10660
  }
9769
10661
  const migratedParsers = legacyParsers.map((p) => ({
@@ -9784,11 +10676,11 @@ ${p.transform ?? ""}`,
9784
10676
  ...migratedParsers
9785
10677
  ]
9786
10678
  };
9787
- await atomicWrite(join15(dataDir, "projects.json"), runeProjects);
9788
- await atomicWrite(join15(dataDir, "services.json"), runeServices);
9789
- await atomicWrite(join15(dataDir, "environments.json"), runeEnvs);
9790
- await atomicWrite(join15(dataDir, "environment-overrides.json"), flatOverrides);
9791
- await atomicWrite(join15(dataDir, "settings.json"), newSettings);
10679
+ await atomicWrite(join16(dataDir, "projects.json"), runeProjects);
10680
+ await atomicWrite(join16(dataDir, "services.json"), runeServices);
10681
+ await atomicWrite(join16(dataDir, "environments.json"), runeEnvs);
10682
+ await atomicWrite(join16(dataDir, "environment-overrides.json"), flatOverrides);
10683
+ await atomicWrite(join16(dataDir, "settings.json"), newSettings);
9792
10684
  const svcEnvCount = runeEnvs.filter((e) => e.scope === "service").length;
9793
10685
  console.log(`[legacy-migration] Written: ${runeProjects.length} projects, ${runeServices.length} services, ${runeEnvs.length} environments (${svcEnvCount} service-scoped)`);
9794
10686
  console.log(`[legacy-migration] Overrides: ${Object.keys(flatOverrides).length}, custom parsers: ${migratedParsers.length} (all disabled \u2014 rewrite required)`);
@@ -9799,12 +10691,12 @@ ${p.transform ?? ""}`,
9799
10691
  }
9800
10692
  async function findLegacyKeyInBackup(dataDir) {
9801
10693
  try {
9802
- const entries = await readdir3(dataDir);
10694
+ const entries = await readdir4(dataDir);
9803
10695
  const backupDirs = entries.filter((e) => e.startsWith("backup-")).sort().reverse();
9804
10696
  for (const dir of backupDirs) {
9805
- const keyJsonPath = join15(dataDir, dir, "dbs", "encryption-key.json");
10697
+ const keyJsonPath = join16(dataDir, dir, "dbs", "encryption-key.json");
9806
10698
  try {
9807
- const raw = await readFile11(keyJsonPath, "utf-8");
10699
+ const raw = await readFile12(keyJsonPath, "utf-8");
9808
10700
  const parsed = JSON.parse(raw);
9809
10701
  if (!parsed.encryptionKey) continue;
9810
10702
  const sodium = await getSodium();
@@ -9826,14 +10718,14 @@ async function runOverrideOnlyMigration(dataDir) {
9826
10718
  const abDataDir = resolve5(dataDir);
9827
10719
  const abCwd = resolve5(process.cwd());
9828
10720
  const candidateDirs = [
9829
- join15(abCwd, ".runeya", "dbs", "overrides"),
9830
- join15(abDataDir, "dbs", "overrides")
10721
+ join16(abCwd, ".runeya", "dbs", "overrides"),
10722
+ join16(abDataDir, "dbs", "overrides")
9831
10723
  ];
9832
10724
  let overridesDir = null;
9833
10725
  let overrideFiles = [];
9834
10726
  for (const dir of candidateDirs) {
9835
10727
  try {
9836
- const files = (await readdir3(dir)).filter((f) => f.endsWith(".encrypted.json"));
10728
+ const files = (await readdir4(dir)).filter((f) => f.endsWith(".encrypted.json"));
9837
10729
  if (files.length > 0) {
9838
10730
  overridesDir = dir;
9839
10731
  overrideFiles = files;
@@ -9843,9 +10735,9 @@ async function runOverrideOnlyMigration(dataDir) {
9843
10735
  }
9844
10736
  }
9845
10737
  if (!overridesDir || overrideFiles.length === 0) return false;
9846
- const legacyKeyPath = join15(overridesDir, "..", "encryption-key.json");
10738
+ const legacyKeyPath = join16(overridesDir, "..", "encryption-key.json");
9847
10739
  try {
9848
- await access(legacyKeyPath);
10740
+ await access2(legacyKeyPath);
9849
10741
  return false;
9850
10742
  } catch {
9851
10743
  }
@@ -9861,7 +10753,7 @@ async function runOverrideOnlyMigration(dataDir) {
9861
10753
  const envNameToId = {};
9862
10754
  let envsRaw;
9863
10755
  try {
9864
- envsRaw = JSON.parse(await readFile11(join15(abDataDir, "environments.json"), "utf-8"));
10756
+ envsRaw = JSON.parse(await readFile12(join16(abDataDir, "environments.json"), "utf-8"));
9865
10757
  for (const _env of envsRaw) {
9866
10758
  const env2 = _env;
9867
10759
  if (env2["name"] && env2["id"]) {
@@ -9880,7 +10772,7 @@ async function runOverrideOnlyMigration(dataDir) {
9880
10772
  let servicesRaw = [];
9881
10773
  const serviceNameToId = {};
9882
10774
  try {
9883
- servicesRaw = JSON.parse(await readFile11(join15(abDataDir, "services.json"), "utf-8"));
10775
+ servicesRaw = JSON.parse(await readFile12(join16(abDataDir, "services.json"), "utf-8"));
9884
10776
  for (const _svc of servicesRaw) {
9885
10777
  const svc = _svc;
9886
10778
  if (svc["name"] && svc["id"]) serviceNameToId[svc["name"]] = svc["id"];
@@ -9897,7 +10789,7 @@ async function runOverrideOnlyMigration(dataDir) {
9897
10789
  const envName = baseName.slice(0, -"-environment".length);
9898
10790
  let data;
9899
10791
  try {
9900
- data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
10792
+ data = await decryptLegacyFile(join16(overridesDir, file), sodiumKey, sodium);
9901
10793
  } catch (err) {
9902
10794
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9903
10795
  continue;
@@ -9919,12 +10811,12 @@ async function runOverrideOnlyMigration(dataDir) {
9919
10811
  if (isSameAsBase(value, baseVar)) continue;
9920
10812
  flatOverrides[varId] = makeVariable(cleaned, value);
9921
10813
  } else {
9922
- flatOverrides[randomUUID9()] = makeVariable(cleaned, value);
10814
+ flatOverrides[randomUUID10()] = makeVariable(cleaned, value);
9923
10815
  }
9924
10816
  }
9925
10817
  }
9926
10818
  if (Object.keys(flatOverrides).length > 0) {
9927
- await atomicWrite(join15(abDataDir, "environment-overrides.json"), flatOverrides);
10819
+ await atomicWrite(join16(abDataDir, "environment-overrides.json"), flatOverrides);
9928
10820
  console.log(`[override-migration] Written ${Object.keys(flatOverrides).length} override(s) to environment-overrides.json`);
9929
10821
  }
9930
10822
  let envsModified = false;
@@ -9934,7 +10826,7 @@ async function runOverrideOnlyMigration(dataDir) {
9934
10826
  const serviceName = baseName.slice(0, -"-envs".length);
9935
10827
  let data;
9936
10828
  try {
9937
- data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
10829
+ data = await decryptLegacyFile(join16(overridesDir, file), sodiumKey, sodium);
9938
10830
  } catch (err) {
9939
10831
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9940
10832
  continue;
@@ -9956,7 +10848,7 @@ async function runOverrideOnlyMigration(dataDir) {
9956
10848
  );
9957
10849
  if (!svcScopedEnv) {
9958
10850
  svcScopedEnv = {
9959
- id: randomUUID9(),
10851
+ id: randomUUID10(),
9960
10852
  projectId: globalEnv["projectId"],
9961
10853
  name: `${service?.["name"] || serviceName} (${globalEnv["name"]})`,
9962
10854
  scope: "service",
@@ -9978,7 +10870,7 @@ async function runOverrideOnlyMigration(dataDir) {
9978
10870
  const baseEntry = Object.entries(svcScopedEnv["variables"]).find(([, v]) => v.key === cleaned);
9979
10871
  let varId = baseEntry?.[0];
9980
10872
  if (!varId) {
9981
- varId = randomUUID9();
10873
+ varId = randomUUID10();
9982
10874
  svcScopedEnv["variables"][varId] = {
9983
10875
  key: cleaned,
9984
10876
  value: { value: "", pre: "", post: "", isSecret: false }
@@ -9993,7 +10885,7 @@ async function runOverrideOnlyMigration(dataDir) {
9993
10885
  }
9994
10886
  }
9995
10887
  if (envsModified) {
9996
- await atomicWrite(join15(abDataDir, "environments.json"), envsRaw);
10888
+ await atomicWrite(join16(abDataDir, "environments.json"), envsRaw);
9997
10889
  console.log("[override-migration] Merged service overrides into environments.json");
9998
10890
  }
9999
10891
  const migratedDir = overridesDir.replace(/overrides$/, "overrides-migrated");
@@ -10017,7 +10909,7 @@ var ARGON2_OPTIONS = { memoryCost: 65536, timeCost: 3, parallelism: 4, outputLen
10017
10909
  async function readKeyFile(filePath) {
10018
10910
  const s = await stat4(filePath);
10019
10911
  if (s.size > MAX_KEY_FILE_SIZE) throw new Error(`Fichier suspect : ${filePath} (${s.size} octets)`);
10020
- return readFile12(filePath);
10912
+ return readFile13(filePath);
10021
10913
  }
10022
10914
  function aesEncrypt(key, plaintext) {
10023
10915
  const iv = randomBytes4(12);
@@ -10036,7 +10928,7 @@ function aesDecrypt(key, data) {
10036
10928
  }
10037
10929
  async function getPepperFromFile(dataDir) {
10038
10930
  try {
10039
- const raw = await readKeyFile(join16(dataDir, PEPPER_FILENAME));
10931
+ const raw = await readKeyFile(join17(dataDir, PEPPER_FILENAME));
10040
10932
  const hex = raw.toString("utf-8").trim();
10041
10933
  if (/^[0-9a-f]{64}$/i.test(hex)) return Buffer.from(hex, "hex");
10042
10934
  } catch {
@@ -10044,7 +10936,7 @@ async function getPepperFromFile(dataDir) {
10044
10936
  return null;
10045
10937
  }
10046
10938
  async function writePepperToFile(dataDir, pepper) {
10047
- const filePath = join16(dataDir, PEPPER_FILENAME);
10939
+ const filePath = join17(dataDir, PEPPER_FILENAME);
10048
10940
  const tmpPath = filePath + ".tmp";
10049
10941
  await writeFile12(tmpPath, pepper.toString("hex"), "utf-8");
10050
10942
  await chmod9(tmpPath, 384);
@@ -10095,14 +10987,14 @@ async function writeEncryptedKey(dataDir, masterPassword, encryptionKey) {
10095
10987
  const payload = aesEncrypt(derivedKey, encryptionKey);
10096
10988
  const fileContent = Buffer.concat([salt, payload]);
10097
10989
  const encoded = fileContent.toString("base64");
10098
- const filePath = join16(dataDir, ENCRYPTED_KEY_FILENAME);
10990
+ const filePath = join17(dataDir, ENCRYPTED_KEY_FILENAME);
10099
10991
  const tmpPath = filePath + ".tmp";
10100
10992
  await writeFile12(tmpPath, encoded, "utf-8");
10101
10993
  await chmod9(tmpPath, 384);
10102
10994
  await rename10(tmpPath, filePath);
10103
10995
  }
10104
10996
  async function readEncryptedKey(dataDir, masterPassword) {
10105
- const filePath = join16(dataDir, ENCRYPTED_KEY_FILENAME);
10997
+ const filePath = join17(dataDir, ENCRYPTED_KEY_FILENAME);
10106
10998
  const raw = await readKeyFile(filePath);
10107
10999
  const fileContent = Buffer.from(raw.toString("utf-8").trim(), "base64");
10108
11000
  const salt = fileContent.subarray(0, 16);
@@ -10122,13 +11014,13 @@ async function getPepperFromKeychain(dataDir) {
10122
11014
  }
10123
11015
  async function writeVerifyFile(dataDir, encryptionKey) {
10124
11016
  const payload = aesEncrypt(encryptionKey, Buffer.from(CANARY_PLAINTEXT, "utf-8"));
10125
- const filePath = join16(dataDir, VERIFY_FILENAME);
11017
+ const filePath = join17(dataDir, VERIFY_FILENAME);
10126
11018
  const tmpPath = filePath + ".tmp";
10127
11019
  await writeFile12(tmpPath, payload.toString("base64"), "utf-8");
10128
11020
  await rename10(tmpPath, filePath);
10129
11021
  }
10130
11022
  async function verifyEncryptionKey(dataDir, encryptionKey) {
10131
- const filePath = join16(dataDir, VERIFY_FILENAME);
11023
+ const filePath = join17(dataDir, VERIFY_FILENAME);
10132
11024
  const raw = await readKeyFile(filePath);
10133
11025
  const payload = Buffer.from(raw.toString("utf-8").trim(), "base64");
10134
11026
  const plaintext = aesDecrypt(encryptionKey, payload);
@@ -10138,7 +11030,7 @@ async function verifyEncryptionKey(dataDir, encryptionKey) {
10138
11030
  }
10139
11031
  async function hasVerifyFile(dataDir) {
10140
11032
  try {
10141
- await stat4(join16(dataDir, VERIFY_FILENAME));
11033
+ await stat4(join17(dataDir, VERIFY_FILENAME));
10142
11034
  return true;
10143
11035
  } catch {
10144
11036
  return false;
@@ -10146,7 +11038,7 @@ async function hasVerifyFile(dataDir) {
10146
11038
  }
10147
11039
  async function hasEncryptedKeyFile(dataDir) {
10148
11040
  try {
10149
- await stat4(join16(dataDir, ENCRYPTED_KEY_FILENAME));
11041
+ await stat4(join17(dataDir, ENCRYPTED_KEY_FILENAME));
10150
11042
  return true;
10151
11043
  } catch {
10152
11044
  return false;
@@ -10160,17 +11052,17 @@ async function getServerState() {
10160
11052
  let hasVerify = false;
10161
11053
  let hasData = false;
10162
11054
  try {
10163
- await stat5(join17(dataDir, ENCRYPTED_KEY_FILENAME2));
11055
+ await stat5(join18(dataDir, ENCRYPTED_KEY_FILENAME2));
10164
11056
  hasEncFile = true;
10165
11057
  } catch {
10166
11058
  }
10167
11059
  try {
10168
- await stat5(join17(dataDir, VERIFY_FILENAME2));
11060
+ await stat5(join18(dataDir, VERIFY_FILENAME2));
10169
11061
  hasVerify = true;
10170
11062
  } catch {
10171
11063
  }
10172
11064
  try {
10173
- await stat5(join17(dataDir, "projects.json"));
11065
+ await stat5(join18(dataDir, "projects.json"));
10174
11066
  hasData = true;
10175
11067
  } catch {
10176
11068
  }
@@ -10613,6 +11505,9 @@ async function createLocalServer() {
10613
11505
  }
10614
11506
  await agentManager.init();
10615
11507
  await agentSpawner.spawnLocalAgent();
11508
+ startClaudeTranscriptWatcher().catch((err) => {
11509
+ console.warn("[server] Failed to start Claude transcript watcher:", err);
11510
+ });
10616
11511
  }
10617
11512
  if (hasEncryptionKey()) {
10618
11513
  await runPostUnlock();
@@ -11020,6 +11915,11 @@ async function pullEnv(opts) {
11020
11915
  return Object.entries(env2).map(([key, value]) => `${key}=${value}`).join("\n");
11021
11916
  }
11022
11917
  dotenv.config({ quiet: true });
11918
+ if (process.env["DATA_DIR"]) {
11919
+ const resolved = resolvePath(process.env["DATA_DIR"], "..");
11920
+ process.chdir(resolved);
11921
+ console.log(`[server] Working directory: ${resolved} (from DATA_DIR)`);
11922
+ }
11023
11923
  function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500) {
11024
11924
  return new Promise((resolve5, reject) => {
11025
11925
  let attempts = 0;
@@ -11089,4 +11989,4 @@ export {
11089
11989
  createLocalServer,
11090
11990
  pullEnv
11091
11991
  };
11092
- //# sourceMappingURL=dist-HZBWBT6S.js.map
11992
+ //# sourceMappingURL=dist-HGCFHXO4.js.map