@runeya/runeya 2.0.9 → 2.0.11

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 (318) hide show
  1. package/agent/index.js +513 -52
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-V47DLMYP-26VYXTSG.js +8 -0
  4. package/{chunk-BSPKMRKW.js → chunk-6547GLUJ.js} +49 -16
  5. package/chunk-6547GLUJ.js.map +1 -0
  6. package/{dist-PSPHMTZN.js → dist-2H7XZQCV.js} +1255 -378
  7. package/dist-2H7XZQCV.js.map +1 -0
  8. package/index.js +6 -1
  9. package/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/web/assets/AddressBar.vue-BxQkuTJ-.js +1 -0
  12. package/web/assets/AgentScalarChatInterface.vue-BZyh4z8j.js +13 -0
  13. package/web/assets/AgentTerminalPage-SWnqcTm-.js +1 -0
  14. package/web/assets/AgentTerminalPage-anYDkj7H.css +1 -0
  15. package/web/assets/AiChatPage-PJvieNVr.js +1 -0
  16. package/web/assets/AppLayout-CDtyYNWp.css +1 -0
  17. package/web/assets/AppLayout-qD2ReaTx.js +1 -0
  18. package/web/assets/AuditPage-DCvW5-HA.js +1 -0
  19. package/web/assets/CodeInput.vue-DDs5Eel7.js +1 -0
  20. package/web/assets/Collection.vue-HH2Z976J.js +1 -0
  21. package/web/assets/CollectionAuthentication.vue-BbsKOTJ9.js +1 -0
  22. package/web/assets/CollectionCookies.vue-Dzlq0l8S.js +1 -0
  23. package/web/assets/CollectionEnvironment.vue-BS9B1OU-.js +1 -0
  24. package/web/assets/CollectionOverview.vue-DXXQHn9J.js +1 -0
  25. package/web/assets/CollectionScripts.vue-TVNOxHuu.js +1 -0
  26. package/web/assets/CollectionServers.vue-DDyH4n80.js +1 -0
  27. package/web/assets/CollectionSettings.vue-BD5QvG5c.js +1 -0
  28. package/web/assets/CollectionSync.vue-g_NhkdXC.js +1 -0
  29. package/web/assets/CommandActionInput.vue-Cot3fJDd.js +1 -0
  30. package/web/assets/Cookies.vue-X8bc_7ud.js +1 -0
  31. package/web/assets/DashboardPage-BuP2oOo3.js +1 -0
  32. package/web/assets/DataTable.vue-DmcNIRcD.js +1 -0
  33. package/web/assets/DataTableCheckbox.vue-B7bvOQ_f.js +1 -0
  34. package/web/assets/{DataTableHeader.vue-ByesjY2f.js → DataTableHeader.vue-D5k0UCfl.js} +1 -1
  35. package/web/assets/DataTableInput.vue-DVUCY60y.js +1 -0
  36. package/web/assets/DeleteSidebarListElement.vue-CAri6AWj.js +1 -0
  37. package/web/assets/EditSidebarListElement.vue-DFY1ZZNE.js +1 -0
  38. package/web/assets/{EditorIcon-B70ii4EM.js → EditorIcon-DOV6wP8X.js} +2 -2
  39. package/web/assets/{EmptyState.vue-CcnWPtfW.js → EmptyState.vue-watFzTsD.js} +2 -2
  40. package/web/assets/Environment.vue-BbpisYLs.js +1 -0
  41. package/web/assets/EnvironmentForm-CyJtNH2k.js +108 -0
  42. package/web/assets/EnvironmentFormPage-Du4EXZlm.js +1 -0
  43. package/web/assets/EnvironmentListPage-1Jdh0AWO.js +1 -0
  44. package/web/assets/EnvironmentModal.vue-DaI310h8.js +1 -0
  45. package/web/assets/EnvironmentSelector-BPhBcKYj.js +1 -0
  46. package/web/assets/Form.vue-CMWUiD6f.js +1 -0
  47. package/web/assets/IconSelector.vue-90PWhHNq.js +1 -0
  48. package/web/assets/KanbanBoardPage-DzFRgXlJ.js +141 -0
  49. package/web/assets/KanbanListPage-O3T-L6z3.js +1 -0
  50. package/web/assets/LaunchProjectPage-B2m_PflY.js +1 -0
  51. package/web/assets/LaunchServicesPage-DgfE9971.js +1 -0
  52. package/web/assets/LogViewer-CXVXsymr.js +3 -0
  53. package/web/assets/ProcessControls-DVQwL-qx.js +1 -0
  54. package/web/assets/ProcessDetailPage-8GtSwfXG.js +1 -0
  55. package/web/assets/ProcessDetailPage-CzGUsQM9.css +1 -0
  56. package/web/assets/ProcessMetrics-9RjmvDOk.js +51 -0
  57. package/web/assets/ProjectDetailPage-B3s8bbmw.js +1 -0
  58. package/web/assets/ProjectListPage-HQ9gZWHw.js +1 -0
  59. package/web/assets/RandomJobTestPage-DAM5nNLf.js +2 -0
  60. package/web/assets/Request.vue-DiF4lAAD.js +1 -0
  61. package/web/assets/RequestAuth.vue-DHU9RNUf.js +1 -0
  62. package/web/assets/RequestRoot.vue-BiM2Tr20.js +19 -0
  63. package/web/assets/ScalarAsciiArt.vue-2vz_9aEa.js +2 -0
  64. package/web/assets/{ScalarIconEyeSlash.vue-DWKhR_BX.js → ScalarIconEyeSlash.vue-CwJnZ-zD.js} +1 -1
  65. package/web/assets/{ScalarIconTrash.vue-CtSwrO5S.js → ScalarIconTrash.vue-DAEOI9Kk.js} +1 -1
  66. package/web/assets/ScenarioEditorPage-BzPL_7Up.js +14 -0
  67. package/web/assets/ServerVariablesForm.vue-Dj2R8IEV.js +1 -0
  68. package/web/assets/ServiceControlsBar-CsHZTuPK.js +1 -0
  69. package/web/assets/ServiceDetailPage-Csf55wQD.js +2 -0
  70. package/web/assets/ServiceDetailPage-znbWajpt.css +1 -0
  71. package/web/assets/ServiceFormPage-DDJYv2z3.js +1 -0
  72. package/web/assets/Settings.vue-Dyzw_xwE.js +1 -0
  73. package/web/assets/SettingsPage-CYQGpGyw.js +225 -0
  74. package/web/assets/SettingsPage-CgJN_IL7.css +1 -0
  75. package/web/assets/SetupKeyPage-CnaFhUR6.js +1 -0
  76. package/web/assets/SetupPage-sqBZmTQ9.js +1 -0
  77. package/web/assets/Sidebar.vue-DqiliUT_.js +1 -0
  78. package/web/assets/SidebarButton.vue-MCBoC3jO.js +1 -0
  79. package/web/assets/SidebarListElement.vue-DswWWg_S.js +1 -0
  80. package/web/assets/UnlockPage-rDy89SdE.js +1 -0
  81. package/web/assets/VariablesTable-CEaksHyg.js +1 -0
  82. package/web/assets/{VariablesTable-BtdK5olT.css → VariablesTable-W4rxlL35.css} +1 -1
  83. package/web/assets/ViewLayout.vue-CgpeQh3K.js +1 -0
  84. package/web/assets/ViewLayoutContent.vue-DP_GK_Ip.js +1 -0
  85. package/web/assets/ViewLayoutSection.vue-YQYzkcAB.js +1 -0
  86. package/web/assets/active-entities-CWFFm964.js +1 -0
  87. package/web/assets/api-reference-UJl6P2J4.js +1 -0
  88. package/web/assets/autocomplete-CK9Z1-Wz.js +422 -0
  89. package/web/assets/card-DadsU5ZI.js +32 -0
  90. package/web/assets/{checkbox-C0sXsL9e.js → checkbox-4z5ftHhs.js} +3 -3
  91. package/web/assets/{chip-rnk9_cKV.js → chip-KJbF4Jjk.js} +2 -2
  92. package/web/assets/column-Cp1kVQOt.js +763 -0
  93. package/web/assets/core-7y546zlP.js +1 -0
  94. package/web/assets/{cssMode-DdlczEni.js → cssMode-CIUATcSv.js} +1 -1
  95. package/web/assets/dist-BLSATgV0.js +1 -0
  96. package/web/assets/{dist-DBBhfLmf.js → dist-Cp1hgSqY.js} +3 -3
  97. package/web/assets/{dist-DeOvPbza.js → dist-CpE_ZYIQ.js} +1 -1
  98. package/web/assets/dist-DR4LqG1l.js +192 -0
  99. package/web/assets/{divider-D6L7p-FU.js → divider-D-vBLTer.js} +2 -2
  100. package/web/assets/editor.store-COpS72tz.js +1 -0
  101. package/web/assets/{fieldset-CiwzR0un.js → fieldset-CSlJxjpZ.js} +2 -2
  102. package/web/assets/{freemarker2-_wLEdPSz.js → freemarker2-Dsxb69Av.js} +1 -1
  103. package/web/assets/{handlebars-66zKrSAk.js → handlebars-ChbvHuc-.js} +1 -1
  104. package/web/assets/{html-oM24gnvF.js → html-DOIF2Zpc.js} +1 -1
  105. package/web/assets/{htmlMode-D65_fKu-.js → htmlMode-DKICvQwb.js} +1 -1
  106. package/web/assets/index-A6eiSk8H.css +1 -0
  107. package/web/assets/{index-D5xJxOo2.js → index-C0UzL9Y-.js} +269 -234
  108. package/web/assets/{javascript-D-njx6Yh.js → javascript-Bmt68E8X.js} +1 -1
  109. package/web/assets/{jsonMode-bqHogKpM.js → jsonMode-CYcvn9c0.js} +1 -1
  110. package/web/assets/{keyboard-BzTLpobK.js → keyboard-DZklF_AY.js} +2 -2
  111. package/web/assets/{lib-CPo2W-Kx.js → lib-B5TdN_ca.js} +4 -4
  112. package/web/assets/{library-DmdL5Pgv.js → library-DtrGq8p8.js} +1 -1
  113. package/web/assets/{liquid-BDL70fhI.js → liquid-3ot_oRqd.js} +1 -1
  114. package/web/assets/{mdx-Bo_N_Jd-.js → mdx-Cm1rlDNM.js} +1 -1
  115. package/web/assets/{mediaTypes-Cnm-r_sv.js → mediaTypes-Bz4C7Sle.js} +2 -2
  116. package/web/assets/{message-gtjFpAbc.js → message-DFReN7Fq.js} +2 -2
  117. package/web/assets/{minus-C5Lsl7Wp.js → minus-t_EQEzUj.js} +2 -2
  118. package/web/assets/{multiselect-BlmCsiSW.js → multiselect-BZbgA1pl.js} +3 -3
  119. package/web/assets/{password-RYOg4soC.js → password-D82un95s.js} +4 -4
  120. package/web/assets/{plus-C52I3-BN.js → plus-B-AhHpHU.js} +6 -6
  121. package/web/assets/pretty-ms-XBeIOdhN.js +202 -0
  122. package/web/assets/process.store-jv0SI_6q.js +1 -0
  123. package/web/assets/project.store-DHXLE6Lv.js +1 -0
  124. package/web/assets/{python-D1UXSNgK.js → python-BuQCsdKw.js} +1 -1
  125. package/web/assets/{radiobutton-B0t-WlKP.js → radiobutton-Btattklk.js} +2 -2
  126. package/web/assets/{razor-Bd4GOtMe.js → razor-DgnEPQLs.js} +1 -1
  127. package/web/assets/router-w5lyb-nn.js +1 -0
  128. package/web/assets/settings.store-bNnLJ43c.js +1 -0
  129. package/web/assets/store-DWOu5TTl.js +1840 -0
  130. package/web/assets/{tabpanel-geoVG-sX.js → tabpanel-CIyzJ44J.js} +3 -3
  131. package/web/assets/{tag-Dv5CaUPa.js → tag-DV-szbCr.js} +2 -2
  132. package/web/assets/trpc-wp_QsEqG.js +1 -0
  133. package/web/assets/{tsMode-BkM4CkKr.js → tsMode-ChD0IpIg.js} +1 -1
  134. package/web/assets/{typescript-CUKl0qxN.js → typescript-BRYpIqKe.js} +1 -1
  135. package/web/assets/urls-CoBMqtDH.js +12 -0
  136. package/web/assets/{use-tree-walker-E64HILpa.js → use-tree-walker-M4-Gn01v.js} +1 -1
  137. package/web/assets/useAiChatDrawer-BhlTNoXz.js +1 -0
  138. package/web/assets/{useClipboard-Bep2crMO.js → useClipboard-Cro2wlg3.js} +1 -1
  139. package/web/assets/{useLayout-F9XKAPPL.js → useLayout-BciG4r-1.js} +1 -1
  140. package/web/assets/{usePluginManager-yL67GqCi.js → usePluginManager-BonTfGSN.js} +1 -1
  141. package/web/assets/useSidebar-77_u4hef.js +1 -0
  142. package/web/assets/{vue-router-uE6ClrN-.js → vue-router-DdBypSuY.js} +1 -1
  143. package/web/assets/{xml-DMOW2yTg.js → xml-Cz29Bxul.js} +1 -1
  144. package/web/assets/{yaml-D2q3K0ZL.js → yaml-CiZl2tNW.js} +1 -1
  145. package/web/index.html +2 -2
  146. package/agent-manager-UP3PJ35X-LO6B3XKF.js +0 -8
  147. package/chunk-BSPKMRKW.js.map +0 -1
  148. package/dist-PSPHMTZN.js.map +0 -1
  149. package/web/assets/AddressBar.vue-BOHUxRhN.js +0 -1
  150. package/web/assets/AgentPage-BTWkJvqr.css +0 -1
  151. package/web/assets/AgentPage-DFHxsu-9.js +0 -2
  152. package/web/assets/AgentScalarChatInterface.vue-Cxxiyyj8.js +0 -13
  153. package/web/assets/AgentTerminalPage-D-T5emEl.js +0 -36
  154. package/web/assets/AgentTerminalPage-EX_n6P3x.css +0 -1
  155. package/web/assets/AiChatPage-C81mFvv5.js +0 -1
  156. package/web/assets/ApiDocsPage-CMGvcGEJ.js +0 -5
  157. package/web/assets/ApiDocsPage-D2Z0wqGZ.css +0 -1
  158. package/web/assets/AppLayout-D10vX1fo.js +0 -1
  159. package/web/assets/AppLayout-sngalCCj.css +0 -1
  160. package/web/assets/AuditPage-DZiwogoY.js +0 -1
  161. package/web/assets/CodeInput.vue-8aNMYCYM.js +0 -1
  162. package/web/assets/Collection.vue-N8zZa4pa.js +0 -1
  163. package/web/assets/CollectionAuthentication.vue-cJOxjafo.js +0 -1
  164. package/web/assets/CollectionCookies.vue-DrFQqbwv.js +0 -1
  165. package/web/assets/CollectionEnvironment.vue-DWPTZ8qx.js +0 -1
  166. package/web/assets/CollectionOverview.vue-1Qf130d5.js +0 -1
  167. package/web/assets/CollectionScripts.vue-DhcFFz80.js +0 -1
  168. package/web/assets/CollectionServers.vue-h7E03W0v.js +0 -1
  169. package/web/assets/CollectionSettings.vue-CFrOwl7U.js +0 -1
  170. package/web/assets/CollectionSync.vue-PV_tIrJS.js +0 -1
  171. package/web/assets/CommandActionInput.vue-L7AcEf6T.js +0 -1
  172. package/web/assets/Cookies.vue-_P19OjMm.js +0 -1
  173. package/web/assets/DashboardPage-BUDWnuJT.js +0 -1
  174. package/web/assets/DataTable.vue-DYVeGQPP.js +0 -1
  175. package/web/assets/DataTableCheckbox.vue-n2xCTJj1.js +0 -1
  176. package/web/assets/DataTableInput.vue-uPpuKzrX.js +0 -1
  177. package/web/assets/DeleteSidebarListElement.vue-CBIb58rU.js +0 -1
  178. package/web/assets/EditSidebarListElement.vue-DqTVSZtI.js +0 -1
  179. package/web/assets/Environment.vue-Cgx06nkH.js +0 -1
  180. package/web/assets/EnvironmentForm-iBZzqZKd.js +0 -108
  181. package/web/assets/EnvironmentFormPage-YVxAAreT.js +0 -1
  182. package/web/assets/EnvironmentListPage-CHYDJ_c-.js +0 -1
  183. package/web/assets/EnvironmentModal.vue-BJNYZuPC.js +0 -1
  184. package/web/assets/EnvironmentSelector-BZn_taec.js +0 -1
  185. package/web/assets/Form.vue-NLXFw40d.js +0 -1
  186. package/web/assets/IconSelector.vue-Ykh6DAln.js +0 -1
  187. package/web/assets/KanbanBoardPage-gtXv0ZhK.js +0 -141
  188. package/web/assets/KanbanListPage-DMNSYO_o.js +0 -1
  189. package/web/assets/LaunchProjectPage-DSyBuhky.js +0 -1
  190. package/web/assets/LaunchServicesPage-CiGkiqwE.js +0 -1
  191. package/web/assets/LogViewer-DBVkaZnm.js +0 -3
  192. package/web/assets/ProcessControls-Cya4eb3d.js +0 -1
  193. package/web/assets/ProcessDetailPage-8ypC6zkc.js +0 -1
  194. package/web/assets/ProcessDetailPage-B0qGKtvQ.css +0 -1
  195. package/web/assets/ProcessMetrics-CxRgbaUS.js +0 -1
  196. package/web/assets/ProjectDetailPage-a1lTAjKP.js +0 -1
  197. package/web/assets/ProjectListPage-Dewqu8Dm.js +0 -1
  198. package/web/assets/RandomJobTestPage-E6mevkqw.js +0 -2
  199. package/web/assets/Request.vue-DQPTi6ur.js +0 -1
  200. package/web/assets/RequestAuth.vue-DV5nNO3f.js +0 -1
  201. package/web/assets/RequestRoot.vue-DKlASKU5.js +0 -19
  202. package/web/assets/ScalarAsciiArt.vue-mk9jZSKh.js +0 -2
  203. package/web/assets/ScenarioEditorPage-DogHTbNU.js +0 -14
  204. package/web/assets/ServerVariablesForm.vue-DYmWiAuz.js +0 -1
  205. package/web/assets/ServiceControlsBar-B8Sm6p9E.js +0 -1
  206. package/web/assets/ServiceDetailPage-BriHTCpT.css +0 -1
  207. package/web/assets/ServiceDetailPage-DpWLccOg.js +0 -2
  208. package/web/assets/ServiceFormPage-DQbOgtB3.js +0 -1
  209. package/web/assets/Settings.vue-buoYkM3l.js +0 -1
  210. package/web/assets/SettingsPage-CCjZq5jP.js +0 -508
  211. package/web/assets/SettingsPage-vGhb_tlY.css +0 -1
  212. package/web/assets/SetupKeyPage-BdSpJPDR.js +0 -1
  213. package/web/assets/SetupPage-BsWaVllB.js +0 -1
  214. package/web/assets/Sidebar.vue-DntMEn5m.js +0 -1
  215. package/web/assets/SidebarButton.vue-DTin2zI2.js +0 -1
  216. package/web/assets/SidebarListElement.vue-zlfRLuE5.js +0 -1
  217. package/web/assets/UnlockPage-CmwRV1XA.js +0 -1
  218. package/web/assets/VariablesTable-C8lxLV1V.js +0 -1
  219. package/web/assets/ViewLayout.vue-C7w_U_o6.js +0 -1
  220. package/web/assets/ViewLayoutContent.vue-CAXM5eIo.js +0 -1
  221. package/web/assets/ViewLayoutSection.vue-BzJIsja7.js +0 -1
  222. package/web/assets/active-entities-C2TbBQeI.js +0 -1
  223. package/web/assets/api-reference-BJkGMKQj.js +0 -1
  224. package/web/assets/autocomplete-CxPmwkcZ.js +0 -308
  225. package/web/assets/card-D5_gGGD-.js +0 -32
  226. package/web/assets/column-ChvIrUdH.js +0 -763
  227. package/web/assets/core-wYvkV3-A.js +0 -1
  228. package/web/assets/dist-BHh6fQzR.js +0 -1
  229. package/web/assets/dist-ZJ37S89a.js +0 -192
  230. package/web/assets/editor.store-CWtgIxvE.js +0 -1
  231. package/web/assets/index-Pqvy10Qz.css +0 -1
  232. package/web/assets/pretty-ms-BBvjfSt1.js +0 -202
  233. package/web/assets/process.store-3BOses3z.js +0 -1
  234. package/web/assets/project.store-emr8mjg8.js +0 -1
  235. package/web/assets/router-DHhyM3YQ.js +0 -1
  236. package/web/assets/settings.store-CDr0Mia7.js +0 -1
  237. package/web/assets/store-CZKREOuj.js +0 -1840
  238. package/web/assets/toggleswitch-DNzwv4Lc.js +0 -115
  239. package/web/assets/trpc-B-vyi6vd.js +0 -1
  240. package/web/assets/urls-BqL-MrG5.js +0 -12
  241. package/web/assets/useAiChatDrawer-uS6dsG6x.js +0 -1
  242. package/web/assets/useSidebar-DVRRagIa.js +0 -1
  243. /package/{agent-manager-UP3PJ35X-LO6B3XKF.js.map → agent-manager-V47DLMYP-26VYXTSG.js.map} +0 -0
  244. /package/web/assets/{_plugin-vue_export-helper-C7ztIXtE.js → _plugin-vue_export-helper-BVSTY9ge.js} +0 -0
  245. /package/web/assets/{abap-C4tHUUCU.js → abap-BF8nUS5p.js} +0 -0
  246. /package/web/assets/{apex-6cG6aG-w.js → apex-Bbj_Fak2.js} +0 -0
  247. /package/web/assets/{azcli-51BngrSU.js → azcli-B8w3JS7J.js} +0 -0
  248. /package/web/assets/{base64-uZi92-DX.js → base64-CG47Jc3Y.js} +0 -0
  249. /package/web/assets/{bat-BDcdVSIt.js → bat-CaNuqacB.js} +0 -0
  250. /package/web/assets/{bicep-Cg0BCcky.js → bicep-DAvnLugW.js} +0 -0
  251. /package/web/assets/{cameligo-C3ekVui9.js → cameligo-BlI8LCmP.js} +0 -0
  252. /package/web/assets/{clojure-Ci5xNgh_.js → clojure-CxxhrBQl.js} +0 -0
  253. /package/web/assets/{coffee-xsRz4GKy.js → coffee-SkbnTBhO.js} +0 -0
  254. /package/web/assets/{cpp-B2gW4e23.js → cpp-Dm2HJxj0.js} +0 -0
  255. /package/web/assets/{csharp-SRujvHqy.js → csharp-BOGHPGcq.js} +0 -0
  256. /package/web/assets/{csp-Cr-ll6gP.js → csp-CMA_7tOs.js} +0 -0
  257. /package/web/assets/{css-BYTJZCno.js → css-DU8LSGcn.js} +0 -0
  258. /package/web/assets/{cypher-AmQ0R_D3.js → cypher-BZPsEDmT.js} +0 -0
  259. /package/web/assets/{dart-Du01a0J5.js → dart-C1tIJhFk.js} +0 -0
  260. /package/web/assets/{dist-DkaYBczY.js → dist-BPIMeATj.js} +0 -0
  261. /package/web/assets/{dockerfile-Dtlk9wqf.js → dockerfile-z4dmGi5q.js} +0 -0
  262. /package/web/assets/{ecl-CpmVnLkK.js → ecl-Rv9JwhOI.js} +0 -0
  263. /package/web/assets/{elixir-B3MVAC8k.js → elixir-DL6xFSed.js} +0 -0
  264. /package/web/assets/{flow9-Chkly0RN.js → flow9-DFZ52-2d.js} +0 -0
  265. /package/web/assets/{fsharp-CZvMkppk.js → fsharp-RvVOB9Dm.js} +0 -0
  266. /package/web/assets/{go-dT2iT7lt.js → go-B2USMDMv.js} +0 -0
  267. /package/web/assets/{graphql-DmObWyBD.js → graphql-CsHpCYeJ.js} +0 -0
  268. /package/web/assets/{hcl-D1x5Oyp4.js → hcl-CK021J-J.js} +0 -0
  269. /package/web/assets/{ini-Bg2o1S3s.js → ini-v1EoSUxR.js} +0 -0
  270. /package/web/assets/{java-XviKSSn5.js → java-CUXpSPxY.js} +0 -0
  271. /package/web/assets/{julia-DfTIwUGh.js → julia-DwKveiZg.js} +0 -0
  272. /package/web/assets/{kotlin-CNaIBh9d.js → kotlin-SsG_4ZBt.js} +0 -0
  273. /package/web/assets/{less-D7o76tXd.js → less-BviquM8A.js} +0 -0
  274. /package/web/assets/{lexon-Dd374QjV.js → lexon-C8G_qWst.js} +0 -0
  275. /package/web/assets/{lua-DZKuiFbY.js → lua-xqYje_o5.js} +0 -0
  276. /package/web/assets/{m3-CbwRx9Bb.js → m3-B5PetinG.js} +0 -0
  277. /package/web/assets/{markdown-DwLB5Rib.js → markdown-BksexNux.js} +0 -0
  278. /package/web/assets/{mips-DLbIq1yM.js → mips-DKSTezUQ.js} +0 -0
  279. /package/web/assets/{msdax-D4IjoZ6m.js → msdax-SeFNE86e.js} +0 -0
  280. /package/web/assets/{mysql-DQL_jy6H.js → mysql-5TQWJDdB.js} +0 -0
  281. /package/web/assets/{objective-c-D74wM5bv.js → objective-c-DeaYl08g.js} +0 -0
  282. /package/web/assets/{pascal-5tRlZk4O.js → pascal-CGBtIfZg.js} +0 -0
  283. /package/web/assets/{pascaligo-CAE4rAEt.js → pascaligo-B85wuk0w.js} +0 -0
  284. /package/web/assets/{perl-DREAbuQT.js → perl-Bl61zsEQ.js} +0 -0
  285. /package/web/assets/{pgsql-DASKdyAW.js → pgsql-KgXv5Cq7.js} +0 -0
  286. /package/web/assets/{php-C37ZWqoO.js → php-KRRf0kxu.js} +0 -0
  287. /package/web/assets/{pla-v5u9R8i1.js → pla-CFpO6lSz.js} +0 -0
  288. /package/web/assets/{postiats-Szt5m-7G.js → postiats-B0jQ-DXO.js} +0 -0
  289. /package/web/assets/{powerquery-BzVrUSu3.js → powerquery-T9Ge5xUx.js} +0 -0
  290. /package/web/assets/{powershell-BdbPbX_X.js → powershell-rmYjKoE4.js} +0 -0
  291. /package/web/assets/{protobuf-qqDa50tu.js → protobuf-B--9ME3E.js} +0 -0
  292. /package/web/assets/{pug-Cu20UQ5w.js → pug-y0-ppnAV.js} +0 -0
  293. /package/web/assets/{qsharp-DcbHL5X7.js → qsharp-DobgOAC8.js} +0 -0
  294. /package/web/assets/{r-Bvig6Flk.js → r-BHQZIThn.js} +0 -0
  295. /package/web/assets/{redis-BxHDcW3-.js → redis-DMHMVIni.js} +0 -0
  296. /package/web/assets/{redshift-B8dvlzjH.js → redshift-ilRjY5HF.js} +0 -0
  297. /package/web/assets/{restructuredtext-npodtAFM.js → restructuredtext-jr4YYJbv.js} +0 -0
  298. /package/web/assets/{ruby-DiofbcJD.js → ruby-D-h_0NIs.js} +0 -0
  299. /package/web/assets/{rust-BjYid3-m.js → rust-VxeTSQU2.js} +0 -0
  300. /package/web/assets/{sb-CzcPAP71.js → sb-BlhKufrP.js} +0 -0
  301. /package/web/assets/{scala-Ci4wFrYi.js → scala-IAk6nSCU.js} +0 -0
  302. /package/web/assets/{scheme-D3iWhvP7.js → scheme-Dggdm5LS.js} +0 -0
  303. /package/web/assets/{scss-DdlqDgmu.js → scss-Bzj9Cp1q.js} +0 -0
  304. /package/web/assets/{shell-Bawb9gbH.js → shell-CSwYJdGJ.js} +0 -0
  305. /package/web/assets/{solidity-Z2Sq8PBC.js → solidity-DnWqIXnk.js} +0 -0
  306. /package/web/assets/{sophia--yD-H1Ow.js → sophia-ClaYC_5N.js} +0 -0
  307. /package/web/assets/{sparql-0vv3-wLi.js → sparql-CfjIZAcz.js} +0 -0
  308. /package/web/assets/{sql-0gdWKhmJ.js → sql-DxdJ7snd.js} +0 -0
  309. /package/web/assets/{st-rE-x-DTZ.js → st-DJgQE1uj.js} +0 -0
  310. /package/web/assets/{swift-CSKeG8Ne.js → swift-5cz3wIvv.js} +0 -0
  311. /package/web/assets/{systemverilog-CblNM3Im.js → systemverilog-C8-7QuIt.js} +0 -0
  312. /package/web/assets/{tcl-BYnzj-GZ.js → tcl-C99u42o5.js} +0 -0
  313. /package/web/assets/{twig-DiGWiEtp.js → twig-Cv9kOcBK.js} +0 -0
  314. /package/web/assets/{typespec-a6xMcpXw.js → typespec-DF1s-ehc.js} +0 -0
  315. /package/web/assets/{uuid-B9GFVXPm.js → uuid-6HQMUkhT.js} +0 -0
  316. /package/web/assets/{vb---51HVMC.js → vb-4mpz9ITX.js} +0 -0
  317. /package/web/assets/{w3c-keyname-DDCGDLuI.js → w3c-keyname-emj7Fjc1.js} +0 -0
  318. /package/web/assets/{wgsl-2A8z4pT5.js → wgsl-xExw6Z08.js} +0 -0
@@ -34,7 +34,7 @@ import {
34
34
  nativeParsers,
35
35
  serviceStore,
36
36
  traefikManager
37
- } from "./chunk-BSPKMRKW.js";
37
+ } from "./chunk-6547GLUJ.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,11 +408,13 @@ function requireEnvAction(ctx, environmentId, action, serviceId) {
400
408
  });
401
409
  }
402
410
  var startedAt = Date.now();
411
+ var appVersion = process.env["RUNEYA_VERSION"] ?? (true ? "2.0.10" : "dev");
403
412
  var healthRouter = router({
404
- check: publicProcedure.meta({ openapi: { method: "GET", path: "/health", tags: ["health"], summary: "Health check" } }).output(z.object({ status: z.literal("ok"), uptime: z.number() })).query(() => {
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(() => {
405
414
  return {
406
415
  status: "ok",
407
- uptime: Date.now() - startedAt
416
+ uptime: Date.now() - startedAt,
417
+ version: appVersion
408
418
  };
409
419
  })
410
420
  });
@@ -571,7 +581,6 @@ var EnvironmentOverridesStore = class {
571
581
  const raw = await readFile3(this.getFilePath(), "utf-8");
572
582
  const parsed = JSON.parse(raw);
573
583
  if (!getEncryptionKey()) {
574
- this.loaded = true;
575
584
  return;
576
585
  }
577
586
  this.diskEncryptedSlots = extractEncryptedVariableSlots(
@@ -1868,7 +1877,8 @@ var processRouter = router({
1868
1877
  agentId: z5.string().max(128),
1869
1878
  processId: z5.string().max(128),
1870
1879
  command: z5.string().min(1).max(4096),
1871
- cwd: z5.string().max(4096).optional()
1880
+ cwd: z5.string().max(4096).optional(),
1881
+ commandId: z5.string().max(128).optional()
1872
1882
  })).mutation(async ({ input }) => {
1873
1883
  const service = await serviceStore.get(input.processId);
1874
1884
  if (service) {
@@ -1882,7 +1892,7 @@ var processRouter = router({
1882
1892
  const result = await agentManager.agentMutation(
1883
1893
  input.agentId,
1884
1894
  "process.executeAdHocCommand",
1885
- { id: input.processId, command: input.command, cwd: input.cwd },
1895
+ { id: input.processId, command: input.command, cwd: input.cwd, commandId: input.commandId },
1886
1896
  AGENT_LONG_FETCH_TIMEOUT
1887
1897
  );
1888
1898
  await auditService.log({
@@ -1906,9 +1916,10 @@ var processRouter = router({
1906
1916
  /** Cancel a running ad-hoc command on an agent */
1907
1917
  cancelAdHocCommand: protectedProcedure.input(z5.object({
1908
1918
  agentId: z5.string().max(128),
1909
- processId: z5.string().max(128)
1919
+ processId: z5.string().max(128),
1920
+ commandId: z5.string().max(128).optional()
1910
1921
  })).mutation(async ({ input }) => {
1911
- await agentManager.agentMutation(input.agentId, "process.cancelAdHocCommand", { id: input.processId });
1922
+ await agentManager.agentMutation(input.agentId, "process.cancelAdHocCommand", { id: input.processId, commandId: input.commandId });
1912
1923
  return { success: true };
1913
1924
  }),
1914
1925
  /** Get current metrics for a process from an agent */
@@ -2978,200 +2989,6 @@ var apiKeysRouter = router({
2978
2989
  return { path: input.relativePath };
2979
2990
  })
2980
2991
  });
2981
- var Id128Schema = z13.string().max(128);
2982
- var ConversationIdSchema = z13.string().max(128);
2983
- var NameSchema = z13.string().min(1).max(200);
2984
- var ShortString200Schema = z13.string().max(200);
2985
- var DescriptionSchema = z13.string().max(2e3);
2986
- var OptionalProviderIdSchema = Id128Schema.optional();
2987
- function generateAdminLlmsTxt(services, jwt) {
2988
- const baseUrl = `http://localhost:${env.PORT}`;
2989
- const trpcBase = `${baseUrl}/api/trpc`;
2990
- const lines = [];
2991
- lines.push("# Runeya \u2014 Service Management API");
2992
- lines.push("");
2993
- lines.push("> You are operating as an authenticated admin with full access to all services.");
2994
- lines.push(`> Base URL: ${trpcBase}`);
2995
- if (jwt) {
2996
- lines.push("> Authentication: pass `Authorization: Bearer $RUNEYA_API_TOKEN` on all tRPC requests.");
2997
- }
2998
- lines.push("");
2999
- if (services.length === 0) {
3000
- lines.push("No services configured.");
3001
- return lines.join("\n");
3002
- }
3003
- lines.push("## Services");
3004
- lines.push("");
3005
- for (const svc of services) {
3006
- lines.push(`### ${svc.name}`);
3007
- lines.push(`- **ID**: \`${svc.id}\``);
3008
- if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3009
- if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3010
- if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3011
- lines.push("- **Permitted actions**: all");
3012
- lines.push("");
3013
- }
3014
- lines.push("## MCP Tools");
3015
- lines.push("");
3016
- lines.push("You have access to Runeya MCP tools to manage services. **Always prefer MCP tools** over raw HTTP calls.");
3017
- lines.push("Available tools: `list_services`, `get_service_status`, `start_service`, `stop_service`, `restart_service`, `get_logs`.");
3018
- lines.push("");
3019
- if (jwt) {
3020
- lines.push("**Important**: Pass the `authToken` parameter with value `$RUNEYA_API_TOKEN` (from environment) on every MCP tool call for authentication.");
3021
- lines.push("");
3022
- }
3023
- lines.push("Use the service IDs and agent IDs listed above when calling these tools.");
3024
- lines.push("");
3025
- lines.push("## API Reference (fallback)");
3026
- lines.push("");
3027
- lines.push("Only use raw HTTP calls if MCP tools are unavailable.");
3028
- lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3029
- lines.push("");
3030
- lines.push("**tRPC v11 request format:**");
3031
- lines.push('- **GET queries**: `?input={"id":"abc"}`');
3032
- lines.push('- **POST mutations**: JSON body `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3033
- lines.push("");
3034
- lines.push(`### List services
3035
- \`GET ${trpcBase}/service.list\``);
3036
- lines.push("");
3037
- lines.push(`### Get service
3038
- \`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3039
- lines.push("");
3040
- lines.push(`### Read logs
3041
- \`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3042
- lines.push("");
3043
- lines.push(`### Start service
3044
- \`POST ${trpcBase}/process.start\`
3045
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3046
- lines.push("");
3047
- lines.push(`### Stop service
3048
- \`POST ${trpcBase}/process.stop\`
3049
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3050
- lines.push("");
3051
- lines.push(`### Restart service
3052
- \`POST ${trpcBase}/process.restart\`
3053
- Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3054
- lines.push("");
3055
- lines.push(`### Get service status
3056
- \`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3057
- lines.push("");
3058
- return lines.join("\n");
3059
- }
3060
- function generateLlmsTxt(apiKey, services) {
3061
- const { scopes } = apiKey;
3062
- const baseUrl = `http://localhost:${env.PORT}`;
3063
- const trpcBase = `${baseUrl}/api/trpc`;
3064
- const lines = [];
3065
- lines.push("# Runeya \u2014 Service Management API");
3066
- lines.push("");
3067
- lines.push("> This file describes the API surface available for this key.");
3068
- lines.push(`> Base URL: ${trpcBase}`);
3069
- lines.push(`> Authentication: pass this key as \`Authorization: Bearer ${apiKey.key}\` on all tRPC requests.`);
3070
- lines.push("");
3071
- if (services.length === 0) {
3072
- lines.push("No services accessible with this key.");
3073
- return lines.join("\n");
3074
- }
3075
- const permByServiceId = new Map(
3076
- scopes.services.map((p) => [p.serviceId, p])
3077
- );
3078
- lines.push("## Services");
3079
- lines.push("");
3080
- for (const svc of services) {
3081
- const perm = permByServiceId.get(svc.id);
3082
- const actions = perm?.actions ?? [];
3083
- lines.push(`### ${svc.name}`);
3084
- lines.push(`- **ID**: \`${svc.id}\``);
3085
- if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3086
- if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3087
- if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3088
- lines.push("- **Permitted actions**:");
3089
- if (actions.length === 0) {
3090
- lines.push(" - _(none)_");
3091
- } else {
3092
- for (const a of actions) lines.push(` - \`${a}\``);
3093
- }
3094
- const envPerms = perm?.environments ?? [];
3095
- if (envPerms.length > 0) {
3096
- lines.push("- **Environment access**:");
3097
- for (const ep of envPerms) {
3098
- const envActions = ep.actions.join(", ");
3099
- const secrets = ep.includeSecrets ? " _(includes secrets)_" : "";
3100
- lines.push(` - \`${ep.environmentId}\`: ${envActions}${secrets}`);
3101
- }
3102
- }
3103
- lines.push("");
3104
- }
3105
- lines.push("## API Reference");
3106
- lines.push("");
3107
- lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3108
- lines.push("");
3109
- lines.push("**Important \u2014 tRPC v11 request format:**");
3110
- lines.push('- **GET queries**: pass input as a plain JSON-encoded string in the `input` query param (no `{"json":...}` wrapper).');
3111
- lines.push(' Example: `?input={"id":"abc"}`');
3112
- lines.push('- **POST mutations**: send the input object as a plain JSON body (no `{"json":...}` wrapper).');
3113
- lines.push(' Example body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3114
- lines.push("- The `agentId` is listed per service above.");
3115
- lines.push("- The `processId` is the same as the service `ID` listed above.");
3116
- lines.push("");
3117
- const allActions = new Set(scopes.services.flatMap((p) => p.actions));
3118
- const hasEnvRead = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:read")));
3119
- const hasEnvWrite = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:write")));
3120
- if (allActions.has("service:read")) {
3121
- lines.push("### List services");
3122
- lines.push(`\`GET ${trpcBase}/service.list\``);
3123
- lines.push("");
3124
- lines.push("### Get service");
3125
- lines.push(`\`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3126
- lines.push("");
3127
- }
3128
- if (allActions.has("logs:read")) {
3129
- lines.push("### Read logs");
3130
- lines.push(`\`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3131
- lines.push("");
3132
- }
3133
- if (allActions.has("service:start")) {
3134
- lines.push("### Start service");
3135
- lines.push(`\`POST ${trpcBase}/process.start\``);
3136
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3137
- lines.push("");
3138
- }
3139
- if (allActions.has("service:stop")) {
3140
- lines.push("### Stop service");
3141
- lines.push(`\`POST ${trpcBase}/process.stop\``);
3142
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3143
- lines.push("");
3144
- }
3145
- if (allActions.has("service:restart")) {
3146
- lines.push("### Restart service");
3147
- lines.push(`\`POST ${trpcBase}/process.restart\``);
3148
- lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3149
- lines.push("");
3150
- }
3151
- if (allActions.has("service:status")) {
3152
- lines.push("### Get service status");
3153
- lines.push(`\`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3154
- lines.push("");
3155
- }
3156
- if (allActions.has("service:healthcheck")) {
3157
- lines.push("### Get service health");
3158
- lines.push(`\`GET ${trpcBase}/process.health?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3159
- lines.push("");
3160
- }
3161
- if (hasEnvRead) {
3162
- lines.push("### Read environment variables (flat KEY=VALUE \u2014 secrets shown as *****)");
3163
- lines.push(`\`GET ${trpcBase}/environment.resolveFlat?input={"environmentId":"<environmentId>"}\``);
3164
- lines.push("Returns a flat `Record<string, string>`. Secret variables you are not authorized to see are shown as `*****`.");
3165
- lines.push("");
3166
- }
3167
- if (hasEnvWrite) {
3168
- lines.push("### Write environment variable");
3169
- lines.push(`\`POST ${trpcBase}/environment.upsertVariable\``);
3170
- lines.push('Body: `{"environmentId":"<environmentId>","key":"<key>","value":"<value>"}`');
3171
- lines.push("");
3172
- }
3173
- return lines.join("\n");
3174
- }
3175
2992
  var RUNEYA_CAPABILITIES = [
3176
2993
  {
3177
2994
  name: "list_services",
@@ -3406,43 +3223,8 @@ async function callTrpc(baseUrl, route, method, input, authToken) {
3406
3223
  const result = json["result"];
3407
3224
  return result?.["data"] ?? result ?? json;
3408
3225
  }
3409
- var CHAT_TOOLS = buildAnthropicTools(RUNEYA_CAPABILITIES);
3410
- var CHAT_TOOLS_OPENAI = buildOpenAITools(RUNEYA_CAPABILITIES);
3411
- var TOOL_SAFETY_INSTRUCTION = buildToolSafetyInstruction(RUNEYA_CAPABILITIES);
3412
- function hasServiceAction(apiKey, serviceId, action) {
3413
- if (!apiKey) return true;
3414
- const perm = apiKey.scopes.services.find((p) => p.serviceId === serviceId);
3415
- return perm?.actions.includes(action) ?? false;
3416
- }
3417
- var KNOWN_TOOL_NAMES = RUNEYA_CAPABILITIES.map((c) => c.name);
3418
- function normalizeToolName(raw) {
3419
- if (KNOWN_TOOL_NAMES.includes(raw)) return raw;
3420
- const match = KNOWN_TOOL_NAMES.find((t2) => raw.startsWith(t2));
3421
- return match ?? raw;
3422
- }
3423
- async function executeTool(rawName, input, apiKey, authToken) {
3424
- const name = normalizeToolName(rawName);
3425
- const cap = RUNEYA_CAPABILITIES.find((c) => c.name === name);
3426
- if (!cap) throw new Error(`Unknown tool: ${name}`);
3427
- const serviceId = input["serviceId"] ?? input["id"] ?? input["processId"];
3428
- if (serviceId) {
3429
- const actionMap = {
3430
- get_service_status: "service:status",
3431
- get_logs: "logs:read",
3432
- start_service: "service:start",
3433
- stop_service: "service:stop",
3434
- restart_service: "service:restart"
3435
- };
3436
- const action = actionMap[name];
3437
- if (action && !hasServiceAction(apiKey, serviceId, action)) {
3438
- throw new Error(`No '${action}' permission for service ${serviceId}`);
3439
- }
3440
- }
3441
- const params = Object.fromEntries(
3442
- Object.entries(input).map(([k, v]) => [k, v != null ? String(v) : void 0])
3443
- );
3444
- const baseUrl = `http://localhost:${env.PORT}`;
3445
- return callTrpc(baseUrl, cap.route, cap.method, cap.buildInput(params), authToken);
3226
+ function buildApiDocs(apiBaseUrl, apiToken) {
3227
+ return buildPromptDocs(apiBaseUrl, apiToken, RUNEYA_CAPABILITIES);
3446
3228
  }
3447
3229
  var FILENAME3 = "conversations.private.json";
3448
3230
  var ConversationStore = class {
@@ -3508,7 +3290,6 @@ var ConversationStore = class {
3508
3290
  async load() {
3509
3291
  if (this.loaded) return;
3510
3292
  if (!getEncryptionKey()) {
3511
- this.loaded = true;
3512
3293
  return;
3513
3294
  }
3514
3295
  try {
@@ -3580,7 +3361,7 @@ var ConversationStore = class {
3580
3361
  if (patch.workflowId !== void 0) conv.workflowId = patch.workflowId;
3581
3362
  if (patch.archived !== void 0) conv.archived = patch.archived;
3582
3363
  if (patch.codexThreadId !== void 0) conv.codexThreadId = patch.codexThreadId;
3583
- conv.updatedAt = Date.now();
3364
+ if (options?.touch !== false) conv.updatedAt = Date.now();
3584
3365
  await this.save();
3585
3366
  if (!options?.silent) {
3586
3367
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv) });
@@ -3618,14 +3399,430 @@ var ConversationStore = class {
3618
3399
  this.events.emit("changed", { type: "deleted", id });
3619
3400
  }
3620
3401
  }
3621
- onChanged(handler) {
3622
- this.events.on("changed", handler);
3402
+ onChanged(handler) {
3403
+ this.events.on("changed", handler);
3404
+ }
3405
+ offChanged(handler) {
3406
+ this.events.off("changed", handler);
3407
+ }
3408
+ };
3409
+ var conversationStore = new ConversationStore();
3410
+ var PROJECTS_DIR = join10(homedir3(), ".claude", "projects");
3411
+ var DEFAULT_TITLES = /* @__PURE__ */ new Set(["", "New conversation", "Nouvelle conversation"]);
3412
+ async function findClaudeSessionFile(sessionId) {
3413
+ let entries;
3414
+ try {
3415
+ entries = await readdir2(PROJECTS_DIR);
3416
+ } catch {
3417
+ return null;
3418
+ }
3419
+ const filename = `${sessionId}.jsonl`;
3420
+ for (const dir of entries) {
3421
+ const candidate = join10(PROJECTS_DIR, dir, filename);
3422
+ try {
3423
+ await access(candidate);
3424
+ return candidate;
3425
+ } catch {
3426
+ }
3427
+ }
3428
+ return null;
3429
+ }
3430
+ async function claudeSessionExists(sessionId) {
3431
+ return await findClaudeSessionFile(sessionId) !== null;
3432
+ }
3433
+ async function readClaudeTranscript(sessionId) {
3434
+ const path = await findClaudeSessionFile(sessionId);
3435
+ if (!path) return { found: false, path: null, entries: [] };
3436
+ const raw = await readFile8(path, "utf-8");
3437
+ const entries = [];
3438
+ for (const line of raw.split("\n")) {
3439
+ if (!line.trim()) continue;
3440
+ try {
3441
+ entries.push(JSON.parse(line));
3442
+ } catch {
3443
+ }
3444
+ }
3445
+ return { found: true, path, entries };
3446
+ }
3447
+ function claudeEntriesToMessages(entries) {
3448
+ const messages = [];
3449
+ const toolPartById = /* @__PURE__ */ new Map();
3450
+ for (const entry of entries) {
3451
+ if (entry.type !== "user" && entry.type !== "assistant") continue;
3452
+ const msg = entry.message;
3453
+ if (!msg) continue;
3454
+ const role = msg.role === "user" || msg.role === "assistant" ? msg.role : entry.type;
3455
+ const content = msg.content;
3456
+ const texts = [];
3457
+ const parts = [];
3458
+ let consumedByToolResult = false;
3459
+ if (typeof content === "string") {
3460
+ texts.push(content);
3461
+ } else if (Array.isArray(content)) {
3462
+ for (const block of content) {
3463
+ if (typeof block === "string") {
3464
+ texts.push(block);
3465
+ continue;
3466
+ }
3467
+ if (!block || typeof block !== "object") continue;
3468
+ const b = block;
3469
+ if (b.type === "text" && typeof b.text === "string") {
3470
+ texts.push(b.text);
3471
+ parts.push({ type: "text", text: b.text });
3472
+ } else if (b.type === "tool_use" && b.id) {
3473
+ const part = {
3474
+ type: "tool",
3475
+ id: b.id,
3476
+ name: b.name ?? "tool",
3477
+ input: b.input,
3478
+ pending: true
3479
+ };
3480
+ parts.push(part);
3481
+ toolPartById.set(b.id, part);
3482
+ } else if (b.type === "tool_result" && b.tool_use_id) {
3483
+ const target = toolPartById.get(b.tool_use_id);
3484
+ if (target) {
3485
+ target.result = b.content;
3486
+ target.isError = b.is_error;
3487
+ target.pending = false;
3488
+ }
3489
+ consumedByToolResult = true;
3490
+ }
3491
+ }
3492
+ }
3493
+ if (role === "user" && consumedByToolResult && texts.length === 0) continue;
3494
+ const last = messages[messages.length - 1];
3495
+ if (last && last.role === role) {
3496
+ const joinedText = [last.content, texts.join("\n").trim()].filter(Boolean).join("\n");
3497
+ last.content = joinedText;
3498
+ if (parts.length > 0) {
3499
+ last.parts = [...last.parts ?? [], ...parts];
3500
+ }
3501
+ continue;
3502
+ }
3503
+ messages.push({
3504
+ role,
3505
+ content: texts.join("\n").trim(),
3506
+ parts: parts.length > 0 ? parts : void 0
3507
+ });
3508
+ }
3509
+ return messages;
3510
+ }
3511
+ async function syncConversationFromClaudeTranscript(conversationId) {
3512
+ const conv = await conversationStore.get(conversationId);
3513
+ if (!conv) return false;
3514
+ const transcript = await readClaudeTranscript(conversationId);
3515
+ if (!transcript.found) return false;
3516
+ const messages = claudeEntriesToMessages(transcript.entries);
3517
+ const isDefaultTitle = !conv.title || DEFAULT_TITLES.has(conv.title);
3518
+ const sameLength = messages.length === conv.messages.length;
3519
+ const sameLastContent = sameLength && (messages.at(-1)?.content ?? "") === (conv.messages.at(-1)?.content ?? "");
3520
+ if (sameLength && sameLastContent && !isDefaultTitle) {
3521
+ return false;
3522
+ }
3523
+ const patch = { messages };
3524
+ if (isDefaultTitle) {
3525
+ const firstUserMsg = messages.find((m) => m.role === "user" && m.content.trim().length > 0);
3526
+ if (firstUserMsg) {
3527
+ const firstLine = firstUserMsg.content.split("\n")[0].trim();
3528
+ patch.title = firstLine.length > 80 ? firstLine.slice(0, 80) + "\u2026" : firstLine;
3529
+ }
3530
+ }
3531
+ await conversationStore.update(conversationId, patch, { touch: false });
3532
+ return true;
3533
+ }
3534
+ var lastTranscriptActivity = /* @__PURE__ */ new Map();
3535
+ function getLastTranscriptActivity(sessionId) {
3536
+ return lastTranscriptActivity.get(sessionId) ?? null;
3537
+ }
3538
+ var started = false;
3539
+ var watchers = [];
3540
+ var pendingResyncs = /* @__PURE__ */ new Map();
3541
+ var RESYNC_DEBOUNCE_MS = 250;
3542
+ function scheduleResync(sessionId) {
3543
+ const existing = pendingResyncs.get(sessionId);
3544
+ if (existing) clearTimeout(existing);
3545
+ pendingResyncs.set(
3546
+ sessionId,
3547
+ setTimeout(() => {
3548
+ pendingResyncs.delete(sessionId);
3549
+ syncConversationFromClaudeTranscript(sessionId).catch((err) => {
3550
+ console.warn("[claude-transcript-sync] resync failed for", sessionId, err);
3551
+ });
3552
+ }, RESYNC_DEBOUNCE_MS)
3553
+ );
3554
+ }
3555
+ function handleFsEvent(filename) {
3556
+ if (!filename) return;
3557
+ if (!filename.endsWith(".jsonl")) return;
3558
+ const base = filename.split("/").pop();
3559
+ const sessionId = base.slice(0, -".jsonl".length);
3560
+ 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;
3561
+ lastTranscriptActivity.set(sessionId, Date.now());
3562
+ scheduleResync(sessionId);
3563
+ }
3564
+ async function startClaudeTranscriptWatcher() {
3565
+ if (started) return;
3566
+ started = true;
3567
+ let topLevel;
3568
+ try {
3569
+ topLevel = await readdir2(PROJECTS_DIR);
3570
+ } catch {
3571
+ return;
3572
+ }
3573
+ try {
3574
+ const w = watch(PROJECTS_DIR, { recursive: true }, (_event, filename) => {
3575
+ handleFsEvent(filename ? filename.toString() : null);
3576
+ });
3577
+ w.on("error", (err) => console.warn("[claude-transcript-sync] watcher error:", err));
3578
+ watchers.push(w);
3579
+ return;
3580
+ } catch {
3581
+ }
3582
+ for (const dir of topLevel) {
3583
+ try {
3584
+ const w = watch(join10(PROJECTS_DIR, dir), (_event, filename) => {
3585
+ handleFsEvent(filename ? filename.toString() : null);
3586
+ });
3587
+ w.on("error", () => {
3588
+ });
3589
+ watchers.push(w);
3590
+ } catch {
3591
+ }
3592
+ }
3593
+ }
3594
+ var Id128Schema = z13.string().max(128);
3595
+ var ConversationIdSchema = z13.string().max(128);
3596
+ var NameSchema = z13.string().min(1).max(200);
3597
+ var ShortString200Schema = z13.string().max(200);
3598
+ var DescriptionSchema = z13.string().max(2e3);
3599
+ var OptionalProviderIdSchema = Id128Schema.optional();
3600
+ function generateAdminLlmsTxt(services, jwt) {
3601
+ const baseUrl = `http://localhost:${env.PORT}`;
3602
+ const trpcBase = `${baseUrl}/api/trpc`;
3603
+ const lines = [];
3604
+ lines.push("# Runeya \u2014 Service Management API");
3605
+ lines.push("");
3606
+ lines.push("> You are operating as an authenticated admin with full access to all services.");
3607
+ lines.push(`> Base URL: ${trpcBase}`);
3608
+ if (jwt) {
3609
+ lines.push("> Authentication: pass `Authorization: Bearer $RUNEYA_API_TOKEN` on all tRPC requests.");
3610
+ }
3611
+ lines.push("");
3612
+ if (services.length === 0) {
3613
+ lines.push("No services configured.");
3614
+ return lines.join("\n");
3615
+ }
3616
+ lines.push("## Services");
3617
+ lines.push("");
3618
+ for (const svc of services) {
3619
+ lines.push(`### ${svc.name}`);
3620
+ lines.push(`- **ID**: \`${svc.id}\``);
3621
+ if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3622
+ if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3623
+ if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3624
+ lines.push("- **Permitted actions**: all");
3625
+ lines.push("");
3626
+ }
3627
+ lines.push("## MCP Tools");
3628
+ lines.push("");
3629
+ lines.push("You have access to Runeya MCP tools to manage services. **Always prefer MCP tools** over raw HTTP calls.");
3630
+ lines.push("Available tools: `list_services`, `get_service_status`, `start_service`, `stop_service`, `restart_service`, `get_logs`.");
3631
+ lines.push("");
3632
+ if (jwt) {
3633
+ lines.push("**Important**: Pass the `authToken` parameter with value `$RUNEYA_API_TOKEN` (from environment) on every MCP tool call for authentication.");
3634
+ lines.push("");
3635
+ }
3636
+ lines.push("Use the service IDs and agent IDs listed above when calling these tools.");
3637
+ lines.push("");
3638
+ lines.push("## API Reference (fallback)");
3639
+ lines.push("");
3640
+ lines.push("Only use raw HTTP calls if MCP tools are unavailable.");
3641
+ lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3642
+ lines.push("");
3643
+ lines.push("**tRPC v11 request format:**");
3644
+ lines.push('- **GET queries**: `?input={"id":"abc"}`');
3645
+ lines.push('- **POST mutations**: JSON body `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3646
+ lines.push("");
3647
+ lines.push(`### List services
3648
+ \`GET ${trpcBase}/service.list\``);
3649
+ lines.push("");
3650
+ lines.push(`### Get service
3651
+ \`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3652
+ lines.push("");
3653
+ lines.push(`### Read logs
3654
+ \`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3655
+ lines.push("");
3656
+ lines.push(`### Start service
3657
+ \`POST ${trpcBase}/process.start\`
3658
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3659
+ lines.push("");
3660
+ lines.push(`### Stop service
3661
+ \`POST ${trpcBase}/process.stop\`
3662
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3663
+ lines.push("");
3664
+ lines.push(`### Restart service
3665
+ \`POST ${trpcBase}/process.restart\`
3666
+ Body: \`{"agentId":"<agentId>","processId":"<serviceId>"}\``);
3667
+ lines.push("");
3668
+ lines.push(`### Get service status
3669
+ \`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3670
+ lines.push("");
3671
+ return lines.join("\n");
3672
+ }
3673
+ function generateLlmsTxt(apiKey, services) {
3674
+ const { scopes } = apiKey;
3675
+ const baseUrl = `http://localhost:${env.PORT}`;
3676
+ const trpcBase = `${baseUrl}/api/trpc`;
3677
+ const lines = [];
3678
+ lines.push("# Runeya \u2014 Service Management API");
3679
+ lines.push("");
3680
+ lines.push("> This file describes the API surface available for this key.");
3681
+ lines.push(`> Base URL: ${trpcBase}`);
3682
+ lines.push(`> Authentication: pass this key as \`Authorization: Bearer ${apiKey.key}\` on all tRPC requests.`);
3683
+ lines.push("");
3684
+ if (services.length === 0) {
3685
+ lines.push("No services accessible with this key.");
3686
+ return lines.join("\n");
3687
+ }
3688
+ const permByServiceId = new Map(
3689
+ scopes.services.map((p) => [p.serviceId, p])
3690
+ );
3691
+ lines.push("## Services");
3692
+ lines.push("");
3693
+ for (const svc of services) {
3694
+ const perm = permByServiceId.get(svc.id);
3695
+ const actions = perm?.actions ?? [];
3696
+ lines.push(`### ${svc.name}`);
3697
+ lines.push(`- **ID**: \`${svc.id}\``);
3698
+ if (svc.agentId) lines.push(`- **Agent ID**: \`${svc.agentId}\``);
3699
+ if (svc.description) lines.push(`- **Description**: ${svc.description}`);
3700
+ if (svc.cwd) lines.push(`- **Working directory**: \`${svc.cwd}\``);
3701
+ lines.push("- **Permitted actions**:");
3702
+ if (actions.length === 0) {
3703
+ lines.push(" - _(none)_");
3704
+ } else {
3705
+ for (const a of actions) lines.push(` - \`${a}\``);
3706
+ }
3707
+ const envPerms = perm?.environments ?? [];
3708
+ if (envPerms.length > 0) {
3709
+ lines.push("- **Environment access**:");
3710
+ for (const ep of envPerms) {
3711
+ const envActions = ep.actions.join(", ");
3712
+ const secrets = ep.includeSecrets ? " _(includes secrets)_" : "";
3713
+ lines.push(` - \`${ep.environmentId}\`: ${envActions}${secrets}`);
3714
+ }
3715
+ }
3716
+ lines.push("");
3717
+ }
3718
+ lines.push("## API Reference");
3719
+ lines.push("");
3720
+ lines.push("All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).");
3721
+ lines.push("");
3722
+ lines.push("**Important \u2014 tRPC v11 request format:**");
3723
+ lines.push('- **GET queries**: pass input as a plain JSON-encoded string in the `input` query param (no `{"json":...}` wrapper).');
3724
+ lines.push(' Example: `?input={"id":"abc"}`');
3725
+ lines.push('- **POST mutations**: send the input object as a plain JSON body (no `{"json":...}` wrapper).');
3726
+ lines.push(' Example body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3727
+ lines.push("- The `agentId` is listed per service above.");
3728
+ lines.push("- The `processId` is the same as the service `ID` listed above.");
3729
+ lines.push("");
3730
+ const allActions = new Set(scopes.services.flatMap((p) => p.actions));
3731
+ const hasEnvRead = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:read")));
3732
+ const hasEnvWrite = scopes.services.some((p) => p.environments.some((e) => e.actions.includes("env:write")));
3733
+ if (allActions.has("service:read")) {
3734
+ lines.push("### List services");
3735
+ lines.push(`\`GET ${trpcBase}/service.list\``);
3736
+ lines.push("");
3737
+ lines.push("### Get service");
3738
+ lines.push(`\`GET ${trpcBase}/service.get?input={"agentId":"<agentId>","id":"<serviceId>"}\``);
3739
+ lines.push("");
3740
+ }
3741
+ if (allActions.has("logs:read")) {
3742
+ lines.push("### Read logs");
3743
+ lines.push(`\`GET ${trpcBase}/process.logs?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3744
+ lines.push("");
3745
+ }
3746
+ if (allActions.has("service:start")) {
3747
+ lines.push("### Start service");
3748
+ lines.push(`\`POST ${trpcBase}/process.start\``);
3749
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3750
+ lines.push("");
3751
+ }
3752
+ if (allActions.has("service:stop")) {
3753
+ lines.push("### Stop service");
3754
+ lines.push(`\`POST ${trpcBase}/process.stop\``);
3755
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3756
+ lines.push("");
3757
+ }
3758
+ if (allActions.has("service:restart")) {
3759
+ lines.push("### Restart service");
3760
+ lines.push(`\`POST ${trpcBase}/process.restart\``);
3761
+ lines.push('Body: `{"agentId":"<agentId>","processId":"<serviceId>"}`');
3762
+ lines.push("");
3763
+ }
3764
+ if (allActions.has("service:status")) {
3765
+ lines.push("### Get service status");
3766
+ lines.push(`\`GET ${trpcBase}/process.status?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3767
+ lines.push("");
3768
+ }
3769
+ if (allActions.has("service:healthcheck")) {
3770
+ lines.push("### Get service health");
3771
+ lines.push(`\`GET ${trpcBase}/process.health?input={"agentId":"<agentId>","processId":"<serviceId>"}\``);
3772
+ lines.push("");
3773
+ }
3774
+ if (hasEnvRead) {
3775
+ lines.push("### Read environment variables (flat KEY=VALUE \u2014 secrets shown as *****)");
3776
+ lines.push(`\`GET ${trpcBase}/environment.resolveFlat?input={"environmentId":"<environmentId>"}\``);
3777
+ lines.push("Returns a flat `Record<string, string>`. Secret variables you are not authorized to see are shown as `*****`.");
3778
+ lines.push("");
3623
3779
  }
3624
- offChanged(handler) {
3625
- this.events.off("changed", handler);
3780
+ if (hasEnvWrite) {
3781
+ lines.push("### Write environment variable");
3782
+ lines.push(`\`POST ${trpcBase}/environment.upsertVariable\``);
3783
+ lines.push('Body: `{"environmentId":"<environmentId>","key":"<key>","value":"<value>"}`');
3784
+ lines.push("");
3626
3785
  }
3627
- };
3628
- var conversationStore = new ConversationStore();
3786
+ return lines.join("\n");
3787
+ }
3788
+ var CHAT_TOOLS = buildAnthropicTools(RUNEYA_CAPABILITIES);
3789
+ var CHAT_TOOLS_OPENAI = buildOpenAITools(RUNEYA_CAPABILITIES);
3790
+ var TOOL_SAFETY_INSTRUCTION = buildToolSafetyInstruction(RUNEYA_CAPABILITIES);
3791
+ function hasServiceAction(apiKey, serviceId, action) {
3792
+ if (!apiKey) return true;
3793
+ const perm = apiKey.scopes.services.find((p) => p.serviceId === serviceId);
3794
+ return perm?.actions.includes(action) ?? false;
3795
+ }
3796
+ var KNOWN_TOOL_NAMES = RUNEYA_CAPABILITIES.map((c) => c.name);
3797
+ function normalizeToolName(raw) {
3798
+ if (KNOWN_TOOL_NAMES.includes(raw)) return raw;
3799
+ const match = KNOWN_TOOL_NAMES.find((t2) => raw.startsWith(t2));
3800
+ return match ?? raw;
3801
+ }
3802
+ async function executeTool(rawName, input, apiKey, authToken) {
3803
+ const name = normalizeToolName(rawName);
3804
+ const cap = RUNEYA_CAPABILITIES.find((c) => c.name === name);
3805
+ if (!cap) throw new Error(`Unknown tool: ${name}`);
3806
+ const serviceId = input["serviceId"] ?? input["id"] ?? input["processId"];
3807
+ if (serviceId) {
3808
+ const actionMap = {
3809
+ get_service_status: "service:status",
3810
+ get_logs: "logs:read",
3811
+ start_service: "service:start",
3812
+ stop_service: "service:stop",
3813
+ restart_service: "service:restart"
3814
+ };
3815
+ const action = actionMap[name];
3816
+ if (action && !hasServiceAction(apiKey, serviceId, action)) {
3817
+ throw new Error(`No '${action}' permission for service ${serviceId}`);
3818
+ }
3819
+ }
3820
+ const params = Object.fromEntries(
3821
+ Object.entries(input).map(([k, v]) => [k, v != null ? String(v) : void 0])
3822
+ );
3823
+ const baseUrl = `http://localhost:${env.PORT}`;
3824
+ return callTrpc(baseUrl, cap.route, cap.method, cap.buildInput(params), authToken);
3825
+ }
3629
3826
  var MONTHS = {
3630
3827
  jan: 0,
3631
3828
  feb: 1,
@@ -4337,7 +4534,7 @@ async function atomicWriteJsonSecure(filePath, data, pretty = false) {
4337
4534
  }
4338
4535
  async function readJsonFileSafe(filePath) {
4339
4536
  try {
4340
- const raw = await readFile8(filePath, "utf-8");
4537
+ const raw = await readFile9(filePath, "utf-8");
4341
4538
  return JSON.parse(raw);
4342
4539
  } catch {
4343
4540
  return null;
@@ -4383,8 +4580,8 @@ var ScenarioRunStore = class _ScenarioRunStore {
4383
4580
  "- Use read-only references from linked upstream files as context.",
4384
4581
  ""
4385
4582
  ].join("\n");
4386
- await atomicWriteSecure(join10(dir, "README.md"), overview);
4387
- await atomicWriteSecure(join10(dir, "USER_REQUEST.md"), `# User request
4583
+ await atomicWriteSecure(join11(dir, "README.md"), overview);
4584
+ await atomicWriteSecure(join11(dir, "USER_REQUEST.md"), `# User request
4388
4585
 
4389
4586
  ${userInput.trim() || "(empty)"}
4390
4587
  `);
@@ -4410,7 +4607,7 @@ ${userInput.trim() || "(empty)"}
4410
4607
  "- Waiting for assignment.",
4411
4608
  ""
4412
4609
  ].join("\n");
4413
- await atomicWriteSecure(join10(dir, fileName), content);
4610
+ await atomicWriteSecure(join11(dir, fileName), content);
4414
4611
  }
4415
4612
  }
4416
4613
  async deleteRunDir(conversationId) {
@@ -4420,11 +4617,32 @@ ${userInput.trim() || "(empty)"}
4420
4617
  ensureRunState(conversationId) {
4421
4618
  let runState = this.activeRuns.get(conversationId);
4422
4619
  if (!runState) {
4423
- runState = { nodeStates: {}, nodeOutputs: {}, waitingInputs: {} };
4620
+ runState = { nodeStates: {}, nodeOutputs: {}, nodeSessionIds: {}, nodeAgentIds: {}, waitingInputs: {} };
4424
4621
  this.activeRuns.set(conversationId, runState);
4425
4622
  }
4426
4623
  return runState;
4427
4624
  }
4625
+ setNodeSession(conversationId, nodeId, sessionId, agentId) {
4626
+ const rs = this.ensureRunState(conversationId);
4627
+ rs.nodeSessionIds[nodeId] = sessionId;
4628
+ rs.nodeAgentIds[nodeId] = agentId;
4629
+ }
4630
+ getNodeSession(conversationId, nodeId) {
4631
+ const rs = this.activeRuns.get(conversationId);
4632
+ const sessionId = rs?.nodeSessionIds[nodeId];
4633
+ const agentId = rs?.nodeAgentIds[nodeId];
4634
+ if (sessionId && agentId) return { sessionId, agentId };
4635
+ return null;
4636
+ }
4637
+ async getNodeSessionPersistent(conversationId, nodeId) {
4638
+ const live = this.getNodeSession(conversationId, nodeId);
4639
+ if (live) return live;
4640
+ const persisted = await this.loadPersistedRunState(conversationId);
4641
+ const sessionId = persisted?.nodeSessionIds?.[nodeId];
4642
+ const agentId = persisted?.nodeAgentIds?.[nodeId];
4643
+ if (sessionId && agentId) return { sessionId, agentId };
4644
+ return null;
4645
+ }
4428
4646
  setNodeState(conversationId, nodeId, state) {
4429
4647
  this.ensureRunState(conversationId).nodeStates[nodeId] = state;
4430
4648
  }
@@ -4453,20 +4671,22 @@ ${userInput.trim() || "(empty)"}
4453
4671
  const data = {
4454
4672
  nodeStates: { ...runState.nodeStates },
4455
4673
  nodeOutputs: { ...runState.nodeOutputs },
4674
+ ...Object.keys(runState.nodeSessionIds).length > 0 ? { nodeSessionIds: { ...runState.nodeSessionIds } } : {},
4675
+ ...Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {},
4456
4676
  ...Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}
4457
4677
  };
4458
- await atomicWriteJsonSecure(join10(dir, "run-state.json"), data);
4678
+ await atomicWriteJsonSecure(join11(dir, "run-state.json"), data);
4459
4679
  } catch {
4460
4680
  }
4461
4681
  }
4462
4682
  async loadPersistedRunState(conversationId) {
4463
4683
  const dir = this.getRunDir(conversationId);
4464
- return readJsonFileSafe(join10(dir, "run-state.json"));
4684
+ return readJsonFileSafe(join11(dir, "run-state.json"));
4465
4685
  }
4466
4686
  async readUserRequest(conversationId) {
4467
4687
  try {
4468
4688
  const dir = this.getRunDir(conversationId);
4469
- const content = await readFile9(join10(dir, "USER_REQUEST.md"), "utf-8");
4689
+ const content = await readFile10(join11(dir, "USER_REQUEST.md"), "utf-8");
4470
4690
  return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
4471
4691
  } catch {
4472
4692
  return null;
@@ -4475,7 +4695,7 @@ ${userInput.trim() || "(empty)"}
4475
4695
  async readReadme(conversationId) {
4476
4696
  try {
4477
4697
  const dir = this.getRunDir(conversationId);
4478
- return await readFile9(join10(dir, "README.md"), "utf-8");
4698
+ return await readFile10(join11(dir, "README.md"), "utf-8");
4479
4699
  } catch {
4480
4700
  return null;
4481
4701
  }
@@ -4484,14 +4704,14 @@ ${userInput.trim() || "(empty)"}
4484
4704
  const dir = this.getRunDir(conversationId);
4485
4705
  await mkdir10(dir, { recursive: true });
4486
4706
  await atomicWriteJsonSecure(
4487
- join10(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
4707
+ join11(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
4488
4708
  scenario,
4489
4709
  true
4490
4710
  );
4491
4711
  }
4492
4712
  async loadScenarioSnapshot(conversationId) {
4493
4713
  const dir = this.getRunDir(conversationId);
4494
- return readJsonFileSafe(join10(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
4714
+ return readJsonFileSafe(join11(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
4495
4715
  }
4496
4716
  };
4497
4717
  var scenarioRunStore = new ScenarioRunStore();
@@ -4566,7 +4786,7 @@ var ImageStorageService = class {
4566
4786
  const imageId = randomUUID4();
4567
4787
  const safeExt = ext.replace(/[^a-z.]/g, "");
4568
4788
  const fileName = `${imageId}${safeExt}`;
4569
- const filePath = join11(this.dir, fileName);
4789
+ const filePath = join12(this.dir, fileName);
4570
4790
  const resolvedPath = resolve3(filePath);
4571
4791
  const resolvedDir = resolve3(this.dir);
4572
4792
  if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep2)) {
@@ -4580,10 +4800,10 @@ var ImageStorageService = class {
4580
4800
  async getImagePath(imageId) {
4581
4801
  if (!UUID_RE.test(imageId)) return null;
4582
4802
  try {
4583
- const files = await readdir2(this.dir);
4803
+ const files = await readdir3(this.dir);
4584
4804
  const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
4585
4805
  if (!metaFile) return null;
4586
- const raw = JSON.parse(await readFile10(join11(this.dir, metaFile), "utf-8"));
4806
+ const raw = JSON.parse(await readFile11(join12(this.dir, metaFile), "utf-8"));
4587
4807
  if (!existsSync(raw.filePath)) return null;
4588
4808
  return { filePath: raw.filePath, mimeType: raw.mimeType };
4589
4809
  } catch {
@@ -4601,14 +4821,14 @@ var ImageStorageService = class {
4601
4821
  async deleteByContextId(contextId) {
4602
4822
  let files;
4603
4823
  try {
4604
- files = await readdir2(this.dir);
4824
+ files = await readdir3(this.dir);
4605
4825
  } catch {
4606
4826
  return;
4607
4827
  }
4608
4828
  await Promise.all(
4609
4829
  files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
4610
4830
  try {
4611
- const raw = JSON.parse(await readFile10(join11(this.dir, file), "utf-8"));
4831
+ const raw = JSON.parse(await readFile11(join12(this.dir, file), "utf-8"));
4612
4832
  if (raw.contextId === contextId) {
4613
4833
  await unlink2(raw.filePath).catch(() => {
4614
4834
  });
@@ -4623,14 +4843,14 @@ var ImageStorageService = class {
4623
4843
  async readImageAsBase64(imageId) {
4624
4844
  const result = await this.getImagePath(imageId);
4625
4845
  if (!result) return null;
4626
- const buffer = await readFile10(result.filePath);
4846
+ const buffer = await readFile11(result.filePath);
4627
4847
  return { base64: buffer.toString("base64"), mimeType: result.mimeType };
4628
4848
  }
4629
4849
  async listExpiredImages(ttlMs = IMAGE_TTL_MS) {
4630
4850
  const expired = [];
4631
4851
  let files;
4632
4852
  try {
4633
- files = await readdir2(this.dir);
4853
+ files = await readdir3(this.dir);
4634
4854
  } catch {
4635
4855
  return [];
4636
4856
  }
@@ -4638,7 +4858,7 @@ var ImageStorageService = class {
4638
4858
  for (const file of files) {
4639
4859
  if (!file.endsWith(".meta.json")) continue;
4640
4860
  try {
4641
- const raw = JSON.parse(await readFile10(join11(this.dir, file), "utf-8"));
4861
+ const raw = JSON.parse(await readFile11(join12(this.dir, file), "utf-8"));
4642
4862
  if (now - raw.createdAt > ttlMs) expired.push(raw);
4643
4863
  } catch {
4644
4864
  }
@@ -4698,9 +4918,6 @@ async function resolveImagesToBase64(images) {
4698
4918
  );
4699
4919
  return results.filter((r) => r !== null);
4700
4920
  }
4701
- function buildApiDocs(apiBaseUrl, apiToken) {
4702
- return buildPromptDocs(apiBaseUrl, apiToken, RUNEYA_CAPABILITIES);
4703
- }
4704
4921
  function parseEndToken(output) {
4705
4922
  const tail = output.trimEnd().slice(-2e3);
4706
4923
  const branchMatch = tail.match(/<pass\s+branch="(\d+)"\s*\/>/);
@@ -4732,8 +4949,297 @@ var TOKEN_INSTRUCTIONS = `
4732
4949
 
4733
4950
  Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9ponse.
4734
4951
  `.trim();
4952
+ async function waitForFile(sessionId, signal, timeoutMs) {
4953
+ const start = Date.now();
4954
+ while (Date.now() - start < timeoutMs) {
4955
+ if (signal.aborted) throw new Error("Aborted");
4956
+ const path = await findClaudeSessionFile(sessionId);
4957
+ if (path) return path;
4958
+ await new Promise((r) => setTimeout(r, 150));
4959
+ }
4960
+ throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);
4961
+ }
4962
+ function tailTranscript(opts) {
4963
+ const { sessionId, signal, onEntry, fileAppearTimeoutMs = 3e4 } = opts;
4964
+ let stopped = false;
4965
+ let watcher = null;
4966
+ let fh = null;
4967
+ let offset = 0;
4968
+ let pendingBuf = "";
4969
+ let reading = false;
4970
+ let queuedRead = false;
4971
+ const cleanup = async () => {
4972
+ stopped = true;
4973
+ if (watcher) {
4974
+ try {
4975
+ watcher.close();
4976
+ } catch {
4977
+ }
4978
+ watcher = null;
4979
+ }
4980
+ if (fh) {
4981
+ try {
4982
+ await fh.close();
4983
+ } catch {
4984
+ }
4985
+ fh = null;
4986
+ }
4987
+ };
4988
+ const drainBuffer = () => {
4989
+ let idx;
4990
+ while ((idx = pendingBuf.indexOf("\n")) >= 0) {
4991
+ const line = pendingBuf.slice(0, idx).trim();
4992
+ pendingBuf = pendingBuf.slice(idx + 1);
4993
+ if (!line) continue;
4994
+ try {
4995
+ const entry = JSON.parse(line);
4996
+ onEntry(entry);
4997
+ } catch {
4998
+ }
4999
+ }
5000
+ };
5001
+ const readMore = async () => {
5002
+ if (!fh || stopped) return;
5003
+ if (reading) {
5004
+ queuedRead = true;
5005
+ return;
5006
+ }
5007
+ reading = true;
5008
+ try {
5009
+ while (!stopped) {
5010
+ const st = await fh.stat();
5011
+ if (st.size <= offset) break;
5012
+ const len = st.size - offset;
5013
+ const buf = Buffer.alloc(len);
5014
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
5015
+ if (bytesRead <= 0) break;
5016
+ offset += bytesRead;
5017
+ pendingBuf += buf.slice(0, bytesRead).toString("utf-8");
5018
+ drainBuffer();
5019
+ }
5020
+ } finally {
5021
+ reading = false;
5022
+ if (queuedRead && !stopped) {
5023
+ queuedRead = false;
5024
+ void readMore();
5025
+ }
5026
+ }
5027
+ };
5028
+ const whenDone = (async () => {
5029
+ const onAbort = () => {
5030
+ void cleanup();
5031
+ };
5032
+ signal.addEventListener("abort", onAbort, { once: true });
5033
+ try {
5034
+ const path = await waitForFile(sessionId, signal, fileAppearTimeoutMs);
5035
+ if (stopped) return;
5036
+ fh = await open(path, "r");
5037
+ await readMore();
5038
+ if (stopped) return;
5039
+ watcher = watch2(path, { persistent: false }, () => {
5040
+ void readMore();
5041
+ });
5042
+ watcher.on("error", () => {
5043
+ });
5044
+ const poll = setInterval(() => {
5045
+ void readMore();
5046
+ }, 500);
5047
+ await new Promise((resolve5) => {
5048
+ const tick = setInterval(() => {
5049
+ if (stopped) {
5050
+ clearInterval(tick);
5051
+ clearInterval(poll);
5052
+ resolve5();
5053
+ }
5054
+ }, 100);
5055
+ });
5056
+ } finally {
5057
+ signal.removeEventListener("abort", onAbort);
5058
+ await cleanup();
5059
+ }
5060
+ })();
5061
+ return {
5062
+ stop: () => {
5063
+ void cleanup();
5064
+ },
5065
+ whenDone
5066
+ };
5067
+ }
4735
5068
  async function getAgentManager() {
4736
- const { agentManager: agentManager2 } = await import("./agent-manager-UP3PJ35X-LO6B3XKF.js");
5069
+ const { agentManager: agentManager2 } = await import("./agent-manager-V47DLMYP-26VYXTSG.js");
5070
+ return agentManager2;
5071
+ }
5072
+ function buildPtyArgs(params, sessionId, opts) {
5073
+ const { isResume, systemContext, apiBaseUrl, apiToken, model, mcpConfig, skipTokenInstructions, prompt } = params;
5074
+ const sessionArg = isResume ? ["--resume", sessionId] : ["--session-id", sessionId];
5075
+ const apiDocs = buildApiDocs(apiBaseUrl, apiToken);
5076
+ const fullSystemContext = apiDocs ? `${systemContext}
5077
+
5078
+ ${apiDocs}` : systemContext;
5079
+ const systemPrompt = skipTokenInstructions ? fullSystemContext : `${fullSystemContext}
5080
+
5081
+ ${TOKEN_INSTRUCTIONS}`;
5082
+ const mcpArgs = mcpConfig && !opts.excludeMcp ? ["--mcp-config", mcpConfig] : [];
5083
+ const args = [
5084
+ ...sessionArg,
5085
+ "--dangerously-skip-permissions",
5086
+ "--model",
5087
+ model,
5088
+ "--append-system-prompt",
5089
+ systemPrompt,
5090
+ ...mcpArgs,
5091
+ prompt
5092
+ ];
5093
+ return args;
5094
+ }
5095
+ function dispatchEntry(entry, state, cbs) {
5096
+ if (entry.type !== "user" && entry.type !== "assistant") return;
5097
+ const msg = entry.message;
5098
+ if (!msg) return;
5099
+ const role = msg.role ?? entry.type;
5100
+ const content = msg.content;
5101
+ if (typeof content === "string") {
5102
+ if (role === "assistant" && content) {
5103
+ state.assistantText += content;
5104
+ cbs.onStatus("");
5105
+ cbs.onDelta(content);
5106
+ }
5107
+ return;
5108
+ }
5109
+ if (!Array.isArray(content)) return;
5110
+ for (const raw of content) {
5111
+ if (!raw || typeof raw !== "object") continue;
5112
+ const block = raw;
5113
+ if (block.type === "text" && typeof block.text === "string" && role === "assistant") {
5114
+ state.assistantText += block.text;
5115
+ cbs.onStatus("");
5116
+ cbs.onDelta(block.text);
5117
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
5118
+ cbs.onStatus("\u{1F4AD} En r\xE9flexion\u2026");
5119
+ cbs.onThinking?.(block.thinking);
5120
+ } else if (block.type === "tool_use" && block.id) {
5121
+ const name = block.name ?? "tool";
5122
+ state.tools.set(block.id, { id: block.id, name });
5123
+ cbs.onStatus(`\u{1F527} ${name}`);
5124
+ cbs.onToolCall?.(block.id, name, block.input ?? {});
5125
+ } else if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
5126
+ const c = block.content;
5127
+ let result = "";
5128
+ if (typeof c === "string") result = c;
5129
+ else if (Array.isArray(c)) {
5130
+ result = c.filter((b) => b["type"] === "text").map((b) => b["text"] ?? "").join("");
5131
+ }
5132
+ cbs.onToolResult?.(block.tool_use_id, result, block.is_error === true);
5133
+ }
5134
+ }
5135
+ }
5136
+ async function runClaudeViaPty(params) {
5137
+ const { agentId, signal, skipTokenInstructions } = params;
5138
+ if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
5139
+ const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID5();
5140
+ const args = buildPtyArgs(params, sessionId, { excludeMcp: true });
5141
+ const agentMgr = await getAgentManager();
5142
+ const displayArgs = args.map((a) => {
5143
+ if (a === params.prompt) return "<prompt>";
5144
+ if (a.includes(TOKEN_INSTRUCTIONS)) return "<system-context>";
5145
+ return a;
5146
+ });
5147
+ params.onCommand?.(`claude ${displayArgs.join(" ")}`);
5148
+ return new Promise((resolve5, reject) => {
5149
+ let settled = false;
5150
+ let spawnId = null;
5151
+ const state = { assistantText: "", tools: /* @__PURE__ */ new Map() };
5152
+ const innerAbort = new AbortController();
5153
+ const onParentAbort = () => innerAbort.abort();
5154
+ signal.addEventListener("abort", onParentAbort, { once: true });
5155
+ const cleanup = () => {
5156
+ signal.removeEventListener("abort", onParentAbort);
5157
+ innerAbort.abort();
5158
+ };
5159
+ const killSpawn = async () => {
5160
+ if (!spawnId) return;
5161
+ try {
5162
+ await agentMgr.agentMutation(agentId, "ai.kill", { spawnId });
5163
+ } catch {
5164
+ }
5165
+ };
5166
+ const settle = (result) => {
5167
+ if (settled) return;
5168
+ settled = true;
5169
+ cleanup();
5170
+ void killSpawn();
5171
+ resolve5(result);
5172
+ };
5173
+ const fail = (err) => {
5174
+ if (settled) return;
5175
+ settled = true;
5176
+ cleanup();
5177
+ void killSpawn();
5178
+ reject(err);
5179
+ };
5180
+ const tail = tailTranscript({
5181
+ sessionId,
5182
+ signal: innerAbort.signal,
5183
+ onEntry: (entry) => {
5184
+ try {
5185
+ dispatchEntry(entry, state, {
5186
+ onDelta: params.onDelta,
5187
+ onStatus: params.onStatus,
5188
+ onThinking: params.onThinking,
5189
+ onToolCall: params.onToolCall,
5190
+ onToolResult: params.onToolResult
5191
+ });
5192
+ } catch {
5193
+ }
5194
+ if (!skipTokenInstructions) {
5195
+ const { status, message, branchChoice } = parseEndToken(state.assistantText);
5196
+ if (status !== "none") {
5197
+ settle({
5198
+ output: state.assistantText,
5199
+ tokenStatus: status,
5200
+ tokenMessage: message,
5201
+ branchChoice,
5202
+ actualSessionId: sessionId
5203
+ });
5204
+ }
5205
+ }
5206
+ }
5207
+ });
5208
+ agentMgr.agentMutation(agentId, "ai.spawnPty", {
5209
+ cli: "claude",
5210
+ args,
5211
+ cols: 120,
5212
+ rows: 30,
5213
+ sessionId,
5214
+ enableMcp: !!params.mcpConfig,
5215
+ mcpApiBaseUrl: params.apiBaseUrl || void 0
5216
+ }).then(({ spawnId: id }) => {
5217
+ spawnId = id;
5218
+ if (signal.aborted) {
5219
+ fail(new Error("Aborted"));
5220
+ return;
5221
+ }
5222
+ }).catch((err) => {
5223
+ fail(new Error(`Failed to spawn claude PTY: ${err.message}`));
5224
+ });
5225
+ signal.addEventListener("abort", () => fail(new Error("Aborted")), { once: true });
5226
+ void tail.whenDone.then(() => {
5227
+ if (settled) return;
5228
+ if (skipTokenInstructions) {
5229
+ settle({
5230
+ output: state.assistantText,
5231
+ tokenStatus: "none",
5232
+ tokenMessage: "",
5233
+ actualSessionId: sessionId
5234
+ });
5235
+ } else {
5236
+ fail(new Error("Claude PTY ended before producing an end token"));
5237
+ }
5238
+ });
5239
+ });
5240
+ }
5241
+ async function getAgentManager2() {
5242
+ const { agentManager: agentManager2 } = await import("./agent-manager-V47DLMYP-26VYXTSG.js");
4737
5243
  return agentManager2;
4738
5244
  }
4739
5245
  function createClaudeStreamState() {
@@ -4957,6 +5463,10 @@ function processClaudeStdoutLines(lines, streamState, params, ctx) {
4957
5463
  }
4958
5464
  var ClaudeCodeRunner = class {
4959
5465
  run(params) {
5466
+ const ptyEnabled = params.usePty ?? process.env["RUNEYA_CLAUDE_PTY"] === "1";
5467
+ if (ptyEnabled && params.agentId && !(params.images && params.images.length > 0)) {
5468
+ return runClaudeViaPty(params);
5469
+ }
4960
5470
  if (params.agentId) {
4961
5471
  return this.runViaAgent(params);
4962
5472
  }
@@ -4966,7 +5476,7 @@ var ClaudeCodeRunner = class {
4966
5476
  async runViaAgent(params) {
4967
5477
  const { agentId, signal, skipTokenInstructions } = params;
4968
5478
  const { args, env: env2, stdinData, sessionArg } = buildClaudeSpawnConfig(params, { excludeMcp: true });
4969
- const agentMgr = await getAgentManager();
5479
+ const agentMgr = await getAgentManager2();
4970
5480
  const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
4971
5481
  console.log(`[claude-code-runner] [agent:${agentId}] arg size: ${totalArgSize} bytes, session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
4972
5482
  const displayArgs = args.map((a) => {
@@ -5301,8 +5811,8 @@ async function shutdownCodexAppServer() {
5301
5811
  await client.shutdown();
5302
5812
  }
5303
5813
  }
5304
- async function getAgentManager2() {
5305
- const { agentManager: agentManager2 } = await import("./agent-manager-UP3PJ35X-LO6B3XKF.js");
5814
+ async function getAgentManager3() {
5815
+ const { agentManager: agentManager2 } = await import("./agent-manager-V47DLMYP-26VYXTSG.js");
5306
5816
  return agentManager2;
5307
5817
  }
5308
5818
  function parseRuneyaCurlCommand(command) {
@@ -5371,7 +5881,7 @@ var CodexRunner = class {
5371
5881
  // ── Run via agent (app-server JSON-RPC via agent tRPC routes) ───────────
5372
5882
  async runViaAgent(params) {
5373
5883
  const { agentId, prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken } = params;
5374
- const agentMgr = await getAgentManager2();
5884
+ const agentMgr = await getAgentManager3();
5375
5885
  if (isResume && params.sessionId && !this.lastThreadId) {
5376
5886
  this.lastThreadId = params.sessionId;
5377
5887
  }
@@ -6634,6 +7144,325 @@ ${input.prompt}` : input.prompt;
6634
7144
  };
6635
7145
  runStream();
6636
7146
  yield* drainSession(session, signal, false);
7147
+ }),
7148
+ // ── PTY passthrough (interactive Claude Code session) ────────────────────
7149
+ pty: router({
7150
+ /** Start a PTY session running `claude --resume <sessionId>` (or fresh). */
7151
+ start: protectedProcedure.input(z14.object({
7152
+ agentId: z14.string().max(128).optional(),
7153
+ sessionId: z14.string().max(128).optional(),
7154
+ resume: z14.boolean().default(false),
7155
+ model: z14.string().max(128).optional(),
7156
+ cwd: z14.string().max(4096).optional(),
7157
+ cols: z14.number().int().min(1).max(1e3).default(120),
7158
+ rows: z14.number().int().min(1).max(1e3).default(30),
7159
+ enableMcp: z14.boolean().default(false),
7160
+ apiBaseUrl: z14.string().max(512).optional(),
7161
+ attachedContexts: z14.array(z14.object({
7162
+ label: z14.string().max(200),
7163
+ text: z14.string().max(1e6)
7164
+ })).max(20).optional()
7165
+ })).mutation(async ({ input, ctx }) => {
7166
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7167
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7168
+ if (input.sessionId) {
7169
+ try {
7170
+ const existing = await agentManager.agentQuery(
7171
+ agentId,
7172
+ "ai.findPtyBySession",
7173
+ { sessionId: input.sessionId }
7174
+ );
7175
+ if (existing && existing.status === "running") {
7176
+ await agentManager.agentMutation(agentId, "ai.resizePty", {
7177
+ spawnId: existing.spawnId,
7178
+ cols: input.cols,
7179
+ rows: input.rows
7180
+ }).catch(() => {
7181
+ });
7182
+ return { spawnId: existing.spawnId, agentId, reattached: true };
7183
+ }
7184
+ } catch {
7185
+ }
7186
+ }
7187
+ const args = [];
7188
+ if (input.sessionId) {
7189
+ const sessionExists = await claudeSessionExists(input.sessionId);
7190
+ if (input.resume || sessionExists) {
7191
+ args.push("--resume", input.sessionId);
7192
+ } else {
7193
+ args.push("--session-id", input.sessionId);
7194
+ }
7195
+ }
7196
+ if (input.model) args.push("--model", input.model);
7197
+ args.push("--dangerously-skip-permissions");
7198
+ const jwt = ctx.authorization?.startsWith("Bearer ") ? ctx.authorization.slice(7) : void 0;
7199
+ if (input.sessionId) {
7200
+ try {
7201
+ const conv = await conversationStore.get(input.sessionId);
7202
+ if (conv) {
7203
+ const systemContext = await buildCliSystemContext(conv, jwt);
7204
+ const apiBase = input.apiBaseUrl ?? `http://localhost:${env.PORT}`;
7205
+ const apiDocs = buildApiDocs(apiBase, jwt ?? "");
7206
+ const fullSystemPrompt = apiDocs ? `${systemContext}
7207
+
7208
+ ${apiDocs}` : systemContext;
7209
+ if (fullSystemPrompt.trim().length > 0) {
7210
+ args.push("--append-system-prompt", fullSystemPrompt);
7211
+ }
7212
+ }
7213
+ } catch (err) {
7214
+ console.warn("[chat.pty.start] buildCliSystemContext failed:", err.message);
7215
+ }
7216
+ }
7217
+ const res = await agentManager.agentMutation(
7218
+ agentId,
7219
+ "ai.spawnPty",
7220
+ {
7221
+ cli: "claude",
7222
+ args,
7223
+ cwd: input.cwd,
7224
+ cols: input.cols,
7225
+ rows: input.rows,
7226
+ sessionId: input.sessionId,
7227
+ enableMcp: input.enableMcp,
7228
+ mcpApiBaseUrl: input.apiBaseUrl ?? `http://localhost:${env.PORT}`,
7229
+ mcpApiToken: jwt
7230
+ }
7231
+ );
7232
+ const contexts = input.attachedContexts ?? [];
7233
+ if (contexts.length > 0) {
7234
+ const body = contexts.map((c) => `### ${c.label}
7235
+ ${c.text}`).join("\n\n");
7236
+ const injection = `\x1B[200~---------
7237
+ Context:
7238
+ \x1B[201~\x1B[200~${body}\x1B[201~\x1B[200~
7239
+ ---------
7240
+ \x1B[201~`;
7241
+ setTimeout(() => {
7242
+ agentManager.agentMutation(agentId, "ai.writePty", { spawnId: res.spawnId, data: injection }).catch((err) => {
7243
+ console.warn("[chat.pty.start] deferred context writePty failed:", err.message);
7244
+ });
7245
+ }, 500);
7246
+ }
7247
+ return { spawnId: res.spawnId, agentId, reattached: false };
7248
+ }),
7249
+ /** Subscribe to PTY output stream (raw text with ANSI). */
7250
+ stream: protectedProcedure.input(z14.object({
7251
+ agentId: z14.string().max(128).optional(),
7252
+ spawnId: z14.string().max(128),
7253
+ fromOffset: z14.number().int().min(0).default(0)
7254
+ })).subscription(async function* ({ input, signal }) {
7255
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7256
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7257
+ const queue = [];
7258
+ let resolve5 = null;
7259
+ let stopped = false;
7260
+ const unsub = agentManager.agentSubscribe(
7261
+ agentId,
7262
+ "ai.stream",
7263
+ { spawnId: input.spawnId, fromOffset: input.fromOffset },
7264
+ (data) => {
7265
+ queue.push(data);
7266
+ if (resolve5) {
7267
+ resolve5();
7268
+ resolve5 = null;
7269
+ }
7270
+ },
7271
+ () => {
7272
+ stopped = true;
7273
+ if (resolve5) {
7274
+ resolve5();
7275
+ resolve5 = null;
7276
+ }
7277
+ }
7278
+ );
7279
+ try {
7280
+ while (!signal?.aborted && !stopped) {
7281
+ while (queue.length > 0) {
7282
+ const item = queue.shift();
7283
+ yield item;
7284
+ if (item.chunk.type === "exit") {
7285
+ stopped = true;
7286
+ break;
7287
+ }
7288
+ }
7289
+ if (stopped) break;
7290
+ await new Promise((r) => {
7291
+ resolve5 = r;
7292
+ });
7293
+ }
7294
+ } finally {
7295
+ unsub();
7296
+ }
7297
+ }),
7298
+ /** Write input to a PTY session (keystrokes from xterm). */
7299
+ write: protectedProcedure.input(z14.object({
7300
+ agentId: z14.string().max(128).optional(),
7301
+ spawnId: z14.string().max(128),
7302
+ data: z14.string().max(1e6)
7303
+ })).mutation(async ({ input }) => {
7304
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7305
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7306
+ return agentManager.agentMutation(agentId, "ai.writePty", {
7307
+ spawnId: input.spawnId,
7308
+ data: input.data
7309
+ });
7310
+ }),
7311
+ /** Resize a PTY session. */
7312
+ resize: protectedProcedure.input(z14.object({
7313
+ agentId: z14.string().max(128).optional(),
7314
+ spawnId: z14.string().max(128),
7315
+ cols: z14.number().int().min(1).max(1e3),
7316
+ rows: z14.number().int().min(1).max(1e3)
7317
+ })).mutation(async ({ input }) => {
7318
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7319
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7320
+ return agentManager.agentMutation(agentId, "ai.resizePty", {
7321
+ spawnId: input.spawnId,
7322
+ cols: input.cols,
7323
+ rows: input.rows
7324
+ });
7325
+ }),
7326
+ /**
7327
+ * Upload a pasted image to the agent. The agent saves it to a tmp file and
7328
+ * tries to populate its system clipboard so Claude's native paste detection
7329
+ * picks it up. Returns the tmp path and which clipboard method succeeded
7330
+ * (null if none — caller should inject `@<path>` instead of Ctrl+V).
7331
+ */
7332
+ pasteImage: protectedProcedure.input(z14.object({
7333
+ agentId: z14.string().max(128).optional(),
7334
+ sessionId: z14.string().max(128).optional(),
7335
+ mimeType: z14.string().max(64),
7336
+ base64: z14.string().max(15e6)
7337
+ })).mutation(async ({ input }) => {
7338
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7339
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7340
+ const res = await agentManager.agentMutation(agentId, "ai.pasteImage", {
7341
+ sessionId: input.sessionId,
7342
+ mimeType: input.mimeType,
7343
+ base64: input.base64
7344
+ });
7345
+ if (!res.clipboardMethod) {
7346
+ console.warn("[chat.pty.pasteImage] clipboard spoof failed, using @path fallback:", res.clipboardError);
7347
+ }
7348
+ return res;
7349
+ }),
7350
+ /**
7351
+ * Read the transcript Claude Code stores on disk for a given session.
7352
+ * Returns parsed JSONL entries (user / assistant messages, tool uses, etc.).
7353
+ */
7354
+ transcript: protectedProcedure.input(z14.object({ sessionId: z14.string().max(128) })).query(async ({ input }) => {
7355
+ return readClaudeTranscript(input.sessionId);
7356
+ }),
7357
+ /**
7358
+ * Lightweight liveness probe: is a PTY running for this session, and
7359
+ * when was the transcript JSONL last touched? Used by the non-PTY view
7360
+ * to show a "Claude is thinking…" indicator while output streams.
7361
+ */
7362
+ activity: protectedProcedure.input(z14.object({
7363
+ agentId: z14.string().max(128).optional(),
7364
+ sessionId: z14.string().max(128)
7365
+ })).query(async ({ input }) => {
7366
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7367
+ let running = false;
7368
+ let lastChunkAt = null;
7369
+ if (agentId) {
7370
+ try {
7371
+ const existing = await agentManager.agentQuery(
7372
+ agentId,
7373
+ "ai.findPtyBySession",
7374
+ { sessionId: input.sessionId }
7375
+ );
7376
+ running = existing?.status === "running";
7377
+ lastChunkAt = existing?.lastChunkAt ?? null;
7378
+ } catch {
7379
+ }
7380
+ }
7381
+ const transcriptActivityAt = getLastTranscriptActivity(input.sessionId);
7382
+ const lastActivityAt = lastChunkAt && transcriptActivityAt ? Math.max(lastChunkAt, transcriptActivityAt) : lastChunkAt ?? transcriptActivityAt;
7383
+ return { running, lastActivityAt };
7384
+ }),
7385
+ /**
7386
+ * Convert the on-disk Claude transcript into Runeya Message[] and persist
7387
+ * them on the conversation, so the non-PTY chat view stays in sync.
7388
+ * Uses the conversation id as session id (PTY sessions are spawned that way).
7389
+ */
7390
+ syncTranscript: protectedProcedure.input(z14.object({ conversationId: ConversationIdSchema })).mutation(async ({ input }) => {
7391
+ const conv = await conversationStore.get(input.conversationId);
7392
+ if (!conv) {
7393
+ throw new TRPCError10({ code: "NOT_FOUND", message: `Conversation ${input.conversationId} not found` });
7394
+ }
7395
+ const synced = await syncConversationFromClaudeTranscript(conv.id);
7396
+ const after = await conversationStore.get(conv.id);
7397
+ return { synced, messageCount: after?.messages.length ?? 0 };
7398
+ }),
7399
+ /**
7400
+ * Debug-only: spawn a PTY that emits random data at a chosen rate, tagged
7401
+ * with the given sessionId. Used to benchmark the PTY → tRPC → xterm.js
7402
+ * pipeline. Open the conversation in PTY mode to attach to it.
7403
+ */
7404
+ debugStress: protectedProcedure.input(z14.object({
7405
+ sessionId: z14.string().max(128),
7406
+ agentId: z14.string().max(128).optional(),
7407
+ bytesPerSec: z14.number().int().min(100).max(1e7).default(5e4),
7408
+ chunkSize: z14.number().int().min(1).max(65536).default(256),
7409
+ durationMs: z14.number().int().min(0).max(36e5).default(0),
7410
+ cols: z14.number().int().min(1).max(1e3).default(120),
7411
+ rows: z14.number().int().min(1).max(1e3).default(30)
7412
+ })).mutation(async ({ input }) => {
7413
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7414
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7415
+ const stressSessionId = `${input.sessionId}-stress-${Date.now().toString(36)}`;
7416
+ const { existsSync: existsSync3 } = await import("fs");
7417
+ const candidates = [
7418
+ new URL("../../../../scripts/pty-stress-gen.mjs", import.meta.url),
7419
+ new URL("../../../../../scripts/pty-stress-gen.mjs", import.meta.url),
7420
+ new URL("../../../scripts/pty-stress-gen.mjs", import.meta.url)
7421
+ ];
7422
+ let scriptPath = null;
7423
+ for (const c of candidates) {
7424
+ const p = fileURLToPath(c);
7425
+ if (existsSync3(p)) {
7426
+ scriptPath = p;
7427
+ break;
7428
+ }
7429
+ }
7430
+ if (!scriptPath) {
7431
+ throw new TRPCError10({
7432
+ code: "INTERNAL_SERVER_ERROR",
7433
+ message: `pty-stress-gen.mjs not found near ${fileURLToPath(import.meta.url)}`
7434
+ });
7435
+ }
7436
+ const res = await agentManager.agentMutation(
7437
+ agentId,
7438
+ "ai.spawnPty",
7439
+ {
7440
+ cli: "node",
7441
+ args: [scriptPath],
7442
+ cwd: process.cwd(),
7443
+ cols: input.cols,
7444
+ rows: input.rows,
7445
+ sessionId: stressSessionId,
7446
+ env: {
7447
+ BYTES_PER_SEC: String(input.bytesPerSec),
7448
+ CHUNK_SIZE: String(input.chunkSize),
7449
+ DURATION_MS: String(input.durationMs)
7450
+ }
7451
+ }
7452
+ );
7453
+ return { spawnId: res.spawnId, agentId, stressSessionId };
7454
+ }),
7455
+ /** Kill a PTY session. */
7456
+ kill: protectedProcedure.input(z14.object({
7457
+ agentId: z14.string().max(128).optional(),
7458
+ spawnId: z14.string().max(128)
7459
+ })).mutation(async ({ input }) => {
7460
+ const agentId = input.agentId ?? agentManager.getDefaultAgentId();
7461
+ if (!agentId) throw new TRPCError10({ code: "PRECONDITION_FAILED", message: "No agent available" });
7462
+ return agentManager.agentMutation(agentId, "ai.kill", {
7463
+ spawnId: input.spawnId
7464
+ });
7465
+ })
6637
7466
  })
6638
7467
  });
6639
7468
  var SERVICE_ID_SCHEMA = z15.string().max(128);
@@ -7266,7 +8095,7 @@ function applyHireDirectivesFromOutput(manager, scenario, output) {
7266
8095
  }
7267
8096
  const hired = [];
7268
8097
  for (const payload of hireDirectives) {
7269
- const id = `hire-${randomUUID5().slice(0, 8)}`;
8098
+ const id = `hire-${randomUUID6().slice(0, 8)}`;
7270
8099
  const position = computeHirePosition(manager, scenario.nodes);
7271
8100
  ensureGroupCanContainNode(scenario, manager.groupId ?? null, position);
7272
8101
  const node = {
@@ -7630,6 +8459,9 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
7630
8459
  model,
7631
8460
  effort: "normal",
7632
8461
  signal,
8462
+ // Scenarios default to interactive PTY to stay on subscription pricing.
8463
+ // Override with RUNEYA_CLAUDE_PTY=0 to fall back to headless `-p` mode.
8464
+ usePty: aiCli === "claude-code" && process.env["RUNEYA_CLAUDE_PTY"] !== "0",
7633
8465
  onDelta: (text) => {
7634
8466
  accumulated += text;
7635
8467
  onDelta(text);
@@ -7644,6 +8476,11 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
7644
8476
  flushPersist();
7645
8477
  if (result.actualSessionId) state.sessionId = result.actualSessionId;
7646
8478
  state.isResume = true;
8479
+ const agentForNode = runOverrides?.agentId ?? agentManager.getDefaultAgentId() ?? null;
8480
+ if (state.sessionId && agentForNode) {
8481
+ scenarioRunStore.setNodeSession(conversationId, node.id, state.sessionId, agentForNode);
8482
+ void scenarioRunStore.persistRunState(conversationId);
8483
+ }
7647
8484
  if (result.tokenStatus === "ask_user") {
7648
8485
  publish2({ type: "node_duration", nodeId: node.id, durationMs: Date.now() - turnStart });
7649
8486
  scenarioRunStore.setNodeState(conversationId, node.id, "waiting_input");
@@ -7694,7 +8531,7 @@ async function appendNodeOutput(conversationId, node, output) {
7694
8531
  if (!dir) return;
7695
8532
  const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
7696
8533
  const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
7697
- const filePath = join12(dir, `${slug}_${safeId}.md`);
8534
+ const filePath = join13(dir, `${slug}_${safeId}.md`);
7698
8535
  const content = `## Output
7699
8536
 
7700
8537
  ${output}
@@ -7709,7 +8546,7 @@ async function appendToReadme(conversationId, bus, text) {
7709
8546
  try {
7710
8547
  await mkdir12(dir, { recursive: true });
7711
8548
  const { appendFile: appendFile2 } = await import("fs/promises");
7712
- await appendFile2(join12(dir, "README.md"), line, "utf-8");
8549
+ await appendFile2(join13(dir, "README.md"), line, "utf-8");
7713
8550
  } catch {
7714
8551
  }
7715
8552
  publishToBus(bus, { type: "readme_append", text: line });
@@ -7729,7 +8566,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
7729
8566
 
7730
8567
  ${content}
7731
8568
  `;
7732
- const filePath = join12(dir, "FINAL.md");
8569
+ const filePath = join13(dir, "FINAL.md");
7733
8570
  await atomicWriteSecure(filePath, fullContent);
7734
8571
  await appendToReadme(
7735
8572
  conversationId,
@@ -8352,6 +9189,8 @@ ${out}`;
8352
9189
  ).catch(() => {
8353
9190
  });
8354
9191
  }
9192
+ await scenarioRunStore.persistRunState(conversationId).catch(() => {
9193
+ });
8355
9194
  publishToBus(bus, null);
8356
9195
  activeAbortControllers.delete(conversationId);
8357
9196
  }
@@ -8408,7 +9247,7 @@ var scenarioRouter = router({
8408
9247
  })).mutation(({ input }) => {
8409
9248
  const now = Date.now();
8410
9249
  return createOrBadRequest(() => scenarioStore.create({
8411
- id: randomUUID6(),
9250
+ id: randomUUID7(),
8412
9251
  name: input.name,
8413
9252
  icon: input.icon,
8414
9253
  description: input.description,
@@ -8529,6 +9368,17 @@ var scenarioRouter = router({
8529
9368
  }
8530
9369
  }
8531
9370
  }),
9371
+ /**
9372
+ * Returns the Claude Code session-id + agent-id for a node, so the UI can
9373
+ * open a debug PTY with `claude --resume <sessionId>` on the right agent.
9374
+ * Falls back to the persisted run-state for past nodes.
9375
+ */
9376
+ getNodeSession: protectedProcedure.input(z16.object({
9377
+ conversationId: ConversationIdSchema,
9378
+ nodeId: Id128Schema
9379
+ })).query(async ({ input }) => {
9380
+ return await scenarioRunStore.getNodeSessionPersistent(input.conversationId, input.nodeId);
9381
+ }),
8532
9382
  listPresets: protectedProcedure.query(() => listPresets()),
8533
9383
  getPreset: protectedProcedure.input(z16.object({ id: Id128Schema })).query(({ input }) => {
8534
9384
  const preset = getPreset(input.id);
@@ -8596,7 +9446,7 @@ var debugRouter = router({
8596
9446
  intervalMs: z17.number().int().min(100).max(5e3).optional(),
8597
9447
  maxTicks: z17.number().int().min(1).max(1e4).optional()
8598
9448
  }).optional()).mutation(({ input }) => {
8599
- const jobId = randomUUID7();
9449
+ const jobId = randomUUID8();
8600
9450
  const intervalMs = input?.intervalMs ?? 400;
8601
9451
  const maxTicks = input?.maxTicks ?? 500;
8602
9452
  const job = {
@@ -8782,7 +9632,7 @@ var KanbanQueueService = class extends EventEmitter3 {
8782
9632
  return { id: alreadyRunning.id, addedAt: alreadyRunning.startedAt, ...item };
8783
9633
  }
8784
9634
  const queueItem = {
8785
- id: randomUUID8(),
9635
+ id: randomUUID9(),
8786
9636
  ...item,
8787
9637
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
8788
9638
  };
@@ -9170,7 +10020,7 @@ var SENSITIVE_FILES = [
9170
10020
  var SECURE_MODE = 384;
9171
10021
  async function ensureFilePermissions() {
9172
10022
  for (const filename of SENSITIVE_FILES) {
9173
- const filepath = join13(env.DATA_DIR, filename);
10023
+ const filepath = join14(env.DATA_DIR, filename);
9174
10024
  try {
9175
10025
  const stats = await stat3(filepath);
9176
10026
  const mode = stats.mode & 511;
@@ -9218,7 +10068,7 @@ conversations.private.json
9218
10068
  *.log.*
9219
10069
  `;
9220
10070
  async function ensureDataGitignore(dataDir) {
9221
- const gitignorePath = join14(dataDir, ".gitignore");
10071
+ const gitignorePath = join15(dataDir, ".gitignore");
9222
10072
  await writeFile10(gitignorePath, GITIGNORE_CONTENT, "utf-8");
9223
10073
  }
9224
10074
  var _require = createRequire(import.meta.url);
@@ -9327,7 +10177,7 @@ async function getSodium() {
9327
10177
  return _sodium;
9328
10178
  }
9329
10179
  async function decryptLegacyFile(filePath, key, sodium) {
9330
- const raw = (await readFile11(filePath, "utf-8")).trim();
10180
+ const raw = (await readFile12(filePath, "utf-8")).trim();
9331
10181
  if (!raw || raw === "[]" || raw === "{}") {
9332
10182
  try {
9333
10183
  return JSON.parse(raw);
@@ -9353,29 +10203,29 @@ async function atomicWrite(path, data) {
9353
10203
  async function runLegacyMigration(dataDir) {
9354
10204
  const { resolve: resolve5 } = await import("path");
9355
10205
  const abCwd = resolve5(process.cwd());
9356
- const legacyRoot = join15(abCwd, env.DATA_DIR);
9357
- const legacyKeyPath = join15(legacyRoot, "dbs", "encryption-key.json");
10206
+ const legacyRoot = join16(abCwd, env.DATA_DIR);
10207
+ const legacyKeyPath = join16(legacyRoot, "dbs", "encryption-key.json");
9358
10208
  try {
9359
- await access(legacyKeyPath);
10209
+ await access2(legacyKeyPath);
9360
10210
  } catch {
9361
10211
  return false;
9362
10212
  }
9363
10213
  console.log("[legacy-migration] Legacy Runeya data detected, starting migration\u2026");
9364
10214
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
9365
- const legacyTmp = join15(abCwd, ".runeya-tmp");
10215
+ const legacyTmp = join16(abCwd, ".runeya-tmp");
9366
10216
  await rename9(legacyRoot, legacyTmp);
9367
10217
  await mkdir13(dataDir, { recursive: true });
9368
- const legacyOld = join15(dataDir, `backup-${timestamp}`);
10218
+ const legacyOld = join16(dataDir, `backup-${timestamp}`);
9369
10219
  await rename9(legacyTmp, legacyOld);
9370
10220
  console.log(`[legacy-migration] Legacy data backed up to ${legacyOld}`);
9371
- const keyJson = JSON.parse(await readFile11(join15(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
10221
+ const keyJson = JSON.parse(await readFile12(join16(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
9372
10222
  const sodium = await getSodium();
9373
10223
  const sodiumKey = sodium.from_base64(keyJson.encryptionKey);
9374
10224
  const keyBuffer = Buffer.from(sodiumKey);
9375
10225
  const runyeaKeyHex = keyBuffer.toString("hex");
9376
10226
  await setEncryptionKey(runyeaKeyHex);
9377
10227
  console.log("[legacy-migration] Encryption key loaded in memory (temporary)");
9378
- const dbsDir = join15(legacyOld, "dbs");
10228
+ const dbsDir = join16(legacyOld, "dbs");
9379
10229
  async function readEncrypted(filePath) {
9380
10230
  try {
9381
10231
  return await decryptLegacyFile(filePath, sodiumKey, sodium);
@@ -9384,29 +10234,29 @@ async function runLegacyMigration(dataDir) {
9384
10234
  return null;
9385
10235
  }
9386
10236
  }
9387
- const envsDir = join15(dbsDir, "envs");
9388
- const envFiles = (await readdir3(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10237
+ const envsDir = join16(dbsDir, "envs");
10238
+ const envFiles = (await readdir4(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9389
10239
  const legacyEnvs = {};
9390
10240
  for (const file of envFiles) {
9391
10241
  const name = file.replace(".encrypted.json", "");
9392
- legacyEnvs[name] = await readEncrypted(join15(envsDir, file));
10242
+ legacyEnvs[name] = await readEncrypted(join16(envsDir, file));
9393
10243
  }
9394
- const servicesDir = join15(dbsDir, "services");
9395
- const serviceFiles = (await readdir3(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10244
+ const servicesDir = join16(dbsDir, "services");
10245
+ const serviceFiles = (await readdir4(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9396
10246
  const legacyServices = {};
9397
10247
  for (const file of serviceFiles) {
9398
10248
  const name = file.replace(".encrypted.json", "");
9399
10249
  if (!name) continue;
9400
- const data = await readEncrypted(join15(servicesDir, file));
10250
+ const data = await readEncrypted(join16(servicesDir, file));
9401
10251
  if (data?.label) legacyServices[name] = data;
9402
10252
  }
9403
- const overridesDir = join15(dbsDir, "overrides");
9404
- const overrideFiles = (await readdir3(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10253
+ const overridesDir = join16(dbsDir, "overrides");
10254
+ const overrideFiles = (await readdir4(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9405
10255
  const legacyServiceOverrides = {};
9406
10256
  const legacyEnvOverrides = {};
9407
10257
  for (const file of overrideFiles) {
9408
10258
  const baseName = file.replace(".encrypted.json", "");
9409
- const data = await readEncrypted(join15(overridesDir, file));
10259
+ const data = await readEncrypted(join16(overridesDir, file));
9410
10260
  if (!data) continue;
9411
10261
  if (baseName.endsWith("-envs")) {
9412
10262
  legacyServiceOverrides[baseName.slice(0, -5)] = data;
@@ -9414,18 +10264,18 @@ async function runLegacyMigration(dataDir) {
9414
10264
  legacyEnvOverrides[baseName.slice(0, -12)] = data;
9415
10265
  }
9416
10266
  }
9417
- const parsersDir = join15(dbsDir, "parsers");
9418
- const parserFiles = (await readdir3(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
10267
+ const parsersDir = join16(dbsDir, "parsers");
10268
+ const parserFiles = (await readdir4(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
9419
10269
  const legacyParsers = [];
9420
10270
  for (const file of parserFiles) {
9421
- const data = await readEncrypted(join15(parsersDir, file));
10271
+ const data = await readEncrypted(join16(parsersDir, file));
9422
10272
  if (data?.id) legacyParsers.push(data);
9423
10273
  }
9424
- const projectMonoId = randomUUID9();
10274
+ const projectMonoId = randomUUID10();
9425
10275
  const envIdMap = {};
9426
10276
  const labelToKey = {};
9427
10277
  for (const [name, legacyEnv] of Object.entries(legacyEnvs)) {
9428
- envIdMap[name] = randomUUID9();
10278
+ envIdMap[name] = randomUUID10();
9429
10279
  if (legacyEnv?.label) labelToKey[legacyEnv.label] = name;
9430
10280
  }
9431
10281
  const runeEnvs = [];
@@ -9450,7 +10300,7 @@ async function runLegacyMigration(dataDir) {
9450
10300
  if (hasInvalidInterpolation(value)) continue;
9451
10301
  const cleaned = cleanVarKey(key);
9452
10302
  if (inheritedVars[cleaned] === value) continue;
9453
- const varId = randomUUID9();
10303
+ const varId = randomUUID10();
9454
10304
  variables[varId] = makeVariable(cleaned, value);
9455
10305
  varIdLookup.set(`${envIdMap[name]}:${cleaned}`, varId);
9456
10306
  }
@@ -9471,7 +10321,7 @@ async function runLegacyMigration(dataDir) {
9471
10321
  const serviceIdMap = {};
9472
10322
  const runeServices = [];
9473
10323
  for (const [name, legacy] of Object.entries(legacyServices)) {
9474
- const id = randomUUID9();
10324
+ const id = randomUUID10();
9475
10325
  serviceIdMap[name] = id;
9476
10326
  const rootPath = legacy.rootPath || "";
9477
10327
  const rootPathCwd = rootPath && rootPath !== "." ? rootPath : "";
@@ -9480,7 +10330,7 @@ async function runLegacyMigration(dataDir) {
9480
10330
  const parts = [cmd.spawnCmd, ...cmd.spawnArgs || []].filter(Boolean);
9481
10331
  if (!parts.length) continue;
9482
10332
  commands.push({
9483
- id: cmd.id || randomUUID9(),
10333
+ id: cmd.id || randomUUID10(),
9484
10334
  label: "Launch",
9485
10335
  command: parts.join(" "),
9486
10336
  cwd: cmd.spawnOptions?.cwd || "",
@@ -9541,7 +10391,7 @@ async function runLegacyMigration(dataDir) {
9541
10391
  const legacyBootstrapCmds = container.bootstrap?.commands || [];
9542
10392
  if (legacyBootstrapCmds.length > 0) {
9543
10393
  dockerConfig.bootstrapCommands = legacyBootstrapCmds.filter((bs) => bs.cmd || bs.entrypoint).map((bs, idx) => ({
9544
- id: bs.id || randomUUID9(),
10394
+ id: bs.id || randomUUID10(),
9545
10395
  label: bs.label || `Bootstrap ${idx + 1}`,
9546
10396
  command: bs.cmd || "",
9547
10397
  ...bs.entrypoint ? { entrypoint: bs.entrypoint } : {},
@@ -9589,7 +10439,7 @@ async function runLegacyMigration(dataDir) {
9589
10439
  cwd: serviceCwd,
9590
10440
  runner: dockerConfig ? "docker" : "native",
9591
10441
  shortcuts: (legacy.shortcuts || []).filter((s) => s.spawnCmd || s.label).map((s) => ({
9592
- id: s.id || randomUUID9(),
10442
+ id: s.id || randomUUID10(),
9593
10443
  label: s.label || s.spawnCmd || "",
9594
10444
  command: [s.spawnCmd, ...s.spawnArgs || []].filter(Boolean).join(" "),
9595
10445
  ...s.spawnOptions?.cwd ? { cwd: s.spawnOptions.cwd } : {}
@@ -9629,7 +10479,7 @@ async function runLegacyMigration(dataDir) {
9629
10479
  );
9630
10480
  if (!svcScopedEnv) {
9631
10481
  svcScopedEnv = {
9632
- id: randomUUID9(),
10482
+ id: randomUUID10(),
9633
10483
  projectId: projectMonoId,
9634
10484
  name: `${service?.name || name} (${globalEnv.name})`,
9635
10485
  scope: "service",
@@ -9653,7 +10503,7 @@ async function runLegacyMigration(dataDir) {
9653
10503
  const varPost = typeof varData === "object" ? String(varData.suffix ?? "") : "";
9654
10504
  if (!varKey || existingKeys.has(cleanVarKey(varKey))) continue;
9655
10505
  const cleaned = cleanVarKey(varKey);
9656
- const varId = randomUUID9();
10506
+ const varId = randomUUID10();
9657
10507
  svcScopedEnv.variables[varId] = makeVariable(cleaned, varValue, varPre, varPost);
9658
10508
  existingKeys.add(cleaned);
9659
10509
  }
@@ -9673,7 +10523,7 @@ async function runLegacyMigration(dataDir) {
9673
10523
  );
9674
10524
  if (!svcScopedEnv) {
9675
10525
  svcScopedEnv = {
9676
- id: randomUUID9(),
10526
+ id: randomUUID10(),
9677
10527
  projectId: projectMonoId,
9678
10528
  name: `${service?.name || serviceName} (${globalEnv.name})`,
9679
10529
  scope: "service",
@@ -9694,7 +10544,7 @@ async function runLegacyMigration(dataDir) {
9694
10544
  const baseEntry = Object.entries(svcScopedEnv.variables).find(([, v]) => v.key === cleaned);
9695
10545
  let varId = baseEntry?.[0];
9696
10546
  if (!varId) {
9697
- varId = randomUUID9();
10547
+ varId = randomUUID10();
9698
10548
  svcScopedEnv.variables[varId] = {
9699
10549
  key: cleaned,
9700
10550
  value: { value: "", pre: "", post: "", isSecret: false }
@@ -9724,13 +10574,13 @@ async function runLegacyMigration(dataDir) {
9724
10574
  flatOverrides[varId] = makeVariable(cleaned, value);
9725
10575
  }
9726
10576
  } else {
9727
- flatOverrides[randomUUID9()] = makeVariable(cleaned, value);
10577
+ flatOverrides[randomUUID10()] = makeVariable(cleaned, value);
9728
10578
  }
9729
10579
  } else {
9730
10580
  const cleaned = cleanVarKey(key);
9731
10581
  const existingVarId = Object.entries(env2.variables).find(([, v]) => v.key === cleaned)?.[0];
9732
10582
  if (!existingVarId) {
9733
- const varId = randomUUID9();
10583
+ const varId = randomUUID10();
9734
10584
  env2.variables[varId] = makeVariable(cleaned, value);
9735
10585
  varIdLookup.set(`${envId}:${cleaned}`, varId);
9736
10586
  }
@@ -9759,7 +10609,7 @@ async function runLegacyMigration(dataDir) {
9759
10609
  }
9760
10610
  let existingSettings = {};
9761
10611
  try {
9762
- existingSettings = JSON.parse(await readFile11(join15(dataDir, "settings.json"), "utf-8"));
10612
+ existingSettings = JSON.parse(await readFile12(join16(dataDir, "settings.json"), "utf-8"));
9763
10613
  } catch {
9764
10614
  }
9765
10615
  const migratedParsers = legacyParsers.map((p) => ({
@@ -9780,11 +10630,11 @@ ${p.transform ?? ""}`,
9780
10630
  ...migratedParsers
9781
10631
  ]
9782
10632
  };
9783
- await atomicWrite(join15(dataDir, "projects.json"), runeProjects);
9784
- await atomicWrite(join15(dataDir, "services.json"), runeServices);
9785
- await atomicWrite(join15(dataDir, "environments.json"), runeEnvs);
9786
- await atomicWrite(join15(dataDir, "environment-overrides.json"), flatOverrides);
9787
- await atomicWrite(join15(dataDir, "settings.json"), newSettings);
10633
+ await atomicWrite(join16(dataDir, "projects.json"), runeProjects);
10634
+ await atomicWrite(join16(dataDir, "services.json"), runeServices);
10635
+ await atomicWrite(join16(dataDir, "environments.json"), runeEnvs);
10636
+ await atomicWrite(join16(dataDir, "environment-overrides.json"), flatOverrides);
10637
+ await atomicWrite(join16(dataDir, "settings.json"), newSettings);
9788
10638
  const svcEnvCount = runeEnvs.filter((e) => e.scope === "service").length;
9789
10639
  console.log(`[legacy-migration] Written: ${runeProjects.length} projects, ${runeServices.length} services, ${runeEnvs.length} environments (${svcEnvCount} service-scoped)`);
9790
10640
  console.log(`[legacy-migration] Overrides: ${Object.keys(flatOverrides).length}, custom parsers: ${migratedParsers.length} (all disabled \u2014 rewrite required)`);
@@ -9795,12 +10645,12 @@ ${p.transform ?? ""}`,
9795
10645
  }
9796
10646
  async function findLegacyKeyInBackup(dataDir) {
9797
10647
  try {
9798
- const entries = await readdir3(dataDir);
10648
+ const entries = await readdir4(dataDir);
9799
10649
  const backupDirs = entries.filter((e) => e.startsWith("backup-")).sort().reverse();
9800
10650
  for (const dir of backupDirs) {
9801
- const keyJsonPath = join15(dataDir, dir, "dbs", "encryption-key.json");
10651
+ const keyJsonPath = join16(dataDir, dir, "dbs", "encryption-key.json");
9802
10652
  try {
9803
- const raw = await readFile11(keyJsonPath, "utf-8");
10653
+ const raw = await readFile12(keyJsonPath, "utf-8");
9804
10654
  const parsed = JSON.parse(raw);
9805
10655
  if (!parsed.encryptionKey) continue;
9806
10656
  const sodium = await getSodium();
@@ -9822,14 +10672,14 @@ async function runOverrideOnlyMigration(dataDir) {
9822
10672
  const abDataDir = resolve5(dataDir);
9823
10673
  const abCwd = resolve5(process.cwd());
9824
10674
  const candidateDirs = [
9825
- join15(abCwd, ".runeya", "dbs", "overrides"),
9826
- join15(abDataDir, "dbs", "overrides")
10675
+ join16(abCwd, ".runeya", "dbs", "overrides"),
10676
+ join16(abDataDir, "dbs", "overrides")
9827
10677
  ];
9828
10678
  let overridesDir = null;
9829
10679
  let overrideFiles = [];
9830
10680
  for (const dir of candidateDirs) {
9831
10681
  try {
9832
- const files = (await readdir3(dir)).filter((f) => f.endsWith(".encrypted.json"));
10682
+ const files = (await readdir4(dir)).filter((f) => f.endsWith(".encrypted.json"));
9833
10683
  if (files.length > 0) {
9834
10684
  overridesDir = dir;
9835
10685
  overrideFiles = files;
@@ -9839,9 +10689,9 @@ async function runOverrideOnlyMigration(dataDir) {
9839
10689
  }
9840
10690
  }
9841
10691
  if (!overridesDir || overrideFiles.length === 0) return false;
9842
- const legacyKeyPath = join15(overridesDir, "..", "encryption-key.json");
10692
+ const legacyKeyPath = join16(overridesDir, "..", "encryption-key.json");
9843
10693
  try {
9844
- await access(legacyKeyPath);
10694
+ await access2(legacyKeyPath);
9845
10695
  return false;
9846
10696
  } catch {
9847
10697
  }
@@ -9857,7 +10707,7 @@ async function runOverrideOnlyMigration(dataDir) {
9857
10707
  const envNameToId = {};
9858
10708
  let envsRaw;
9859
10709
  try {
9860
- envsRaw = JSON.parse(await readFile11(join15(abDataDir, "environments.json"), "utf-8"));
10710
+ envsRaw = JSON.parse(await readFile12(join16(abDataDir, "environments.json"), "utf-8"));
9861
10711
  for (const _env of envsRaw) {
9862
10712
  const env2 = _env;
9863
10713
  if (env2["name"] && env2["id"]) {
@@ -9876,7 +10726,7 @@ async function runOverrideOnlyMigration(dataDir) {
9876
10726
  let servicesRaw = [];
9877
10727
  const serviceNameToId = {};
9878
10728
  try {
9879
- servicesRaw = JSON.parse(await readFile11(join15(abDataDir, "services.json"), "utf-8"));
10729
+ servicesRaw = JSON.parse(await readFile12(join16(abDataDir, "services.json"), "utf-8"));
9880
10730
  for (const _svc of servicesRaw) {
9881
10731
  const svc = _svc;
9882
10732
  if (svc["name"] && svc["id"]) serviceNameToId[svc["name"]] = svc["id"];
@@ -9893,7 +10743,7 @@ async function runOverrideOnlyMigration(dataDir) {
9893
10743
  const envName = baseName.slice(0, -"-environment".length);
9894
10744
  let data;
9895
10745
  try {
9896
- data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
10746
+ data = await decryptLegacyFile(join16(overridesDir, file), sodiumKey, sodium);
9897
10747
  } catch (err) {
9898
10748
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9899
10749
  continue;
@@ -9915,12 +10765,12 @@ async function runOverrideOnlyMigration(dataDir) {
9915
10765
  if (isSameAsBase(value, baseVar)) continue;
9916
10766
  flatOverrides[varId] = makeVariable(cleaned, value);
9917
10767
  } else {
9918
- flatOverrides[randomUUID9()] = makeVariable(cleaned, value);
10768
+ flatOverrides[randomUUID10()] = makeVariable(cleaned, value);
9919
10769
  }
9920
10770
  }
9921
10771
  }
9922
10772
  if (Object.keys(flatOverrides).length > 0) {
9923
- await atomicWrite(join15(abDataDir, "environment-overrides.json"), flatOverrides);
10773
+ await atomicWrite(join16(abDataDir, "environment-overrides.json"), flatOverrides);
9924
10774
  console.log(`[override-migration] Written ${Object.keys(flatOverrides).length} override(s) to environment-overrides.json`);
9925
10775
  }
9926
10776
  let envsModified = false;
@@ -9930,7 +10780,7 @@ async function runOverrideOnlyMigration(dataDir) {
9930
10780
  const serviceName = baseName.slice(0, -"-envs".length);
9931
10781
  let data;
9932
10782
  try {
9933
- data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
10783
+ data = await decryptLegacyFile(join16(overridesDir, file), sodiumKey, sodium);
9934
10784
  } catch (err) {
9935
10785
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9936
10786
  continue;
@@ -9952,7 +10802,7 @@ async function runOverrideOnlyMigration(dataDir) {
9952
10802
  );
9953
10803
  if (!svcScopedEnv) {
9954
10804
  svcScopedEnv = {
9955
- id: randomUUID9(),
10805
+ id: randomUUID10(),
9956
10806
  projectId: globalEnv["projectId"],
9957
10807
  name: `${service?.["name"] || serviceName} (${globalEnv["name"]})`,
9958
10808
  scope: "service",
@@ -9974,7 +10824,7 @@ async function runOverrideOnlyMigration(dataDir) {
9974
10824
  const baseEntry = Object.entries(svcScopedEnv["variables"]).find(([, v]) => v.key === cleaned);
9975
10825
  let varId = baseEntry?.[0];
9976
10826
  if (!varId) {
9977
- varId = randomUUID9();
10827
+ varId = randomUUID10();
9978
10828
  svcScopedEnv["variables"][varId] = {
9979
10829
  key: cleaned,
9980
10830
  value: { value: "", pre: "", post: "", isSecret: false }
@@ -9989,7 +10839,7 @@ async function runOverrideOnlyMigration(dataDir) {
9989
10839
  }
9990
10840
  }
9991
10841
  if (envsModified) {
9992
- await atomicWrite(join15(abDataDir, "environments.json"), envsRaw);
10842
+ await atomicWrite(join16(abDataDir, "environments.json"), envsRaw);
9993
10843
  console.log("[override-migration] Merged service overrides into environments.json");
9994
10844
  }
9995
10845
  const migratedDir = overridesDir.replace(/overrides$/, "overrides-migrated");
@@ -10013,7 +10863,7 @@ var ARGON2_OPTIONS = { memoryCost: 65536, timeCost: 3, parallelism: 4, outputLen
10013
10863
  async function readKeyFile(filePath) {
10014
10864
  const s = await stat4(filePath);
10015
10865
  if (s.size > MAX_KEY_FILE_SIZE) throw new Error(`Fichier suspect : ${filePath} (${s.size} octets)`);
10016
- return readFile12(filePath);
10866
+ return readFile13(filePath);
10017
10867
  }
10018
10868
  function aesEncrypt(key, plaintext) {
10019
10869
  const iv = randomBytes4(12);
@@ -10032,7 +10882,7 @@ function aesDecrypt(key, data) {
10032
10882
  }
10033
10883
  async function getPepperFromFile(dataDir) {
10034
10884
  try {
10035
- const raw = await readKeyFile(join16(dataDir, PEPPER_FILENAME));
10885
+ const raw = await readKeyFile(join17(dataDir, PEPPER_FILENAME));
10036
10886
  const hex = raw.toString("utf-8").trim();
10037
10887
  if (/^[0-9a-f]{64}$/i.test(hex)) return Buffer.from(hex, "hex");
10038
10888
  } catch {
@@ -10040,7 +10890,7 @@ async function getPepperFromFile(dataDir) {
10040
10890
  return null;
10041
10891
  }
10042
10892
  async function writePepperToFile(dataDir, pepper) {
10043
- const filePath = join16(dataDir, PEPPER_FILENAME);
10893
+ const filePath = join17(dataDir, PEPPER_FILENAME);
10044
10894
  const tmpPath = filePath + ".tmp";
10045
10895
  await writeFile12(tmpPath, pepper.toString("hex"), "utf-8");
10046
10896
  await chmod9(tmpPath, 384);
@@ -10091,14 +10941,14 @@ async function writeEncryptedKey(dataDir, masterPassword, encryptionKey) {
10091
10941
  const payload = aesEncrypt(derivedKey, encryptionKey);
10092
10942
  const fileContent = Buffer.concat([salt, payload]);
10093
10943
  const encoded = fileContent.toString("base64");
10094
- const filePath = join16(dataDir, ENCRYPTED_KEY_FILENAME);
10944
+ const filePath = join17(dataDir, ENCRYPTED_KEY_FILENAME);
10095
10945
  const tmpPath = filePath + ".tmp";
10096
10946
  await writeFile12(tmpPath, encoded, "utf-8");
10097
10947
  await chmod9(tmpPath, 384);
10098
10948
  await rename10(tmpPath, filePath);
10099
10949
  }
10100
10950
  async function readEncryptedKey(dataDir, masterPassword) {
10101
- const filePath = join16(dataDir, ENCRYPTED_KEY_FILENAME);
10951
+ const filePath = join17(dataDir, ENCRYPTED_KEY_FILENAME);
10102
10952
  const raw = await readKeyFile(filePath);
10103
10953
  const fileContent = Buffer.from(raw.toString("utf-8").trim(), "base64");
10104
10954
  const salt = fileContent.subarray(0, 16);
@@ -10118,13 +10968,13 @@ async function getPepperFromKeychain(dataDir) {
10118
10968
  }
10119
10969
  async function writeVerifyFile(dataDir, encryptionKey) {
10120
10970
  const payload = aesEncrypt(encryptionKey, Buffer.from(CANARY_PLAINTEXT, "utf-8"));
10121
- const filePath = join16(dataDir, VERIFY_FILENAME);
10971
+ const filePath = join17(dataDir, VERIFY_FILENAME);
10122
10972
  const tmpPath = filePath + ".tmp";
10123
10973
  await writeFile12(tmpPath, payload.toString("base64"), "utf-8");
10124
10974
  await rename10(tmpPath, filePath);
10125
10975
  }
10126
10976
  async function verifyEncryptionKey(dataDir, encryptionKey) {
10127
- const filePath = join16(dataDir, VERIFY_FILENAME);
10977
+ const filePath = join17(dataDir, VERIFY_FILENAME);
10128
10978
  const raw = await readKeyFile(filePath);
10129
10979
  const payload = Buffer.from(raw.toString("utf-8").trim(), "base64");
10130
10980
  const plaintext = aesDecrypt(encryptionKey, payload);
@@ -10134,7 +10984,7 @@ async function verifyEncryptionKey(dataDir, encryptionKey) {
10134
10984
  }
10135
10985
  async function hasVerifyFile(dataDir) {
10136
10986
  try {
10137
- await stat4(join16(dataDir, VERIFY_FILENAME));
10987
+ await stat4(join17(dataDir, VERIFY_FILENAME));
10138
10988
  return true;
10139
10989
  } catch {
10140
10990
  return false;
@@ -10142,7 +10992,7 @@ async function hasVerifyFile(dataDir) {
10142
10992
  }
10143
10993
  async function hasEncryptedKeyFile(dataDir) {
10144
10994
  try {
10145
- await stat4(join16(dataDir, ENCRYPTED_KEY_FILENAME));
10995
+ await stat4(join17(dataDir, ENCRYPTED_KEY_FILENAME));
10146
10996
  return true;
10147
10997
  } catch {
10148
10998
  return false;
@@ -10156,17 +11006,17 @@ async function getServerState() {
10156
11006
  let hasVerify = false;
10157
11007
  let hasData = false;
10158
11008
  try {
10159
- await stat5(join17(dataDir, ENCRYPTED_KEY_FILENAME2));
11009
+ await stat5(join18(dataDir, ENCRYPTED_KEY_FILENAME2));
10160
11010
  hasEncFile = true;
10161
11011
  } catch {
10162
11012
  }
10163
11013
  try {
10164
- await stat5(join17(dataDir, VERIFY_FILENAME2));
11014
+ await stat5(join18(dataDir, VERIFY_FILENAME2));
10165
11015
  hasVerify = true;
10166
11016
  } catch {
10167
11017
  }
10168
11018
  try {
10169
- await stat5(join17(dataDir, "projects.json"));
11019
+ await stat5(join18(dataDir, "projects.json"));
10170
11020
  hasData = true;
10171
11021
  } catch {
10172
11022
  }
@@ -10609,6 +11459,9 @@ async function createLocalServer() {
10609
11459
  }
10610
11460
  await agentManager.init();
10611
11461
  await agentSpawner.spawnLocalAgent();
11462
+ startClaudeTranscriptWatcher().catch((err) => {
11463
+ console.warn("[server] Failed to start Claude transcript watcher:", err);
11464
+ });
10612
11465
  }
10613
11466
  if (hasEncryptionKey()) {
10614
11467
  await runPostUnlock();
@@ -10742,20 +11595,39 @@ async function createLocalServer() {
10742
11595
  res.status(500).send("Internal server error");
10743
11596
  }
10744
11597
  });
10745
- app.get("/api/docs", (_req, res) => {
11598
+ app.get("/api/docs", (req, res) => {
11599
+ const dark = req.query["dark"] === "1";
10746
11600
  res.setHeader("Content-Type", "text/html; charset=utf-8");
11601
+ res.setHeader("Cache-Control", "no-store");
10747
11602
  res.send(`<!doctype html>
10748
- <html>
11603
+ <html lang="en">
10749
11604
  <head>
10750
11605
  <meta charset="utf-8" />
10751
11606
  <meta name="viewport" content="width=device-width, initial-scale=1" />
10752
11607
  <title>Runeya API Docs</title>
10753
- <style>body { margin: 0; }</style>
11608
+ <meta name="description" content="Runeya service management API reference" />
11609
+ <style>
11610
+ body { margin: 0; }
11611
+ </style>
10754
11612
  </head>
10755
11613
  <body>
10756
11614
  <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
10757
11615
  <script>
10758
- Scalar.createApiReference(document.body, { url: '/api/openapi.json' });
11616
+ Scalar.createApiReference(document.body, {
11617
+ url: '/api/openapi.json',
11618
+ darkMode: ${dark},
11619
+ layout: 'modern',
11620
+ defaultHttpClient: { targetKey: 'shell', clientKey: 'curl' },
11621
+ defaultOpenAllTags: false,
11622
+ persistAuth: true,
11623
+ agent: { disabled: true },
11624
+ metaData: {
11625
+ title: 'Runeya API Docs',
11626
+ description: 'Runeya service management API reference',
11627
+ ogTitle: 'Runeya API Docs',
11628
+ ogDescription: 'Runeya service management API reference',
11629
+ },
11630
+ });
10759
11631
  </script>
10760
11632
  </body>
10761
11633
  </html>`);
@@ -10997,6 +11869,11 @@ async function pullEnv(opts) {
10997
11869
  return Object.entries(env2).map(([key, value]) => `${key}=${value}`).join("\n");
10998
11870
  }
10999
11871
  dotenv.config({ quiet: true });
11872
+ if (process.env["DATA_DIR"]) {
11873
+ const resolved = resolvePath(process.env["DATA_DIR"], "..");
11874
+ process.chdir(resolved);
11875
+ console.log(`[server] Working directory: ${resolved} (from DATA_DIR)`);
11876
+ }
11000
11877
  function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500) {
11001
11878
  return new Promise((resolve5, reject) => {
11002
11879
  let attempts = 0;
@@ -11066,4 +11943,4 @@ export {
11066
11943
  createLocalServer,
11067
11944
  pullEnv
11068
11945
  };
11069
- //# sourceMappingURL=dist-PSPHMTZN.js.map
11946
+ //# sourceMappingURL=dist-2H7XZQCV.js.map