@runeya/apps-cli 0.1.0 → 0.1.2

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 (274) hide show
  1. package/dist/agent/index.js +5303 -0
  2. package/dist/agent/index.js.map +1 -0
  3. package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js +8 -0
  4. package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js.map +1 -0
  5. package/dist/chunk-32HTB3XS.js +350 -0
  6. package/dist/chunk-32HTB3XS.js.map +1 -0
  7. package/dist/chunk-ERJIU7R4.js +195 -0
  8. package/dist/chunk-ERJIU7R4.js.map +1 -0
  9. package/dist/chunk-XZPCEGWS.js +2452 -0
  10. package/dist/chunk-XZPCEGWS.js.map +1 -0
  11. package/dist/dist-MFM5N25P.js +10985 -0
  12. package/dist/dist-MFM5N25P.js.map +1 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js +14 -0
  15. package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js.map +1 -0
  16. package/dist/package.json +36 -0
  17. package/dist/runeya +4 -0
  18. package/dist/web/assets/AddressBar.vue-dB66q4DS.js +1 -0
  19. package/dist/web/assets/AgentPage-B3wrH3gG.js +2 -0
  20. package/dist/web/assets/AgentPage-BTWkJvqr.css +1 -0
  21. package/dist/web/assets/AgentScalarChatInterface.vue-CJVuZIB9.js +13 -0
  22. package/dist/web/assets/AgentTerminalPage-Cj5fRr-j.js +36 -0
  23. package/dist/web/assets/AgentTerminalPage-EX_n6P3x.css +1 -0
  24. package/dist/web/assets/AiChatPage-Dhsx3B9B.js +1 -0
  25. package/dist/web/assets/ApiDocsPage-BddLJuUF.js +5 -0
  26. package/dist/web/assets/ApiDocsPage-D2Z0wqGZ.css +1 -0
  27. package/dist/web/assets/AppLayout-AfhaeKlp.js +1 -0
  28. package/dist/web/assets/AppLayout-sngalCCj.css +1 -0
  29. package/dist/web/assets/AuditPage-BBV9FW9y.css +1 -0
  30. package/dist/web/assets/AuditPage-DqycuiVb.js +1 -0
  31. package/dist/web/assets/CodeInput.vue-Dyew7ml-.js +1 -0
  32. package/dist/web/assets/Collection.vue-DePdVAHM.js +1 -0
  33. package/dist/web/assets/CollectionAuthentication.vue-DagSEg45.js +1 -0
  34. package/dist/web/assets/CollectionCookies.vue-vMKPCgg1.js +1 -0
  35. package/dist/web/assets/CollectionEnvironment.vue-B0JkZ6GC.js +1 -0
  36. package/dist/web/assets/CollectionOverview.vue-Bm7OsMWM.js +1 -0
  37. package/dist/web/assets/CollectionScripts.vue-CZiBq0rT.js +1 -0
  38. package/dist/web/assets/CollectionServers.vue-bziJbP-5.js +1 -0
  39. package/dist/web/assets/CollectionSettings.vue-D4oBu-fT.js +1 -0
  40. package/dist/web/assets/CollectionSync.vue-BG50-Lo4.js +1 -0
  41. package/dist/web/assets/CommandActionInput.vue-DmZld2w7.js +1 -0
  42. package/dist/web/assets/Cookies.vue-DjxMLL79.js +1 -0
  43. package/dist/web/assets/DashboardPage-QjYCF0Kd.js +1 -0
  44. package/dist/web/assets/DashboardPage-gZJ4_s8T.css +1 -0
  45. package/dist/web/assets/DataTable.vue-BV9IuUNB.js +1 -0
  46. package/dist/web/assets/DataTableCheckbox.vue-Cx0exaNK.js +1 -0
  47. package/dist/web/assets/DataTableHeader.vue-ZtUdyiLV.js +1 -0
  48. package/dist/web/assets/DataTableInput.vue-C4NF8kJn.js +1 -0
  49. package/dist/web/assets/DeleteSidebarListElement.vue-CcAW4Pdn.js +1 -0
  50. package/dist/web/assets/EditSidebarListElement.vue-Crbagha4.js +1 -0
  51. package/dist/web/assets/EditorIcon-BaDORp4k.js +15 -0
  52. package/dist/web/assets/EditorIcon-zS3bHXe1.css +1 -0
  53. package/dist/web/assets/EmptyState.vue-2hJbVxAi.js +22 -0
  54. package/dist/web/assets/Environment.vue-CcERFnQq.js +1 -0
  55. package/dist/web/assets/EnvironmentForm-DSKxdDGW.js +108 -0
  56. package/dist/web/assets/EnvironmentForm-DVMc303P.css +1 -0
  57. package/dist/web/assets/EnvironmentFormPage-BDhR07CG.js +1 -0
  58. package/dist/web/assets/EnvironmentFormPage-OaDJvyrc.css +1 -0
  59. package/dist/web/assets/EnvironmentListPage-BDqcug66.css +1 -0
  60. package/dist/web/assets/EnvironmentListPage-BzdxgZdS.js +1 -0
  61. package/dist/web/assets/EnvironmentModal.vue-DvsPa6V9.js +1 -0
  62. package/dist/web/assets/EnvironmentSelector-B9SZvB3I.css +1 -0
  63. package/dist/web/assets/EnvironmentSelector-BWljvJWP.js +1 -0
  64. package/dist/web/assets/Form.vue-BxhIukSx.js +1 -0
  65. package/dist/web/assets/IconSelector.vue-SxFpYToZ.js +1 -0
  66. package/dist/web/assets/KanbanBoardPage-7nOYpwHn.css +1 -0
  67. package/dist/web/assets/KanbanBoardPage-Bbyn-iLC.js +141 -0
  68. package/dist/web/assets/KanbanListPage-BEX3MKW0.js +1 -0
  69. package/dist/web/assets/KanbanListPage-DbdjvTCx.css +1 -0
  70. package/dist/web/assets/LaunchProjectPage-DB_XBa86.js +1 -0
  71. package/dist/web/assets/LaunchProjectPage-_L_i2xo0.css +1 -0
  72. package/dist/web/assets/LaunchServicesPage-BABQz-ey.css +1 -0
  73. package/dist/web/assets/LaunchServicesPage-fmsU-_p0.js +1 -0
  74. package/dist/web/assets/LogViewer-CJcXcTP0.css +1 -0
  75. package/dist/web/assets/LogViewer-CV9OmAb1.js +3 -0
  76. package/dist/web/assets/ProcessControls-CKmB6sB9.css +1 -0
  77. package/dist/web/assets/ProcessControls-L_hld0kN.js +1 -0
  78. package/dist/web/assets/ProcessDetailPage-B0qGKtvQ.css +1 -0
  79. package/dist/web/assets/ProcessDetailPage-CW0F6wvO.js +1 -0
  80. package/dist/web/assets/ProcessMetrics-BgEFMdnW.css +1 -0
  81. package/dist/web/assets/ProcessMetrics-D2vCK15o.js +1 -0
  82. package/dist/web/assets/ProjectDetailPage-Bo04MsdE.js +1 -0
  83. package/dist/web/assets/ProjectDetailPage-CZBB6F-t.css +1 -0
  84. package/dist/web/assets/ProjectListPage-BGU6SKAr.css +1 -0
  85. package/dist/web/assets/ProjectListPage-CG-r82FQ.js +1 -0
  86. package/dist/web/assets/RandomJobTestPage-C1mgTTSY.css +1 -0
  87. package/dist/web/assets/RandomJobTestPage-COnuE_dj.js +2 -0
  88. package/dist/web/assets/Request.vue-CeAzdpVl.js +1 -0
  89. package/dist/web/assets/RequestAuth.vue-DABBKM9M.js +1 -0
  90. package/dist/web/assets/RequestRoot.vue-CRaiGtgb.js +19 -0
  91. package/dist/web/assets/ScalarAsciiArt.vue-mR3JflbN.js +2 -0
  92. package/dist/web/assets/ScalarIconEyeSlash.vue-JQCpS43o.js +1 -0
  93. package/dist/web/assets/ScalarIconTrash.vue-D7zGeCPC.js +1 -0
  94. package/dist/web/assets/ScenarioEditorPage-BVnDp-fS.js +14 -0
  95. package/dist/web/assets/ScenarioEditorPage-CfBKebp6.css +1 -0
  96. package/dist/web/assets/ServerVariablesForm.vue-DbVG31Dx.js +1 -0
  97. package/dist/web/assets/ServiceControlsBar-Cp2VUifX.js +1 -0
  98. package/dist/web/assets/ServiceControlsBar-D_S_sGQj.css +1 -0
  99. package/dist/web/assets/ServiceDetailPage-BriHTCpT.css +1 -0
  100. package/dist/web/assets/ServiceDetailPage-DMoXJliU.js +2 -0
  101. package/dist/web/assets/ServiceFormPage-BXTTwyWP.js +1 -0
  102. package/dist/web/assets/ServiceFormPage-CyY-14jH.css +1 -0
  103. package/dist/web/assets/Settings.vue-BtjfBYMB.js +1 -0
  104. package/dist/web/assets/SettingsPage-BFqYvm6R.js +508 -0
  105. package/dist/web/assets/SettingsPage-vGhb_tlY.css +1 -0
  106. package/dist/web/assets/SetupKeyPage-CrmikLTx.js +1 -0
  107. package/dist/web/assets/SetupKeyPage-DoY1Ua1s.css +1 -0
  108. package/dist/web/assets/SetupPage-CRuL5LXc.js +1 -0
  109. package/dist/web/assets/SetupPage-_KXpCQSU.css +1 -0
  110. package/dist/web/assets/Sidebar.vue-CLpz7l3s.js +1 -0
  111. package/dist/web/assets/SidebarButton.vue-DAvh-OoW.js +1 -0
  112. package/dist/web/assets/SidebarListElement.vue-CfQ22Epo.js +1 -0
  113. package/dist/web/assets/UnlockPage-Cx3PYgUl.css +1 -0
  114. package/dist/web/assets/UnlockPage-UxunGumn.js +1 -0
  115. package/dist/web/assets/VariablesTable-BtdK5olT.css +1 -0
  116. package/dist/web/assets/VariablesTable-CKe8mApI.js +1 -0
  117. package/dist/web/assets/ViewLayout.vue-D30IFsIP.js +1 -0
  118. package/dist/web/assets/ViewLayoutContent.vue-B4BXKBtQ.js +1 -0
  119. package/dist/web/assets/ViewLayoutSection.vue-BGkXXzBQ.js +1 -0
  120. package/dist/web/assets/_plugin-vue_export-helper-C7ztIXtE.js +1 -0
  121. package/dist/web/assets/abap-C4tHUUCU.js +1 -0
  122. package/dist/web/assets/active-entities-DMSK8Kvo.js +1 -0
  123. package/dist/web/assets/apex-6cG6aG-w.js +1 -0
  124. package/dist/web/assets/api-reference-DUBKG98-.js +1 -0
  125. package/dist/web/assets/autocomplete-Bx5M86A5.js +308 -0
  126. package/dist/web/assets/azcli-51BngrSU.js +1 -0
  127. package/dist/web/assets/base64-uZi92-DX.js +1 -0
  128. package/dist/web/assets/bat-BDcdVSIt.js +1 -0
  129. package/dist/web/assets/bicep-Cg0BCcky.js +2 -0
  130. package/dist/web/assets/cameligo-C3ekVui9.js +1 -0
  131. package/dist/web/assets/card-6r6P_U-q.js +32 -0
  132. package/dist/web/assets/checkbox-CQpllPXd.js +141 -0
  133. package/dist/web/assets/chip-gp-_w98f.js +54 -0
  134. package/dist/web/assets/clojure-Ci5xNgh_.js +1 -0
  135. package/dist/web/assets/codicon-DCmgc-ay.ttf +0 -0
  136. package/dist/web/assets/coffee-xsRz4GKy.js +1 -0
  137. package/dist/web/assets/column-CE6TbiAC.js +763 -0
  138. package/dist/web/assets/core-CLjfGkKM.js +1 -0
  139. package/dist/web/assets/cpp-B2gW4e23.js +1 -0
  140. package/dist/web/assets/csharp-SRujvHqy.js +1 -0
  141. package/dist/web/assets/csp-Cr-ll6gP.js +1 -0
  142. package/dist/web/assets/css-BYTJZCno.js +3 -0
  143. package/dist/web/assets/cssMode-Bl7Y9u0B.js +4 -0
  144. package/dist/web/assets/cypher-AmQ0R_D3.js +1 -0
  145. package/dist/web/assets/dart-Du01a0J5.js +1 -0
  146. package/dist/web/assets/dist-CNJKy0Au.js +192 -0
  147. package/dist/web/assets/dist-DGrDIHmK.js +1 -0
  148. package/dist/web/assets/dist-DkaYBczY.js +1 -0
  149. package/dist/web/assets/dist-E9g7EMme.js +1 -0
  150. package/dist/web/assets/dist-wHtavuXf.js +33 -0
  151. package/dist/web/assets/divider-J1wJyhb6.js +82 -0
  152. package/dist/web/assets/dockerfile-Dtlk9wqf.js +1 -0
  153. package/dist/web/assets/ecl-CpmVnLkK.js +1 -0
  154. package/dist/web/assets/editor.store-CIIP294U.js +1 -0
  155. package/dist/web/assets/editor.worker-Di51weDy.js +12 -0
  156. package/dist/web/assets/elixir-B3MVAC8k.js +1 -0
  157. package/dist/web/assets/fieldset-BRuU3VW2.js +88 -0
  158. package/dist/web/assets/flow9-Chkly0RN.js +1 -0
  159. package/dist/web/assets/freemarker2-C-5i2Fhr.js +3 -0
  160. package/dist/web/assets/fsharp-CZvMkppk.js +1 -0
  161. package/dist/web/assets/go-dT2iT7lt.js +1 -0
  162. package/dist/web/assets/graphql-DmObWyBD.js +1 -0
  163. package/dist/web/assets/handlebars-Bq-uEMhK.js +1 -0
  164. package/dist/web/assets/hcl-D1x5Oyp4.js +1 -0
  165. package/dist/web/assets/html-B-mweV_C.js +1 -0
  166. package/dist/web/assets/htmlMode-BlnuAGDY.js +4 -0
  167. package/dist/web/assets/index-BRDI9Dib.css +1 -0
  168. package/dist/web/assets/index-gfmcW9Sb.js +4220 -0
  169. package/dist/web/assets/ini-Bg2o1S3s.js +1 -0
  170. package/dist/web/assets/java-XviKSSn5.js +1 -0
  171. package/dist/web/assets/javascript-D61EWPTf.js +1 -0
  172. package/dist/web/assets/jsonMode-DmSJWG69.js +10 -0
  173. package/dist/web/assets/julia-DfTIwUGh.js +1 -0
  174. package/dist/web/assets/keyboard-nBda2f6m.js +4 -0
  175. package/dist/web/assets/kotlin-CNaIBh9d.js +1 -0
  176. package/dist/web/assets/less-D7o76tXd.js +2 -0
  177. package/dist/web/assets/lexon-Dd374QjV.js +1 -0
  178. package/dist/web/assets/lib-CegStMAw.js +5 -0
  179. package/dist/web/assets/library-BwIJAfg0.js +1 -0
  180. package/dist/web/assets/liquid-BRXADGhu.js +1 -0
  181. package/dist/web/assets/lua-DZKuiFbY.js +1 -0
  182. package/dist/web/assets/m3-CbwRx9Bb.js +1 -0
  183. package/dist/web/assets/markdown-DwLB5Rib.js +1 -0
  184. package/dist/web/assets/mdx-DXlefD6G.js +1 -0
  185. package/dist/web/assets/mediaTypes-BtSbXDUA.js +2 -0
  186. package/dist/web/assets/message-EfwB0RVQ.js +316 -0
  187. package/dist/web/assets/minus-DCEEn9ly.js +2 -0
  188. package/dist/web/assets/mips-DLbIq1yM.js +1 -0
  189. package/dist/web/assets/msdax-D4IjoZ6m.js +1 -0
  190. package/dist/web/assets/multiselect-BMOAw1qo.js +251 -0
  191. package/dist/web/assets/mysql-DQL_jy6H.js +1 -0
  192. package/dist/web/assets/objective-c-D74wM5bv.js +1 -0
  193. package/dist/web/assets/pascal-5tRlZk4O.js +1 -0
  194. package/dist/web/assets/pascaligo-CAE4rAEt.js +1 -0
  195. package/dist/web/assets/password-B0kJu8RE.js +101 -0
  196. package/dist/web/assets/perl-DREAbuQT.js +1 -0
  197. package/dist/web/assets/pgsql-DASKdyAW.js +1 -0
  198. package/dist/web/assets/php-C37ZWqoO.js +1 -0
  199. package/dist/web/assets/pla-v5u9R8i1.js +1 -0
  200. package/dist/web/assets/plus-CaknAInj.js +197 -0
  201. package/dist/web/assets/postiats-Szt5m-7G.js +1 -0
  202. package/dist/web/assets/powerquery-BzVrUSu3.js +1 -0
  203. package/dist/web/assets/powershell-BdbPbX_X.js +1 -0
  204. package/dist/web/assets/pretty-ms-CG-W6pSW.js +202 -0
  205. package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
  206. package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
  207. package/dist/web/assets/primeicons-Dr5RGzOO.svg +345 -0
  208. package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
  209. package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
  210. package/dist/web/assets/process.store-DxDr1XKZ.js +1 -0
  211. package/dist/web/assets/project.store-CUclo-tf.js +1 -0
  212. package/dist/web/assets/protobuf-qqDa50tu.js +2 -0
  213. package/dist/web/assets/pug-Cu20UQ5w.js +1 -0
  214. package/dist/web/assets/python-BeJzJfgr.js +1 -0
  215. package/dist/web/assets/qsharp-DcbHL5X7.js +1 -0
  216. package/dist/web/assets/r-Bvig6Flk.js +1 -0
  217. package/dist/web/assets/radiobutton-Dpp9kpvR.js +145 -0
  218. package/dist/web/assets/razor-C8GjTcdm.js +1 -0
  219. package/dist/web/assets/redis-BxHDcW3-.js +1 -0
  220. package/dist/web/assets/redshift-B8dvlzjH.js +1 -0
  221. package/dist/web/assets/restructuredtext-npodtAFM.js +1 -0
  222. package/dist/web/assets/router-BAq_YY48.js +1 -0
  223. package/dist/web/assets/ruby-DiofbcJD.js +1 -0
  224. package/dist/web/assets/rust-BjYid3-m.js +1 -0
  225. package/dist/web/assets/sb-CzcPAP71.js +1 -0
  226. package/dist/web/assets/scala-Ci4wFrYi.js +1 -0
  227. package/dist/web/assets/scheme-D3iWhvP7.js +1 -0
  228. package/dist/web/assets/scss-DdlqDgmu.js +3 -0
  229. package/dist/web/assets/settings.store-RtV37pb_.js +1 -0
  230. package/dist/web/assets/shell-Bawb9gbH.js +1 -0
  231. package/dist/web/assets/solidity-Z2Sq8PBC.js +1 -0
  232. package/dist/web/assets/sophia--yD-H1Ow.js +1 -0
  233. package/dist/web/assets/sparql-0vv3-wLi.js +1 -0
  234. package/dist/web/assets/sql-0gdWKhmJ.js +1 -0
  235. package/dist/web/assets/st-rE-x-DTZ.js +1 -0
  236. package/dist/web/assets/store-BSUCffsu.js +1840 -0
  237. package/dist/web/assets/style-4Pk2icOw.css +1 -0
  238. package/dist/web/assets/swift-CSKeG8Ne.js +1 -0
  239. package/dist/web/assets/systemverilog-CblNM3Im.js +1 -0
  240. package/dist/web/assets/tabpanel-CXo9Yhvl.js +150 -0
  241. package/dist/web/assets/tag-BsP7PdQ9.js +54 -0
  242. package/dist/web/assets/tcl-BYnzj-GZ.js +1 -0
  243. package/dist/web/assets/toggleswitch-C0fuTzJt.js +115 -0
  244. package/dist/web/assets/trpc-DgyerAaK.js +1 -0
  245. package/dist/web/assets/ts.worker-DHQ2j8_R.js +51339 -0
  246. package/dist/web/assets/tsMode-CaqfXb40.js +11 -0
  247. package/dist/web/assets/twig-DiGWiEtp.js +1 -0
  248. package/dist/web/assets/typescript-D23_zyMA.js +1 -0
  249. package/dist/web/assets/typespec-a6xMcpXw.js +1 -0
  250. package/dist/web/assets/urls-D4kou9RH.js +12 -0
  251. package/dist/web/assets/use-tree-walker-BebEauYQ.js +1 -0
  252. package/dist/web/assets/useAiChatDrawer-DDBZQU71.js +1 -0
  253. package/dist/web/assets/useClipboard-BgE-EP5h.js +1 -0
  254. package/dist/web/assets/useLayout-BtC8xLAa.js +1 -0
  255. package/dist/web/assets/usePluginManager-MFUURiUn.js +1 -0
  256. package/dist/web/assets/useSidebar-DC34K6Vb.js +1 -0
  257. package/dist/web/assets/uuid-B9GFVXPm.js +1 -0
  258. package/dist/web/assets/vb---51HVMC.js +1 -0
  259. package/dist/web/assets/vue-router-CCwpUNtv.js +1 -0
  260. package/dist/web/assets/w3c-keyname-DDCGDLuI.js +1 -0
  261. package/dist/web/assets/wgsl-2A8z4pT5.js +298 -0
  262. package/dist/web/assets/xml-e0SmIpRt.js +1 -0
  263. package/dist/web/assets/yaml-ChhAbF6s.js +1 -0
  264. package/dist/web/index.html +14 -0
  265. package/package.json +8 -8
  266. package/Dockerfile +0 -54
  267. package/scripts/post-build.mjs +0 -61
  268. package/src/__tests__/cli-port.test.ts +0 -93
  269. package/src/index.ts +0 -298
  270. package/src/port-resolution.ts +0 -19
  271. package/tsconfig.json +0 -9
  272. package/tsup.config.bundled_lh0xis3zdq.mjs +0 -10
  273. package/tsup.config.bundled_qj1zdvku09a.mjs +0 -10
  274. package/tsup.config.ts +0 -6
@@ -0,0 +1,2452 @@
1
+ import {
2
+ decryptValue,
3
+ decryptVariableSlots,
4
+ deriveStableSecret,
5
+ encryptVariableSlots,
6
+ env,
7
+ extractEncryptedVariableSlots,
8
+ getEncryptionKey,
9
+ isEncryptedValue,
10
+ stableEncryptValue
11
+ } from "./chunk-ERJIU7R4.js";
12
+
13
+ // ../server/dist/chunk-G3WSYJ5B.js
14
+ import { EventEmitter as EventEmitter2 } from "events";
15
+ import WebSocket2 from "ws";
16
+ import { z as z20 } from "zod";
17
+ import { randomUUID, randomBytes, createCipheriv, createDecipheriv } from "crypto";
18
+ import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
19
+ import { join } from "path";
20
+ import { randomUUID as randomUUID3 } from "crypto";
21
+ import { readFile as readFile3, writeFile as writeFile3, rename as rename3, mkdir as mkdir3, chmod as chmod3 } from "fs/promises";
22
+ import { join as join3 } from "path";
23
+ import { randomUUID as randomUUID2 } from "crypto";
24
+ import { readFile as readFile2, writeFile as writeFile2, rename as rename2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
25
+ import { join as join2 } from "path";
26
+ import { z } from "zod";
27
+ import { z as z2 } from "zod";
28
+ import { z as z3 } from "zod";
29
+ import { z as z7 } from "zod";
30
+ import { z as z5 } from "zod";
31
+ import { z as z4 } from "zod";
32
+ import { z as z6 } from "zod";
33
+ import { z as z8 } from "zod";
34
+ import { z as z9 } from "zod";
35
+ import { z as z10 } from "zod";
36
+ import { z as z11 } from "zod";
37
+ import { z as z12 } from "zod";
38
+ import { z as z13 } from "zod";
39
+ import { z as z14 } from "zod";
40
+ import { z as z15 } from "zod";
41
+ import { z as z16 } from "zod";
42
+ import { z as z17 } from "zod";
43
+ import { z as z18 } from "zod";
44
+ import { z as z19 } from "zod";
45
+ import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2, pbkdf2Sync } from "crypto";
46
+ import vm from "vm";
47
+ import { spawn } from "child_process";
48
+ import { createInterface } from "readline";
49
+ import { watch } from "fs";
50
+ import { join as join4 } from "path";
51
+ import { EventEmitter } from "events";
52
+ import WebSocket from "ws";
53
+ var FILENAME = "agents.json";
54
+ var ALGORITHM = "aes-256-gcm";
55
+ function encrypt(plaintext) {
56
+ const maybeKey = getEncryptionKey();
57
+ if (!maybeKey) {
58
+ throw new Error("Encryption key not loaded \u2014 please provide the key via setup UI");
59
+ }
60
+ const key = maybeKey;
61
+ const iv = randomBytes(16);
62
+ const cipher = createCipheriv(ALGORITHM, key, iv);
63
+ let enc = cipher.update(plaintext, "utf-8", "hex");
64
+ enc += cipher.final("hex");
65
+ const authTag = cipher.getAuthTag().toString("hex");
66
+ return { encrypted: enc, iv: iv.toString("hex"), authTag };
67
+ }
68
+ function decrypt(encrypted, ivHex, authTagHex) {
69
+ if (!/^[0-9a-f]+$/i.test(encrypted) || !/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex)) {
70
+ throw new Error("Invalid hex string in encrypted agent data");
71
+ }
72
+ const maybeKey = getEncryptionKey();
73
+ if (!maybeKey) {
74
+ throw new Error("Encryption key not loaded \u2014 please provide the key via setup UI");
75
+ }
76
+ const key = maybeKey;
77
+ const iv = Buffer.from(ivHex, "hex");
78
+ const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: 16 });
79
+ decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
80
+ let dec = decipher.update(encrypted, "hex", "utf-8");
81
+ dec += decipher.final("utf-8");
82
+ return dec;
83
+ }
84
+ var AgentStore = class {
85
+ agents = /* @__PURE__ */ new Map();
86
+ passthroughEnv = {};
87
+ traefikConfigs = {};
88
+ loaded = false;
89
+ getFilePath() {
90
+ return join(env.DATA_DIR, FILENAME);
91
+ }
92
+ async ensureDir() {
93
+ await mkdir(env.DATA_DIR, { recursive: true });
94
+ }
95
+ async load() {
96
+ if (this.loaded) return;
97
+ try {
98
+ const raw = await readFile(this.getFilePath(), "utf-8");
99
+ const data = JSON.parse(raw);
100
+ if (Array.isArray(data)) {
101
+ for (const a of data) this.agents.set(a.id, a);
102
+ this.passthroughEnv = {};
103
+ } else {
104
+ const fileData = data;
105
+ for (const a of fileData.agents) this.agents.set(a.id, a);
106
+ this.passthroughEnv = fileData.passthroughEnv ?? {};
107
+ this.traefikConfigs = fileData.traefikConfigs ?? {};
108
+ }
109
+ } catch {
110
+ }
111
+ this.loaded = true;
112
+ }
113
+ async save() {
114
+ await this.ensureDir();
115
+ const filePath = this.getFilePath();
116
+ const tmpPath = filePath + ".tmp";
117
+ const fileData = {
118
+ agents: Array.from(this.agents.values()),
119
+ passthroughEnv: this.passthroughEnv,
120
+ traefikConfigs: this.traefikConfigs
121
+ };
122
+ await writeFile(tmpPath, JSON.stringify(fileData, null, 2), "utf-8");
123
+ await rename(tmpPath, filePath);
124
+ await chmod(filePath, 384);
125
+ }
126
+ toAgentConfig(stored) {
127
+ return {
128
+ id: stored.id,
129
+ name: stored.name,
130
+ url: stored.url,
131
+ passphrase: decrypt(stored.encrypted, stored.iv, stored.authTag),
132
+ passthroughEnv: this.passthroughEnv[stored.id] ?? []
133
+ };
134
+ }
135
+ async list() {
136
+ await this.load();
137
+ if (!getEncryptionKey()) {
138
+ return [];
139
+ }
140
+ return Array.from(this.agents.values()).map((a) => this.toAgentConfig(a));
141
+ }
142
+ async get(id) {
143
+ await this.load();
144
+ if (!getEncryptionKey()) {
145
+ return null;
146
+ }
147
+ const stored = this.agents.get(id);
148
+ if (!stored) return null;
149
+ return this.toAgentConfig(stored);
150
+ }
151
+ async register(name, url, passphrase) {
152
+ await this.load();
153
+ const existing = Array.from(this.agents.values()).find((a) => a.url === url);
154
+ const { encrypted, iv, authTag } = encrypt(passphrase);
155
+ if (existing) {
156
+ existing.name = name;
157
+ existing.encrypted = encrypted;
158
+ existing.iv = iv;
159
+ existing.authTag = authTag;
160
+ await this.save();
161
+ return { id: existing.id, name, url, passphrase, passthroughEnv: this.passthroughEnv[existing.id] ?? [] };
162
+ }
163
+ const id = randomUUID();
164
+ const stored = { id, name, url, encrypted, iv, authTag };
165
+ this.agents.set(id, stored);
166
+ await this.save();
167
+ return { id, name, url, passphrase, passthroughEnv: [] };
168
+ }
169
+ async remove(id) {
170
+ await this.load();
171
+ if (!this.agents.has(id)) return false;
172
+ this.agents.delete(id);
173
+ delete this.passthroughEnv[id];
174
+ delete this.traefikConfigs[id];
175
+ await this.save();
176
+ return true;
177
+ }
178
+ getPassthroughEnv(agentId) {
179
+ return this.passthroughEnv[agentId] ?? [];
180
+ }
181
+ async savePassthroughEnv(agentId, names) {
182
+ await this.load();
183
+ this.passthroughEnv[agentId] = names;
184
+ await this.save();
185
+ }
186
+ getTraefikConfig(agentId) {
187
+ return this.traefikConfigs[agentId] ?? null;
188
+ }
189
+ async saveTraefikConfig(agentId, config) {
190
+ await this.load();
191
+ this.traefikConfigs[agentId] = config;
192
+ await this.save();
193
+ }
194
+ async update(id, fields) {
195
+ await this.load();
196
+ const stored = this.agents.get(id);
197
+ if (!stored) return null;
198
+ if (fields.name !== void 0) stored.name = fields.name;
199
+ if (fields.url !== void 0) stored.url = fields.url;
200
+ if (fields.passphrase !== void 0) {
201
+ const { encrypted, iv, authTag } = encrypt(fields.passphrase);
202
+ stored.encrypted = encrypted;
203
+ stored.iv = iv;
204
+ stored.authTag = authTag;
205
+ }
206
+ await this.save();
207
+ return this.toAgentConfig(stored);
208
+ }
209
+ async updateUrl(id, url) {
210
+ await this.load();
211
+ const stored = this.agents.get(id);
212
+ if (!stored) return null;
213
+ stored.url = url;
214
+ this.agents.set(id, stored);
215
+ await this.save();
216
+ return this.toAgentConfig(stored);
217
+ }
218
+ };
219
+ var agentStore = new AgentStore();
220
+ var UserSchema = z.object({
221
+ id: z.string().max(128),
222
+ email: z.string().email().max(320),
223
+ name: z.string().min(1).max(500),
224
+ avatar: z.string().url().max(2048).nullable().optional()
225
+ });
226
+ var OrgSchema = z2.object({
227
+ id: z2.string().max(128),
228
+ name: z2.string().min(1).max(100),
229
+ slug: z2.string().min(1).max(100).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/),
230
+ ownerId: z2.string().max(128)
231
+ });
232
+ var OrgMemberSchema = z2.object({
233
+ id: z2.string().max(128),
234
+ orgId: z2.string().max(128),
235
+ userId: z2.string().max(128),
236
+ globalRole: z2.enum(["owner", "admin", "member"]),
237
+ invitedAt: z2.coerce.date(),
238
+ joinedAt: z2.coerce.date().nullable().optional()
239
+ });
240
+ var ProjectSchema = z3.object({
241
+ id: z3.string().max(128),
242
+ orgId: z3.string().max(128).nullable().optional(),
243
+ name: z3.string().min(1).max(200),
244
+ description: z3.string().max(1e4).optional().default(""),
245
+ serviceIds: z3.array(z3.string().max(128)).max(1e4).default([]),
246
+ // Environment support
247
+ // NOTE: activeEnvironmentId is runtime-only (not persisted to JSON)
248
+ // It's initialized to 'local' by default at load time
249
+ activeEnvironmentId: z3.string().max(128).optional(),
250
+ environmentIds: z3.array(z3.string().max(128)).max(100).default([])
251
+ });
252
+ var CreateProjectSchema = ProjectSchema.omit({ id: true }).partial().required({ name: true });
253
+ var UpdateProjectSchema = ProjectSchema.partial().required({ id: true });
254
+ var PositionSchema = z4.object({
255
+ x: z4.number(),
256
+ y: z4.number()
257
+ });
258
+ var ScenarioGroupSchema = z4.object({
259
+ id: z4.string().min(1).max(128),
260
+ name: z4.string().min(1).max(200),
261
+ description: z4.string().max(1e3).optional(),
262
+ color: z4.string().max(32).optional(),
263
+ position: PositionSchema.default({ x: 0, y: 0 }),
264
+ width: z4.number().default(300),
265
+ height: z4.number().default(200)
266
+ });
267
+ var ScenarioPresetRoleSchema = z4.object({
268
+ id: z4.string().min(1).max(64),
269
+ name: z4.string().min(1).max(200),
270
+ systemPrompt: z4.string().max(1e4).default(""),
271
+ prompt: z4.string().max(1e4).default("")
272
+ });
273
+ var ScenarioPresetInitialDataSchema = z4.object({
274
+ leaderId: z4.string().min(1).max(128),
275
+ groups: z4.array(z4.object({
276
+ id: z4.string().min(1).max(128),
277
+ name: z4.string().min(1).max(200),
278
+ description: z4.string().max(1e3).optional(),
279
+ color: z4.string().max(32).optional(),
280
+ position: z4.object({ x: z4.number(), y: z4.number() }).default({ x: 0, y: 0 }),
281
+ width: z4.number().default(300),
282
+ height: z4.number().default(200)
283
+ })).max(100).default([]),
284
+ nodes: z4.array(z4.object({
285
+ id: z4.string().min(1).max(128),
286
+ type: z4.enum(["ai", "ask"]).default("ai"),
287
+ parentIds: z4.array(z4.string().max(128)).default([]),
288
+ groupId: z4.string().max(128).nullable().default(null),
289
+ label: z4.string().max(240).default(""),
290
+ role: z4.string().max(64).default("other"),
291
+ prompt: z4.string().max(5e4).default(""),
292
+ question: z4.string().max(2e3).default(""),
293
+ autoGenerated: z4.boolean().default(true),
294
+ canAddNode: z4.boolean().default(false),
295
+ aiCli: z4.enum(["claude-code", "codex"]).default("claude-code"),
296
+ model: z4.string().max(128).default("sonnet"),
297
+ position: z4.object({ x: z4.number(), y: z4.number() }).default({ x: 0, y: 0 })
298
+ })).min(1).max(500)
299
+ });
300
+ var LocalizedStringSchema = z4.record(z4.string(), z4.string().max(100));
301
+ var ScenarioPresetLabelsSchema = z4.object({
302
+ group: LocalizedStringSchema.optional(),
303
+ node: LocalizedStringSchema.optional(),
304
+ role: LocalizedStringSchema.optional(),
305
+ leader: LocalizedStringSchema.optional()
306
+ });
307
+ var ScenarioPresetSchema = z4.object({
308
+ id: z4.string().min(1).max(128),
309
+ name: z4.string().min(1).max(200),
310
+ icon: z4.string().max(64).optional(),
311
+ defaultTraversalMode: z4.enum(["hierarchical", "topological"]).optional(),
312
+ canChangeTraversalMode: z4.boolean().default(true),
313
+ roles: z4.array(ScenarioPresetRoleSchema).min(1).max(100),
314
+ initialData: ScenarioPresetInitialDataSchema.optional(),
315
+ labels: ScenarioPresetLabelsSchema.optional()
316
+ });
317
+ var ScenarioNodeSchema = z4.object({
318
+ id: z4.string().min(1).max(128),
319
+ type: z4.enum(["ai", "ask"]).default("ai"),
320
+ parentIds: z4.array(z4.string().max(128)).default([]),
321
+ groupId: z4.string().max(128).nullable().default(null),
322
+ label: z4.string().max(240).default(""),
323
+ role: z4.string().max(64).default("other"),
324
+ prompt: z4.string().max(5e4).default(""),
325
+ question: z4.string().max(2e3).default(""),
326
+ autoGenerated: z4.boolean().default(true),
327
+ canAddNode: z4.boolean().default(false),
328
+ aiCli: z4.enum(["claude-code", "codex"]).default("claude-code"),
329
+ model: z4.string().max(128).default("sonnet"),
330
+ position: PositionSchema.default({ x: 0, y: 0 }),
331
+ injectParentOutputs: z4.boolean().default(false),
332
+ injectedAncestorIds: z4.array(z4.string().max(128)).default([])
333
+ });
334
+ var ScenarioSchema = z4.object({
335
+ id: z4.string().min(1).max(128),
336
+ name: z4.string().min(1).max(200),
337
+ icon: z4.string().max(64).optional(),
338
+ description: z4.string().max(2e3).optional(),
339
+ leaderId: z4.string().min(1).max(128),
340
+ groups: z4.array(ScenarioGroupSchema).max(100).default([]),
341
+ nodes: z4.array(ScenarioNodeSchema).min(1).max(500),
342
+ traversalMode: z4.enum(["hierarchical", "topological"]).default("hierarchical"),
343
+ presetId: z4.string().max(128).optional(),
344
+ isPublished: z4.boolean().default(false),
345
+ createdAt: z4.number(),
346
+ updatedAt: z4.number()
347
+ });
348
+ var AI_PROVIDERS = ["anthropic", "openai", "mistral", "groq", "ollama"];
349
+ var AIProviderSchema = z5.object({
350
+ id: z5.string().min(1).max(128),
351
+ name: z5.string().min(1).max(200),
352
+ provider: z5.enum(AI_PROVIDERS),
353
+ encryptedApiKey: z5.string().max(2e3).default(""),
354
+ model: z5.string().max(200).optional(),
355
+ active: z5.boolean().default(false),
356
+ createdAt: z5.coerce.date(),
357
+ updatedAt: z5.coerce.date()
358
+ });
359
+ var JSLogParserSchema = z5.object({
360
+ id: z5.string().min(1).max(128),
361
+ name: z5.string().min(1).max(200),
362
+ description: z5.string().max(2e3).optional(),
363
+ code: z5.string().min(1).max(5e4).refine(
364
+ (s) => !s.includes("require(") && !s.includes("import ") && !s.includes("eval("),
365
+ { message: "Code must not contain require, import, or eval patterns" }
366
+ ),
367
+ enabled: z5.boolean().default(true),
368
+ version: z5.literal(2).default(2).optional(),
369
+ builtin: z5.boolean().optional(),
370
+ createdAt: z5.coerce.date(),
371
+ updatedAt: z5.coerce.date()
372
+ });
373
+ var NotificationPreferencesSchema = z5.object({
374
+ scenario_waiting_input: z5.boolean().default(true),
375
+ ai_conversation_complete: z5.boolean().default(true),
376
+ ai_conversation_error: z5.boolean().default(true),
377
+ service_error_log: z5.boolean().default(true),
378
+ service_status_changed: z5.boolean().default(true),
379
+ service_health_changed: z5.boolean().default(true)
380
+ });
381
+ var SettingsSchema = z5.object({
382
+ mode: z5.enum(["local-simple", "cloud"]),
383
+ cloudUrl: z5.string().url().max(2048).nullable().optional(),
384
+ logParsers: z5.array(JSLogParserSchema).max(100).default([]),
385
+ scenarios: z5.array(ScenarioSchema).max(100).default([])
386
+ });
387
+ var TraefikConfigSchema = z5.object({
388
+ enabled: z5.boolean().default(false),
389
+ acmeEmail: z5.string().email().max(256).optional(),
390
+ wildcardDomain: z5.string().max(253).optional(),
391
+ httpPort: z5.number().int().min(1).max(65535).default(80),
392
+ httpsPort: z5.number().int().min(1).max(65535).default(443),
393
+ dnsProvider: z5.string().max(64).optional(),
394
+ dnsCredentials: z5.record(z5.string().max(64), z5.string().max(2048)).default({}),
395
+ acmeStaging: z5.boolean().default(false),
396
+ encryptedCert: z5.string().max(65536).optional(),
397
+ encryptedCertKey: z5.string().max(65536).optional()
398
+ });
399
+ var SettingsPrivateSchema = z5.object({
400
+ aiProviders: z5.array(AIProviderSchema).max(50).default([]),
401
+ scenarios: z5.array(ScenarioSchema).max(100).default([]),
402
+ notifications: NotificationPreferencesSchema.default(() => ({
403
+ scenario_waiting_input: true,
404
+ ai_conversation_complete: true,
405
+ ai_conversation_error: true,
406
+ service_error_log: true,
407
+ service_status_changed: true,
408
+ service_health_changed: true
409
+ })),
410
+ traefikConfig: TraefikConfigSchema.optional()
411
+ });
412
+ var SchemaVersionSchema = z5.object({
413
+ version: z5.number().int().positive(),
414
+ migratedAt: z5.coerce.date()
415
+ });
416
+ var safePath = z6.string().min(1).max(4096).refine(
417
+ (p) => !p.includes("\0"),
418
+ { message: "path must not contain null bytes" }
419
+ ).refine(
420
+ (p) => !/(^|[/\\])\.\.([/\\]|$)/.test(p),
421
+ { message: "path must not contain path-traversal sequences (..)" }
422
+ );
423
+ var ConsoleLogSourceSchema = z6.object({
424
+ type: z6.literal("console")
425
+ });
426
+ var FileLogSourceSchema = z6.object({
427
+ type: z6.literal("file"),
428
+ path: safePath,
429
+ encoding: z6.string().max(32).default("utf-8"),
430
+ startFromEnd: z6.boolean().default(true)
431
+ });
432
+ var MongoLogSourceSchema = z6.object({
433
+ type: z6.literal("mongo"),
434
+ /** @sensitive — stored encrypted (AES-256-GCM) on disk */
435
+ connectionString: z6.string().min(1).max(2048),
436
+ database: z6.string().min(1).max(256),
437
+ collection: z6.string().min(1).max(256),
438
+ filter: z6.record(z6.string(), z6.unknown()).optional(),
439
+ timestampField: z6.string().max(128).default("timestamp"),
440
+ messageField: z6.string().max(128).default("message"),
441
+ levelField: z6.string().max(128).optional()
442
+ });
443
+ var MySQLLogSourceSchema = z6.object({
444
+ type: z6.literal("mysql"),
445
+ host: z6.string().min(1).max(253),
446
+ port: z6.number().int().min(1).max(65535).default(3306),
447
+ user: z6.string().min(1).max(256),
448
+ /** @sensitive — stored encrypted (AES-256-GCM) on disk */
449
+ password: z6.string().max(2048),
450
+ database: z6.string().min(1).max(256),
451
+ table: z6.string().min(1).max(256),
452
+ query: z6.string().max(8192).optional(),
453
+ timestampColumn: z6.string().max(128).default("created_at"),
454
+ messageColumn: z6.string().max(128).default("message"),
455
+ levelColumn: z6.string().max(128).optional(),
456
+ pollIntervalMs: z6.number().int().min(500).max(6e4).default(2e3)
457
+ });
458
+ var LogSourceConfigSchema = z6.discriminatedUnion("type", [
459
+ ConsoleLogSourceSchema,
460
+ FileLogSourceSchema,
461
+ MongoLogSourceSchema,
462
+ MySQLLogSourceSchema
463
+ ]);
464
+ var HealthCheckSchema = z7.object({
465
+ type: z7.enum(["http", "command"]).default("http"),
466
+ // HTTP-specific (required when type=http)
467
+ path: z7.string().min(1).max(500).startsWith("/").optional(),
468
+ method: z7.enum(["GET", "HEAD", "POST", "PUT"]).optional().default("GET"),
469
+ expectedStatus: z7.number().int().min(100).max(599).optional().default(200),
470
+ // Command-specific (required when type=command)
471
+ command: z7.string().min(1).max(4096).optional(),
472
+ // Shared
473
+ interval: z7.number().int().positive().default(1e3),
474
+ timeout: z7.number().int().positive().default(1e3),
475
+ retries: z7.number().int().nonnegative().default(3),
476
+ initialDelay: z7.number().int().nonnegative().optional().default(0)
477
+ }).refine(
478
+ (data) => data.type === "http" ? !!data.path : !!data.command,
479
+ { message: "HTTP type requires path; command type requires command" }
480
+ );
481
+ var BootstrapCommandSchema = z7.object({
482
+ id: z7.string().min(1).max(64),
483
+ label: z7.string().max(200).default(""),
484
+ entrypoint: z7.string().max(4096).optional(),
485
+ command: z7.string().max(4096).default(""),
486
+ user: z7.string().max(256).optional()
487
+ });
488
+ var DockerConfigSchema = z7.object({
489
+ // Image mode (pull from registry) — mutually exclusive with build mode in UI
490
+ image: z7.string().min(1).max(1024).optional(),
491
+ // Build mode — Dockerfile stored inline in services.json
492
+ dockerfile: z7.string().max(65536).optional(),
493
+ buildContext: z7.string().max(4096).optional(),
494
+ buildArgs: z7.record(z7.string().max(256), z7.string().max(4096)).default({}).refine(
495
+ (obj) => Object.keys(obj).length <= 50,
496
+ { message: "Max 50 build args" }
497
+ ),
498
+ targetStage: z7.string().max(256).optional(),
499
+ // Runtime config
500
+ networkMode: z7.enum(["host", "bridge"]).default("host"),
501
+ ports: z7.array(z7.string().max(64)).max(100).default([]),
502
+ volumes: z7.array(z7.string().max(1024)).max(100).default([]),
503
+ mountRootpath: z7.boolean().default(false),
504
+ entrypoint: z7.string().max(4096).optional(),
505
+ command: z7.string().max(4096).optional(),
506
+ user: z7.string().max(256).optional(),
507
+ useNativeCommand: z7.boolean().default(false),
508
+ nativeCommandMode: z7.enum(["shared", "isolated"]).default("isolated"),
509
+ nativeShell: z7.string().max(256).default("sh"),
510
+ bootstrapCommands: z7.array(BootstrapCommandSchema).max(20).default([])
511
+ });
512
+ var RestartStrategySchema = z7.enum(["none", "on-failure", "always", "unless-stopped"]).default("none");
513
+ var ServiceShortcutSchema = z7.object({
514
+ id: z7.string().min(1).max(64),
515
+ label: z7.string().max(200).default(""),
516
+ command: z7.string().max(4096).default(""),
517
+ cwd: z7.string().max(4096).optional()
518
+ });
519
+ var ServiceCommandSchema = z7.object({
520
+ id: z7.string().min(1).max(64),
521
+ label: z7.string().max(200).default(""),
522
+ command: z7.string().max(4096).default(""),
523
+ cwd: z7.string().max(4096).optional(),
524
+ shell: z7.boolean().optional().default(true)
525
+ });
526
+ var VariableSlotSchema = z7.object({
527
+ value: z7.string().max(8192).default(""),
528
+ pre: z7.string().max(8192).default(""),
529
+ post: z7.string().max(8192).default(""),
530
+ isSecret: z7.boolean().default(false)
531
+ });
532
+ var VariableSchema = z7.object({
533
+ key: z7.string().min(1).max(256),
534
+ value: VariableSlotSchema
535
+ });
536
+ var VariablesRecordSchema = z7.record(
537
+ z7.string().max(128),
538
+ VariableSchema
539
+ ).refine(
540
+ (obj) => Object.keys(obj).length <= 200,
541
+ { message: "Max 200 variables" }
542
+ );
543
+ var VariableSlotsRecordSchema = z7.record(
544
+ z7.string().max(256),
545
+ VariableSlotSchema
546
+ ).refine(
547
+ (obj) => Object.keys(obj).length <= 200,
548
+ { message: "Max 200 variable slots" }
549
+ );
550
+ var ServiceConfigSchema = z7.object({
551
+ id: z7.string().max(128),
552
+ orgId: z7.string().max(128).nullable().optional(),
553
+ agentId: z7.string().max(128).optional(),
554
+ // Optional: if absent, inherited from active environment's agentId
555
+ name: z7.string().min(1).max(200),
556
+ commands: z7.array(ServiceCommandSchema).max(50).optional().default([]),
557
+ cwd: z7.string().max(4096).optional(),
558
+ ports: z7.array(z7.number().int().positive().max(65535)).max(20).optional().default([]),
559
+ // Metadata fields (migration from old Runeya)
560
+ groups: z7.array(z7.string().max(200)).max(20).optional().default([]),
561
+ description: z7.string().max(2e3).optional(),
562
+ url: z7.string().max(2048).optional(),
563
+ localUrls: z7.array(z7.object({
564
+ url: z7.string().max(253).regex(/^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/),
565
+ port: z7.number().int().min(1).max(65535)
566
+ })).max(20).optional().default([]),
567
+ openapiUrl: z7.string().max(2048).optional(),
568
+ git: z7.object({
569
+ remote: z7.string().max(2048).optional(),
570
+ home: z7.string().max(2048).optional()
571
+ }).optional(),
572
+ meta: z7.record(z7.string().max(256), z7.string().max(2e3)).optional().refine((obj) => !obj || Object.keys(obj).length <= 100, { message: "Too many meta keys (max 100)" }),
573
+ // Transient env dict (resolved by server before deployment, used directly by agent runner — NOT persisted)
574
+ env: z7.record(z7.string().max(256), z7.string().max(8192)).default({}).refine(
575
+ (obj) => Object.keys(obj).length <= 200,
576
+ { message: "Too many env vars (max 200)" }
577
+ ),
578
+ envSecrets: z7.array(z7.string().max(256)).max(200).default([]),
579
+ // Transient: resolved JS log parsers (from parserIds, resolved by server before deployment — NOT persisted)
580
+ resolvedLogParsers: z7.array(JSLogParserSchema).default([]).optional(),
581
+ // Transient: resolved working directory (cwd with env vars expanded — NOT persisted)
582
+ resolvedCwd: z7.string().optional(),
583
+ runner: z7.enum(["native", "docker"]).optional().default("native"),
584
+ dockerConfig: DockerConfigSchema.optional(),
585
+ healthCheck: HealthCheckSchema.optional(),
586
+ parserIds: z7.array(z7.string().max(128)).max(50).default([]),
587
+ shortcuts: z7.array(ServiceShortcutSchema).max(50).optional().default([]),
588
+ autoRestart: z7.boolean().optional().default(false),
589
+ clearLogsOnStart: z7.boolean().optional().default(false),
590
+ restartStrategy: RestartStrategySchema.optional(),
591
+ maxRestarts: z7.number().int().nonnegative().optional(),
592
+ restartBackoffMs: z7.number().int().positive().optional().default(1e3),
593
+ // Log sources additionnelles (au-delà du stdout/stderr console par défaut)
594
+ logSources: z7.array(LogSourceConfigSchema).max(20).optional().default([])
595
+ });
596
+ var CreateServiceSchema = ServiceConfigSchema.omit({ id: true }).partial().required({ name: true });
597
+ var UpdateServiceSchema = ServiceConfigSchema.partial().required({ id: true }).extend({
598
+ agentId: z7.string().max(128).nullable().optional(),
599
+ // null = clear (inherit from env), undefined = no change
600
+ cwd: z7.string().max(4096).nullable().optional(),
601
+ git: z7.object({ remote: z7.string().max(2048).optional(), home: z7.string().max(2048).optional() }).nullable().optional(),
602
+ meta: z7.record(z7.string().max(256), z7.string().max(2e3)).nullable().optional().refine((obj) => !obj || Object.keys(obj).length <= 100, { message: "Too many meta keys (max 100)" }),
603
+ healthCheck: HealthCheckSchema.nullable().optional()
604
+ });
605
+ var ProcessStateSchema = z8.enum(["starting", "running", "stopped", "errored"]);
606
+ var HealthStatusSchema = z8.enum(["healthy", "unhealthy", "unknown"]);
607
+ var ProcessStatusSchema = z8.object({
608
+ id: z8.string().max(128),
609
+ serviceId: z8.string().max(128),
610
+ agentId: z8.string().max(128),
611
+ state: ProcessStateSchema,
612
+ pid: z8.number().int().nullable().optional(),
613
+ pids: z8.array(z8.number().int()).optional().default([]),
614
+ exitCode: z8.number().int().nullable().optional(),
615
+ startedAt: z8.coerce.date().nullable().optional(),
616
+ restartCount: z8.number().int().nonnegative().default(0),
617
+ uptime: z8.number().nonnegative().default(0),
618
+ healthStatus: HealthStatusSchema.default("unknown")
619
+ });
620
+ var LogLevelSchema = z9.enum(["debug", "info", "warn", "error", "fatal"]);
621
+ var LogLineSchema = z9.object({
622
+ /** Raw log line as received from the process (immutable context) */
623
+ raw: z9.string(),
624
+ /** Source stream (immutable context) */
625
+ stream: z9.enum(["stdout", "stderr"]),
626
+ /** Severity level */
627
+ level: LogLevelSchema,
628
+ /** Parsed/enriched message to display */
629
+ message: z9.string(),
630
+ /** When true, the line is hidden from the log view */
631
+ hidden: z9.boolean(),
632
+ /** Arbitrary key/value pairs shown in the detail panel */
633
+ metadata: z9.record(z9.string().max(256), z9.unknown()).refine(
634
+ (obj) => Object.keys(obj).length <= 100,
635
+ { message: "Too many metadata keys (max 100)" }
636
+ ),
637
+ /** Full parsed JSON object when the line is JSON */
638
+ json: z9.record(z9.string(), z9.unknown()).optional(),
639
+ /** Custom header for the JSON log card (supports HTML, sanitized before display) */
640
+ jsonHeader: z9.string().max(256).optional()
641
+ });
642
+ var LogCardDirectiveSchema = z9.object({
643
+ id: z9.string().max(128),
644
+ type: z9.string().max(64),
645
+ header: z9.string().max(256),
646
+ collapsable: z9.boolean().optional(),
647
+ collapsed: z9.boolean().optional(),
648
+ state: z9.enum(["open", "append", "close", "self"])
649
+ });
650
+ var ParsedLogSchema = z9.object({
651
+ id: z9.string().max(128),
652
+ processId: z9.string().max(128),
653
+ pid: z9.number().int().nullable().optional(),
654
+ timestamp: z9.coerce.date(),
655
+ level: LogLevelSchema.default("info"),
656
+ message: z9.string().max(16384),
657
+ raw: z9.string().max(16384),
658
+ stream: z9.enum(["stdout", "stderr"]).default("stdout"),
659
+ hidden: z9.boolean().optional().default(false),
660
+ metadata: z9.record(z9.string().max(256), z9.unknown()).optional().refine(
661
+ (obj) => !obj || Object.keys(obj).length <= 100,
662
+ { message: "Too many metadata keys (max 100)" }
663
+ ),
664
+ json: z9.record(z9.string(), z9.unknown()).optional(),
665
+ logCard: LogCardDirectiveSchema.optional(),
666
+ sourceType: z9.enum(["console", "file", "mongo", "mysql"]).optional(),
667
+ sourcePath: z9.string().max(4096).optional()
668
+ });
669
+ var ENV_VAR_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
670
+ var AgentConfigSchema = z10.object({
671
+ id: z10.string().max(128),
672
+ name: z10.string().min(1).max(100),
673
+ url: z10.string().url().max(2048),
674
+ passphrase: z10.string().min(1).max(1024),
675
+ passthroughEnv: z10.array(
676
+ z10.string().regex(ENV_VAR_NAME_REGEX).max(256)
677
+ ).max(100).default([])
678
+ });
679
+ var AgentStatusSchema = z10.object({
680
+ connected: z10.boolean(),
681
+ lastSeen: z10.string().nullable().optional(),
682
+ uptime: z10.number().nonnegative().default(0),
683
+ runnersAvailable: z10.array(z10.enum(["native", "docker"])).default(["native"]),
684
+ lanIp: z10.string().optional()
685
+ });
686
+ var GlobalRoleSchema = z11.enum(["owner", "admin", "member"]);
687
+ var ProjectRoleSchema = z11.enum(["admin", "editor", "viewer"]);
688
+ var ProjectMemberRoleSchema = z11.object({
689
+ id: z11.string().max(128),
690
+ projectId: z11.string().max(128),
691
+ userId: z11.string().max(128),
692
+ role: ProjectRoleSchema
693
+ });
694
+ var ConfigVersionSchema = z12.object({
695
+ id: z12.string().max(128),
696
+ entityType: z12.enum(["project", "service"]),
697
+ entityId: z12.string().max(128),
698
+ version: z12.string().max(128),
699
+ parentVersion: z12.string().max(128),
700
+ data: z12.record(z12.string(), z12.unknown()).refine(
701
+ (obj) => Object.keys(obj).length <= 1e3,
702
+ "Too many data keys (max 1000)"
703
+ ),
704
+ patch: z12.array(z12.record(z12.string(), z12.unknown())).max(1e3),
705
+ userId: z12.string().max(128),
706
+ timestamp: z12.coerce.date(),
707
+ resolved: z12.boolean().default(false)
708
+ });
709
+ var ConfigChangeSchema = z12.object({
710
+ entityType: z12.enum(["project", "service"]),
711
+ entityId: z12.string().max(128),
712
+ version: z12.string().max(128),
713
+ parentVersion: z12.string().max(128),
714
+ data: z12.record(z12.string(), z12.unknown()).refine(
715
+ (obj) => Object.keys(obj).length <= 1e3,
716
+ "Too many data keys (max 1000)"
717
+ ),
718
+ patch: z12.array(z12.record(z12.string(), z12.unknown())).max(1e3)
719
+ });
720
+ var SyncStatusSchema = z12.object({
721
+ connected: z12.boolean(),
722
+ lastSync: z12.coerce.date().nullable().optional(),
723
+ pendingChanges: z12.number().int().nonnegative().default(0),
724
+ conflicts: z12.number().int().nonnegative().default(0)
725
+ });
726
+ var AuditActionSchema = z13.enum([
727
+ "create",
728
+ "update",
729
+ "delete",
730
+ "invite",
731
+ "remove",
732
+ "deploy",
733
+ "start",
734
+ "stop",
735
+ "roleChange"
736
+ ]);
737
+ var AuditEntityTypeSchema = z13.enum([
738
+ "org",
739
+ "project",
740
+ "service",
741
+ "member",
742
+ "permission"
743
+ ]);
744
+ var AuditLogSchema = z13.object({
745
+ id: z13.string().max(128),
746
+ orgId: z13.string().max(128),
747
+ userId: z13.string().max(128),
748
+ action: AuditActionSchema,
749
+ entityType: AuditEntityTypeSchema,
750
+ entityId: z13.string().max(128),
751
+ metadata: z13.record(z13.string().max(256), z13.unknown()).default({}).refine(
752
+ (obj) => Object.keys(obj).length <= 100,
753
+ { message: "Too many metadata keys (max 100)" }
754
+ ),
755
+ timestamp: z13.coerce.date()
756
+ });
757
+ var ProcessMetricsSchema = z14.object({
758
+ processId: z14.string().max(128),
759
+ cpu: z14.number().nonnegative(),
760
+ memory: z14.number().int().nonnegative(),
761
+ timestamp: z14.string()
762
+ });
763
+ var EnvironmentVariablesSchema = z15.record(
764
+ z15.string().max(256),
765
+ VariableSchema
766
+ ).refine(
767
+ (obj) => Object.keys(obj).length <= 200,
768
+ { message: "Max 200 variables" }
769
+ );
770
+ var EnvironmentSchema = z15.object({
771
+ id: z15.string().max(128),
772
+ projectId: z15.string().max(128),
773
+ name: z15.string().min(1).max(200),
774
+ description: z15.string().max(2e3).optional().default(""),
775
+ parentIds: z15.array(z15.string().max(128)).max(10).default([]),
776
+ variables: EnvironmentVariablesSchema.default({}),
777
+ // Scope: 'global' = project-level env, 'service' = per-service env
778
+ scope: z15.enum(["global", "service"]).default("global"),
779
+ // When scope='service': the serviceId this env belongs to
780
+ reference: z15.string().max(128).optional(),
781
+ // When scope='service': the globalEnvId this env activates on
782
+ activateOn: z15.string().max(128).optional(),
783
+ immutable: z15.boolean().default(false),
784
+ default: z15.boolean().optional().default(false),
785
+ color: z15.string().max(20).optional(),
786
+ bgColor: z15.string().max(20).optional(),
787
+ // When scope='global': the default agent used by services that don't have an explicit agentId
788
+ agentId: z15.string().max(128).optional()
789
+ });
790
+ var ResolvedEnvironmentSchema = z15.object({
791
+ variables: z15.record(z15.string().max(256), VariableSchema).refine((obj) => Object.keys(obj).length <= 500, { message: "Max 500 resolved variables" }),
792
+ env: z15.record(z15.string().max(256), z15.string().max(8192)).refine((obj) => Object.keys(obj).length <= 500, { message: "Max 500 resolved env vars" }),
793
+ envSecrets: z15.array(z15.string().max(256)).max(500),
794
+ sources: z15.record(z15.string().max(256), z15.object({
795
+ environmentId: z15.string().max(128),
796
+ environmentName: z15.string().max(200),
797
+ overridden: z15.boolean(),
798
+ resolvedFrom: z15.object({
799
+ environmentId: z15.string().max(128),
800
+ environmentName: z15.string().max(200)
801
+ }).optional()
802
+ })).refine((obj) => Object.keys(obj).length <= 500, { message: "Max 500 source entries" })
803
+ });
804
+ var CreateEnvironmentSchema = EnvironmentSchema.omit({ id: true }).extend({
805
+ description: z15.string().max(2e3).optional().default(""),
806
+ parentIds: z15.array(z15.string().max(128)).max(10).optional().default([]),
807
+ variables: EnvironmentVariablesSchema.optional().default({}),
808
+ scope: z15.enum(["global", "service"]).optional().default("global"),
809
+ reference: z15.string().max(128).optional(),
810
+ activateOn: z15.string().max(128).optional(),
811
+ immutable: z15.boolean().optional().default(false)
812
+ });
813
+ var UpdateEnvironmentSchema = EnvironmentSchema.partial().required({ id: true }).extend({
814
+ description: z15.string().max(2e3).optional(),
815
+ parentIds: z15.array(z15.string().max(128)).max(10).optional(),
816
+ variables: EnvironmentVariablesSchema.optional(),
817
+ scope: z15.enum(["global", "service"]).optional(),
818
+ immutable: z15.boolean().optional(),
819
+ default: z15.boolean().optional(),
820
+ agentId: z15.string().max(128).nullable().optional()
821
+ // null = clear (no default agent)
822
+ });
823
+ var EnvironmentOverridesSchema = z16.record(
824
+ z16.string().max(128),
825
+ VariableSchema
826
+ ).refine(
827
+ (obj) => Object.keys(obj).length <= 5e3,
828
+ { message: "Max 5000 overrides" }
829
+ );
830
+ var ApiServiceActionSchema = z17.enum([
831
+ "service:read",
832
+ "service:start",
833
+ "service:stop",
834
+ "service:restart",
835
+ "service:healthcheck",
836
+ "service:status",
837
+ "logs:read"
838
+ ]);
839
+ var ApiEnvActionSchema = z17.enum(["env:read", "env:write"]);
840
+ var ApiEnvPermSchema = z17.object({
841
+ environmentId: z17.string().max(128),
842
+ actions: z17.array(ApiEnvActionSchema).max(10).default(["env:read"]),
843
+ includeSecrets: z17.boolean().default(false)
844
+ });
845
+ var ApiServicePermSchema = z17.object({
846
+ serviceId: z17.string().max(128),
847
+ actions: z17.array(ApiServiceActionSchema).max(10).default(["service:read"]),
848
+ environments: z17.array(ApiEnvPermSchema).max(100).default([])
849
+ });
850
+ var ApiKeyScopeSchema = z17.object({
851
+ services: z17.array(ApiServicePermSchema).max(500).default([])
852
+ });
853
+ var ApiKeySchema = z17.object({
854
+ id: z17.string().max(128),
855
+ name: z17.string().min(1).max(200),
856
+ key: z17.string().regex(/^[0-9a-f]{64}$/i),
857
+ scopes: ApiKeyScopeSchema,
858
+ createdAt: z17.string().max(64),
859
+ isPublic: z17.boolean().default(false)
860
+ });
861
+ var ApiKeysFileSchema = z17.object({
862
+ keys: z17.array(ApiKeySchema).max(100)
863
+ });
864
+ var ALLOWED_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
865
+ var ImagePartSchema = z18.object({
866
+ type: z18.literal("image"),
867
+ imageId: z18.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "Must be a valid UUID"),
868
+ url: z18.string().min(1),
869
+ mimeType: z18.enum(ALLOWED_IMAGE_MIME_TYPES),
870
+ altText: z18.string().max(500).optional()
871
+ });
872
+ var ImageUploadResponseSchema = z18.object({
873
+ imageId: z18.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "Must be a valid UUID"),
874
+ url: z18.string().min(1),
875
+ mimeType: z18.enum(ALLOWED_IMAGE_MIME_TYPES),
876
+ originalName: z18.string()
877
+ });
878
+ var ImageMetaSchema = z18.object({
879
+ imageId: z18.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "Must be a valid UUID"),
880
+ filePath: z18.string().min(1),
881
+ mimeType: z18.enum(ALLOWED_IMAGE_MIME_TYPES),
882
+ originalName: z18.string().min(1),
883
+ createdAt: z18.number().positive(),
884
+ size: z18.number().positive().optional()
885
+ });
886
+ var KanbanCardEventSchema = z19.object({
887
+ type: z19.enum(["created", "edited", "moved", "image_added", "image_removed", "title_changed", "conversation_started", "scenario_started", "comment"]),
888
+ timestamp: z19.string(),
889
+ /** Extra context: old/new column name, old/new title, comment text, etc. */
890
+ detail: z19.string().max(5e3).optional(),
891
+ /** Who authored this event — 'ai' or 'user'. Only set for comment events. */
892
+ author: z19.enum(["ai", "user"]).optional(),
893
+ /** Conversation that produced this event (comments only). Used to navigate back to the conversation. */
894
+ conversationId: z19.string().optional()
895
+ });
896
+ var KanbanCardSchema = z19.object({
897
+ id: z19.string(),
898
+ title: z19.string().min(1).max(500),
899
+ markdown: z19.string().max(1e5).default(""),
900
+ /** Base64-encoded image data URIs attached to the card */
901
+ images: z19.array(z19.string()).default([]),
902
+ order: z19.number().int(),
903
+ history: z19.array(KanbanCardEventSchema).default([]),
904
+ createdAt: z19.string(),
905
+ updatedAt: z19.string()
906
+ });
907
+ var KanbanColumnSchema = z19.object({
908
+ id: z19.string(),
909
+ title: z19.string().min(1).max(200),
910
+ order: z19.number().int(),
911
+ cards: z19.array(KanbanCardSchema).default([])
912
+ });
913
+ var KanbanBoardSchema = z19.object({
914
+ id: z19.string(),
915
+ name: z19.string().min(1).max(200),
916
+ description: z19.string().max(1e4).default(""),
917
+ columns: z19.array(KanbanColumnSchema).default([]),
918
+ archived: z19.boolean().default(false),
919
+ createdAt: z19.string(),
920
+ updatedAt: z19.string()
921
+ });
922
+ var CreateKanbanBoardInput = z19.object({
923
+ name: z19.string().min(1).max(200),
924
+ description: z19.string().max(1e4).optional()
925
+ });
926
+ var UpdateKanbanBoardInput = z19.object({
927
+ id: z19.string(),
928
+ name: z19.string().min(1).max(200).optional(),
929
+ description: z19.string().max(1e4).optional(),
930
+ columns: z19.array(KanbanColumnSchema).optional(),
931
+ archived: z19.boolean().optional()
932
+ });
933
+ var epoch = /* @__PURE__ */ new Date("2025-01-01T00:00:00Z");
934
+ var nativeParsers = [
935
+ {
936
+ id: "native:json",
937
+ name: "JSON Log Parser",
938
+ description: "Parses JSON log lines and extracts level, message, timestamp, metadata, and the full parsed object.",
939
+ code: `(line) => {
940
+ var trimmed = line.message.trim();
941
+ if (!trimmed.startsWith('{')) return line;
942
+ try {
943
+ var obj = JSON.parse(trimmed);
944
+ var level = (obj.level || obj.severity || obj.loglevel || '').toString().toLowerCase();
945
+ var levelMap = { trace: 'debug', debug: 'debug', info: 'info', warn: 'warn', warning: 'warn', error: 'error', fatal: 'fatal', err: 'error' };
946
+ var mappedLevel = levelMap[level] || line.level;
947
+ var message = obj.message || obj.msg || obj.text || trimmed;
948
+ var metadata = {};
949
+ for (var k in obj) {
950
+ if (['level','severity','loglevel','message','msg','text','timestamp','time','ts'].indexOf(k) !== -1) continue;
951
+ metadata[k] = typeof obj[k] === 'string' ? obj[k] : JSON.stringify(obj[k]);
952
+ }
953
+ var merged = {};
954
+ for (var mk in line.metadata) merged[mk] = line.metadata[mk];
955
+ for (var nk in metadata) merged[nk] = metadata[nk];
956
+ return { raw: line.raw, stream: line.stream, level: mappedLevel, message: String(message).slice(0, 16384), hidden: line.hidden, metadata: merged, json: obj, jsonHeader: line.jsonHeader };
957
+ } catch (e) { return line; }
958
+ }`,
959
+ enabled: true,
960
+ version: 2,
961
+ builtin: true,
962
+ createdAt: epoch,
963
+ updatedAt: epoch
964
+ },
965
+ {
966
+ id: "native:debug",
967
+ name: "Runeya Debug Parser",
968
+ description: 'Detects Runeya internal debug logs emitted as JSON arrays: ["runeya", payload]. Marks them as debug level.',
969
+ code: `(line) => {
970
+ var trimmed = line.message.trim();
971
+ if (!trimmed.startsWith('[')) return line;
972
+ try {
973
+ var parsed = JSON.parse(trimmed);
974
+ if (!Array.isArray(parsed) || parsed[0] !== 'runeya') return line;
975
+ var debug = parsed.length === 2 ? parsed[1] : parsed.slice(1);
976
+ var message = typeof debug === 'string' ? debug : JSON.stringify(debug);
977
+ var merged = {};
978
+ for (var k in line.metadata) merged[k] = line.metadata[k];
979
+ merged.runeya_debug = JSON.stringify(debug);
980
+ return { raw: line.raw, stream: line.stream, level: 'debug', message: message, hidden: line.hidden, metadata: merged, jsonHeader: line.jsonHeader };
981
+ } catch (e) { return line; }
982
+ }`,
983
+ enabled: true,
984
+ version: 2,
985
+ builtin: true,
986
+ createdAt: epoch,
987
+ updatedAt: epoch
988
+ },
989
+ {
990
+ id: "native:links",
991
+ name: "Link Detector",
992
+ description: "Detects URLs in plain-text log lines and exposes them in metadata.links for frontend rendering.",
993
+ code: `(line) => {
994
+ var urlRegex = /(https?|ftp):\\/\\/[\\w_-]+(?:\\.[\\w_-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?/gi;
995
+ var urls = line.message.match(urlRegex);
996
+ if (!urls || urls.length === 0) return line;
997
+ var merged = {};
998
+ for (var k in line.metadata) merged[k] = line.metadata[k];
999
+ merged.links = urls.join(' ');
1000
+ return { raw: line.raw, stream: line.stream, level: line.level, message: line.message, hidden: line.hidden, metadata: merged, jsonHeader: line.jsonHeader };
1001
+ }`,
1002
+ enabled: true,
1003
+ version: 2,
1004
+ builtin: true,
1005
+ createdAt: epoch,
1006
+ updatedAt: epoch
1007
+ }
1008
+ ];
1009
+ var VALID_LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error", "fatal"]);
1010
+ var JSLogParser = class {
1011
+ parsers = [];
1012
+ constructor(definitions = []) {
1013
+ this.setParsers(definitions);
1014
+ }
1015
+ setParsers(definitions) {
1016
+ this.parsers = [];
1017
+ for (const def of definitions) {
1018
+ if (!def.enabled) continue;
1019
+ try {
1020
+ const compiled = this.compileParser(def);
1021
+ this.parsers.push({
1022
+ id: def.id,
1023
+ name: def.name,
1024
+ enabled: true,
1025
+ fn: compiled
1026
+ });
1027
+ } catch (err) {
1028
+ console.warn(
1029
+ `[js-log-parser] Failed to compile parser "${def.name}" (${def.id}): ${err instanceof Error ? err.message : String(err)}`
1030
+ );
1031
+ }
1032
+ }
1033
+ }
1034
+ parse(line) {
1035
+ let current = line;
1036
+ for (const parser of this.parsers) {
1037
+ try {
1038
+ current = parser.fn(current);
1039
+ } catch (err) {
1040
+ console.warn(
1041
+ `[js-log-parser] Parser "${parser.name}" threw error: ${err instanceof Error ? err.message : String(err)}`
1042
+ );
1043
+ }
1044
+ }
1045
+ return this.sanitize(current);
1046
+ }
1047
+ compileParser(def) {
1048
+ const forbidden = ["require(", "import ", "eval("];
1049
+ for (const pat of forbidden) {
1050
+ if (def.code.includes(pat)) {
1051
+ throw new Error("Code contains forbidden patterns (require, import, eval)");
1052
+ }
1053
+ }
1054
+ const sandbox = {
1055
+ RegExp,
1056
+ String,
1057
+ Object,
1058
+ Array,
1059
+ JSON,
1060
+ Math,
1061
+ Date,
1062
+ undefined: void 0,
1063
+ null: null
1064
+ };
1065
+ let script;
1066
+ try {
1067
+ script = new vm.Script(`(${def.code})`, { filename: `parser-${def.id}.js` });
1068
+ } catch (err) {
1069
+ throw new Error(`Compilation failed: ${err instanceof Error ? err.message : String(err)}`);
1070
+ }
1071
+ let parserFn;
1072
+ try {
1073
+ parserFn = script.runInNewContext(sandbox, { timeout: 100 });
1074
+ } catch (err) {
1075
+ throw new Error(`Execution failed: ${err instanceof Error ? err.message : String(err)}`);
1076
+ }
1077
+ if (typeof parserFn !== "function") {
1078
+ throw new Error("Parser code must export a function");
1079
+ }
1080
+ return (line) => {
1081
+ const result = parserFn(line);
1082
+ if (result === null || result === void 0 || typeof result !== "object") {
1083
+ return line;
1084
+ }
1085
+ return result;
1086
+ };
1087
+ }
1088
+ /** Sanitize the final LogLine: truncate message, limit metadata, validate level */
1089
+ sanitize(line) {
1090
+ let { message, metadata, level } = line;
1091
+ if (message.length > 16384) {
1092
+ message = message.substring(0, 16384) + "...[truncated]";
1093
+ }
1094
+ const metaKeys = Object.keys(metadata);
1095
+ if (metaKeys.length > 100) {
1096
+ const limited = {};
1097
+ for (let i = 0; i < 100; i++) {
1098
+ limited[metaKeys[i]] = metadata[metaKeys[i]];
1099
+ }
1100
+ metadata = limited;
1101
+ }
1102
+ if (!VALID_LEVELS.has(level)) {
1103
+ level = "info";
1104
+ }
1105
+ if (message !== line.message || metadata !== line.metadata || level !== line.level) {
1106
+ return { ...line, message, metadata, level };
1107
+ }
1108
+ return line;
1109
+ }
1110
+ };
1111
+ var AGENT_HEALTH_POLL_INTERVAL = 15e3;
1112
+ var LOG_BUFFER_SIZE = 5e3;
1113
+ var GRACE_PERIOD = 1e4;
1114
+ var AGENT_FETCH_TIMEOUT = 1e4;
1115
+ var AGENT_LONG_FETCH_TIMEOUT = 6e5;
1116
+ var AGENT_STOP_FETCH_TIMEOUT = GRACE_PERIOD + 15e3;
1117
+ var PASSPHRASE_TIMEOUT = 3e4;
1118
+ var WS_RECONNECT_MIN = 1e3;
1119
+ var WS_RECONNECT_MAX = 15e3;
1120
+ var SUPPORTED_EDITORS = [
1121
+ { key: "vscode", name: "Visual Studio Code", command: "code" },
1122
+ { key: "cursor", name: "Cursor", command: "cursor" },
1123
+ { key: "windsurf", name: "Windsurf", command: "windsurf" },
1124
+ { key: "sublime", name: "Sublime Text", command: "subl" },
1125
+ { key: "atom", name: "Atom", command: "atom" },
1126
+ { key: "antigravity", name: "Antigravity", command: "antigravity" },
1127
+ { key: "intellij", name: "IntelliJ IDEA", command: "idea" },
1128
+ { key: "android-studio", name: "Android Studio", command: "studio" },
1129
+ { key: "webstorm", name: "WebStorm", command: "webstorm" },
1130
+ { key: "zed", name: "Zed", command: "zed" },
1131
+ { key: "neovim", name: "Neovim", command: "nvim" },
1132
+ { key: "vim", name: "Vim", command: "vim" },
1133
+ { key: "emacs", name: "Emacs", command: "emacs" },
1134
+ { key: "fleet", name: "Fleet", command: "fleet" }
1135
+ ];
1136
+ var EDITOR_BY_KEY = new Map(
1137
+ SUPPORTED_EDITORS.map((e) => [e.key, e])
1138
+ );
1139
+ var FILENAME2 = "environments.json";
1140
+ var EnvironmentStore = class {
1141
+ environments = /* @__PURE__ */ new Map();
1142
+ loaded = false;
1143
+ /** Cache of encrypted secret values as last written to disk */
1144
+ diskEncryptedSlots = /* @__PURE__ */ new Map();
1145
+ getFilePath() {
1146
+ return join2(env.DATA_DIR, FILENAME2);
1147
+ }
1148
+ async ensureDir() {
1149
+ await mkdir2(env.DATA_DIR, { recursive: true });
1150
+ }
1151
+ extractEncryptedCache(environment) {
1152
+ const envSlots = extractEncryptedVariableSlots(
1153
+ environment.variables
1154
+ );
1155
+ return { envSlots };
1156
+ }
1157
+ encryptEnvironment(environment) {
1158
+ const cache = this.diskEncryptedSlots.get(environment.id);
1159
+ let encryptedVars = environment.variables;
1160
+ if (environment.variables && Object.keys(environment.variables).length > 0) {
1161
+ encryptedVars = encryptVariableSlots(
1162
+ environment.variables,
1163
+ cache?.envSlots
1164
+ );
1165
+ }
1166
+ return { ...environment, variables: encryptedVars };
1167
+ }
1168
+ decryptEnvironment(environment) {
1169
+ let decryptedVars = environment.variables;
1170
+ if (environment.variables && Object.keys(environment.variables).length > 0) {
1171
+ decryptedVars = decryptVariableSlots(
1172
+ environment.variables,
1173
+ `environment ${environment.id}`
1174
+ );
1175
+ }
1176
+ return { ...environment, variables: decryptedVars };
1177
+ }
1178
+ async load() {
1179
+ if (this.loaded) return;
1180
+ try {
1181
+ const raw = await readFile2(this.getFilePath(), "utf-8");
1182
+ const data = JSON.parse(raw);
1183
+ if (!getEncryptionKey()) {
1184
+ this.loaded = true;
1185
+ return;
1186
+ }
1187
+ for (const e of data) {
1188
+ this.diskEncryptedSlots.set(e.id, this.extractEncryptedCache(e));
1189
+ const decrypted = this.decryptEnvironment(e);
1190
+ this.environments.set(e.id, decrypted);
1191
+ }
1192
+ } catch {
1193
+ }
1194
+ this.loaded = true;
1195
+ }
1196
+ async save() {
1197
+ await this.ensureDir();
1198
+ const filePath = this.getFilePath();
1199
+ const tmpPath = filePath + ".tmp";
1200
+ const preparedEnvironments = Array.from(this.environments.values()).map((e) => this.encryptEnvironment(e));
1201
+ for (const e of preparedEnvironments) {
1202
+ this.diskEncryptedSlots.set(e.id, this.extractEncryptedCache(e));
1203
+ }
1204
+ const data = JSON.stringify(preparedEnvironments, null, 2);
1205
+ await writeFile2(tmpPath, data, "utf-8");
1206
+ await rename2(tmpPath, filePath);
1207
+ await chmod2(filePath, 384);
1208
+ }
1209
+ async list(projectId) {
1210
+ await this.load();
1211
+ return Array.from(this.environments.values()).filter((e) => e.projectId === projectId);
1212
+ }
1213
+ async get(id) {
1214
+ await this.load();
1215
+ return this.environments.get(id) ?? null;
1216
+ }
1217
+ async create(input) {
1218
+ await this.load();
1219
+ const parsed = CreateEnvironmentSchema.parse(input);
1220
+ const environment = {
1221
+ id: randomUUID2(),
1222
+ projectId: parsed.projectId,
1223
+ name: parsed.name,
1224
+ description: parsed.description,
1225
+ parentIds: parsed.parentIds,
1226
+ variables: parsed.variables,
1227
+ scope: parsed.scope,
1228
+ reference: parsed.reference,
1229
+ activateOn: parsed.activateOn,
1230
+ immutable: parsed.immutable,
1231
+ default: parsed.default,
1232
+ color: parsed.color,
1233
+ bgColor: parsed.bgColor,
1234
+ agentId: parsed.agentId
1235
+ };
1236
+ if (environment.default) {
1237
+ for (const [id, e] of this.environments) {
1238
+ if (e.projectId === environment.projectId && e.default) {
1239
+ this.environments.set(id, { ...e, default: false });
1240
+ }
1241
+ }
1242
+ }
1243
+ this.environments.set(environment.id, environment);
1244
+ await this.save();
1245
+ return environment;
1246
+ }
1247
+ async update(input) {
1248
+ await this.load();
1249
+ const existing = this.environments.get(input.id);
1250
+ if (!existing) return null;
1251
+ const updated = {
1252
+ ...existing,
1253
+ // Immutable environments: allow changing variables, color, description — but not name
1254
+ ...!existing.immutable && input.name !== void 0 && { name: input.name },
1255
+ ...input.description !== void 0 && { description: input.description },
1256
+ ...input.parentIds !== void 0 && { parentIds: input.parentIds },
1257
+ ...input.variables !== void 0 && { variables: input.variables },
1258
+ ...input.scope !== void 0 && { scope: input.scope },
1259
+ ...input.reference !== void 0 && { reference: input.reference },
1260
+ ...input.activateOn !== void 0 && { activateOn: input.activateOn },
1261
+ ...input.color !== void 0 && { color: input.color },
1262
+ ...input.bgColor !== void 0 && { bgColor: input.bgColor },
1263
+ ...input.default !== void 0 && { default: input.default },
1264
+ ...input.agentId !== void 0 && { agentId: input.agentId ?? void 0 }
1265
+ };
1266
+ if (updated.default) {
1267
+ for (const [id, e] of this.environments) {
1268
+ if (id !== input.id && e.projectId === updated.projectId && e.default) {
1269
+ this.environments.set(id, { ...e, default: false });
1270
+ }
1271
+ }
1272
+ }
1273
+ this.environments.set(input.id, updated);
1274
+ await this.save();
1275
+ return updated;
1276
+ }
1277
+ async upsertVariable(environmentId, variableId, variable) {
1278
+ const existing = this.environments.get(environmentId);
1279
+ if (!existing) return null;
1280
+ const variables = { ...existing.variables ?? {}, [variableId]: variable };
1281
+ return this.update({ id: environmentId, variables });
1282
+ }
1283
+ async removeVariable(environmentId, variableId) {
1284
+ const existing = this.environments.get(environmentId);
1285
+ if (!existing) return null;
1286
+ const variables = { ...existing.variables ?? {} };
1287
+ delete variables[variableId];
1288
+ return this.update({ id: environmentId, variables });
1289
+ }
1290
+ async delete(id) {
1291
+ await this.load();
1292
+ const env2 = this.environments.get(id);
1293
+ if (!env2) return false;
1294
+ if (env2.immutable) return false;
1295
+ this.environments.delete(id);
1296
+ await this.save();
1297
+ return true;
1298
+ }
1299
+ async getByProjectId(projectId) {
1300
+ await this.load();
1301
+ return Array.from(this.environments.values()).filter((e) => e.projectId === projectId);
1302
+ }
1303
+ async listAllGlobal() {
1304
+ await this.load();
1305
+ return Array.from(this.environments.values()).filter((e) => e.scope === "global");
1306
+ }
1307
+ // --- Scope-based Query Methods ---
1308
+ async listByScope(projectId, scope) {
1309
+ await this.load();
1310
+ return Array.from(this.environments.values()).filter((e) => e.projectId === projectId && e.scope === scope);
1311
+ }
1312
+ async findServiceEnvironment(serviceId, globalEnvId) {
1313
+ await this.load();
1314
+ for (const e of this.environments.values()) {
1315
+ if (e.scope === "service" && e.reference === serviceId && e.activateOn === globalEnvId) return e;
1316
+ }
1317
+ return null;
1318
+ }
1319
+ async findServiceEnvironments(serviceId) {
1320
+ await this.load();
1321
+ return Array.from(this.environments.values()).filter((e) => e.scope === "service" && e.reference === serviceId);
1322
+ }
1323
+ async deleteByReference(serviceId) {
1324
+ await this.load();
1325
+ let changed = false;
1326
+ for (const [id, e] of this.environments) {
1327
+ if (e.scope === "service" && e.reference === serviceId) {
1328
+ this.environments.delete(id);
1329
+ changed = true;
1330
+ }
1331
+ }
1332
+ if (changed) await this.save();
1333
+ }
1334
+ async deleteByActivateOn(globalEnvId) {
1335
+ await this.load();
1336
+ let changed = false;
1337
+ for (const [id, e] of this.environments) {
1338
+ if (e.scope === "service" && e.activateOn === globalEnvId) {
1339
+ this.environments.delete(id);
1340
+ changed = true;
1341
+ }
1342
+ }
1343
+ if (changed) await this.save();
1344
+ }
1345
+ /**
1346
+ * When a service is renamed, update the name of all its service-scoped environments
1347
+ * to keep the `${serviceName} (${globalEnvName})` pattern in sync.
1348
+ */
1349
+ async renameByReference(serviceId, newServiceName) {
1350
+ await this.load();
1351
+ let changed = false;
1352
+ for (const [id, e] of this.environments) {
1353
+ if (e.scope === "service" && e.reference === serviceId && e.activateOn) {
1354
+ const globalEnv = this.environments.get(e.activateOn);
1355
+ if (!globalEnv) continue;
1356
+ this.environments.set(id, { ...e, name: `${newServiceName} (${globalEnv.name})` });
1357
+ changed = true;
1358
+ }
1359
+ }
1360
+ if (changed) await this.save();
1361
+ }
1362
+ /**
1363
+ * When a global environment is renamed, update the name of all service-scoped
1364
+ * environments that activateOn it to keep the naming pattern in sync.
1365
+ * Caller provides a serviceId→name map since environmentStore can't access serviceStore.
1366
+ */
1367
+ async renameByActivateOn(globalEnvId, newGlobalName, serviceNames) {
1368
+ await this.load();
1369
+ let changed = false;
1370
+ for (const [id, e] of this.environments) {
1371
+ if (e.scope === "service" && e.activateOn === globalEnvId && e.reference) {
1372
+ const serviceName = serviceNames.get(e.reference);
1373
+ if (!serviceName) continue;
1374
+ this.environments.set(id, { ...e, name: `${serviceName} (${newGlobalName})` });
1375
+ changed = true;
1376
+ }
1377
+ }
1378
+ if (changed) await this.save();
1379
+ }
1380
+ };
1381
+ var environmentStore = new EnvironmentStore();
1382
+ var FILENAME3 = "services.json";
1383
+ function encryptLogSources(logSources) {
1384
+ return logSources.map((src) => {
1385
+ if (src.type === "mongo") {
1386
+ return { ...src, connectionString: stableEncryptValue(src.connectionString) };
1387
+ }
1388
+ if (src.type === "mysql" && src.password) {
1389
+ return { ...src, password: stableEncryptValue(src.password) };
1390
+ }
1391
+ return src;
1392
+ });
1393
+ }
1394
+ function decryptLogSources(logSources) {
1395
+ return logSources.map((src) => {
1396
+ if (src.type === "mongo" && isEncryptedValue(src.connectionString)) {
1397
+ try {
1398
+ return { ...src, connectionString: decryptValue(src.connectionString) };
1399
+ } catch {
1400
+ console.error("[service-store] Failed to decrypt mongo connectionString");
1401
+ return src;
1402
+ }
1403
+ }
1404
+ if (src.type === "mysql" && src.password && isEncryptedValue(src.password)) {
1405
+ try {
1406
+ return { ...src, password: decryptValue(src.password) };
1407
+ } catch {
1408
+ console.error("[service-store] Failed to decrypt mysql password");
1409
+ return src;
1410
+ }
1411
+ }
1412
+ return src;
1413
+ });
1414
+ }
1415
+ function prepareForDisk(service) {
1416
+ return {
1417
+ ...service,
1418
+ env: {},
1419
+ envSecrets: [],
1420
+ resolvedLogParsers: void 0,
1421
+ resolvedCwd: void 0,
1422
+ logSources: encryptLogSources(service.logSources ?? [])
1423
+ };
1424
+ }
1425
+ var ServiceStore = class {
1426
+ services = /* @__PURE__ */ new Map();
1427
+ loaded = false;
1428
+ listeners = /* @__PURE__ */ new Set();
1429
+ onChange(fn) {
1430
+ this.listeners.add(fn);
1431
+ return () => this.listeners.delete(fn);
1432
+ }
1433
+ notify() {
1434
+ for (const fn of this.listeners) fn();
1435
+ }
1436
+ getFilePath() {
1437
+ return join3(env.DATA_DIR, FILENAME3);
1438
+ }
1439
+ async ensureDir() {
1440
+ await mkdir3(env.DATA_DIR, { recursive: true });
1441
+ }
1442
+ async load() {
1443
+ if (this.loaded) return;
1444
+ try {
1445
+ const raw = await readFile3(this.getFilePath(), "utf-8");
1446
+ const data = JSON.parse(raw);
1447
+ if (!getEncryptionKey()) {
1448
+ this.loaded = true;
1449
+ return;
1450
+ }
1451
+ for (const s of data) {
1452
+ if (s.commands) {
1453
+ for (const cmd of s.commands) {
1454
+ if (cmd.shell === void 0) cmd.shell = true;
1455
+ }
1456
+ }
1457
+ this.services.set(s.id, {
1458
+ ...s,
1459
+ logSources: decryptLogSources(s.logSources ?? [])
1460
+ });
1461
+ }
1462
+ } catch {
1463
+ }
1464
+ this.loaded = true;
1465
+ }
1466
+ async save() {
1467
+ await this.ensureDir();
1468
+ const filePath = this.getFilePath();
1469
+ const tmpPath = filePath + ".tmp";
1470
+ const preparedServices = Array.from(this.services.values()).map((s) => prepareForDisk(s));
1471
+ const data = JSON.stringify(preparedServices, null, 2);
1472
+ await writeFile3(tmpPath, data, "utf-8");
1473
+ await rename3(tmpPath, filePath);
1474
+ await chmod3(filePath, 384);
1475
+ }
1476
+ async list() {
1477
+ await this.load();
1478
+ return Array.from(this.services.values());
1479
+ }
1480
+ async get(id) {
1481
+ await this.load();
1482
+ return this.services.get(id) ?? null;
1483
+ }
1484
+ async create(input) {
1485
+ await this.load();
1486
+ const service = {
1487
+ id: randomUUID3(),
1488
+ orgId: input.orgId ?? null,
1489
+ agentId: input.agentId,
1490
+ name: input.name,
1491
+ commands: input.commands ?? [],
1492
+ cwd: input.cwd,
1493
+ ports: input.ports ?? [],
1494
+ groups: input.groups ?? [],
1495
+ description: input.description,
1496
+ url: input.url,
1497
+ localUrls: input.localUrls ?? [],
1498
+ openapiUrl: input.openapiUrl,
1499
+ git: input.git,
1500
+ meta: input.meta,
1501
+ env: {},
1502
+ envSecrets: [],
1503
+ runner: input.runner ?? "native",
1504
+ dockerConfig: input.dockerConfig,
1505
+ healthCheck: input.healthCheck,
1506
+ parserIds: input.parserIds ?? [],
1507
+ shortcuts: input.shortcuts ?? [],
1508
+ logSources: input.logSources ?? [],
1509
+ autoRestart: input.autoRestart ?? false,
1510
+ restartBackoffMs: input.restartBackoffMs ?? 1e3,
1511
+ clearLogsOnStart: input.clearLogsOnStart ?? false
1512
+ };
1513
+ this.services.set(service.id, service);
1514
+ await this.save();
1515
+ this.notify();
1516
+ return service;
1517
+ }
1518
+ async update(input) {
1519
+ await this.load();
1520
+ const existing = this.services.get(input.id);
1521
+ if (!existing) return null;
1522
+ const updated = {
1523
+ ...existing,
1524
+ ...input.agentId !== void 0 && { agentId: input.agentId ?? void 0 },
1525
+ ...input.name !== void 0 && { name: input.name },
1526
+ ...input.commands !== void 0 && { commands: input.commands },
1527
+ ...input.cwd !== void 0 && { cwd: input.cwd ?? void 0 },
1528
+ ...input.ports !== void 0 && { ports: input.ports },
1529
+ ...input.groups !== void 0 && { groups: input.groups },
1530
+ ...input.description !== void 0 && { description: input.description },
1531
+ ...input.url !== void 0 && { url: input.url },
1532
+ ...input.localUrls !== void 0 && { localUrls: input.localUrls },
1533
+ ...input.openapiUrl !== void 0 && { openapiUrl: input.openapiUrl },
1534
+ ...input.git !== void 0 && { git: input.git ?? void 0 },
1535
+ ...input.meta !== void 0 && { meta: input.meta ?? void 0 },
1536
+ ...input.runner !== void 0 && { runner: input.runner },
1537
+ ...input.dockerConfig !== void 0 && { dockerConfig: input.dockerConfig },
1538
+ ...input.healthCheck !== void 0 && { healthCheck: input.healthCheck ?? void 0 },
1539
+ ...input.autoRestart !== void 0 && { autoRestart: input.autoRestart },
1540
+ ...input.clearLogsOnStart !== void 0 && { clearLogsOnStart: input.clearLogsOnStart },
1541
+ ...input.restartBackoffMs !== void 0 && { restartBackoffMs: input.restartBackoffMs },
1542
+ ...input.orgId !== void 0 && { orgId: input.orgId },
1543
+ ...input.shortcuts !== void 0 && { shortcuts: input.shortcuts },
1544
+ ...input.parserIds !== void 0 && { parserIds: input.parserIds },
1545
+ ...input.logSources !== void 0 && { logSources: input.logSources }
1546
+ };
1547
+ this.services.set(input.id, updated);
1548
+ await this.save();
1549
+ this.notify();
1550
+ return updated;
1551
+ }
1552
+ async delete(id) {
1553
+ await this.load();
1554
+ if (!this.services.has(id)) return false;
1555
+ this.services.delete(id);
1556
+ await this.save();
1557
+ this.notify();
1558
+ await environmentStore.deleteByReference(id);
1559
+ return true;
1560
+ }
1561
+ };
1562
+ var serviceStore = new ServiceStore();
1563
+ function buildAgentEnv() {
1564
+ const safeKeys = ["PATH", "HOME", "USER", "SHELL", "LANG", "TERM", "NODE_ENV", "TMPDIR", "TZ", "DBUS_SESSION_BUS_ADDRESS", "XDG_RUNTIME_DIR"];
1565
+ const agentEnv = {
1566
+ RUNEYA_AGENT_PORT: String(env.RUNEYA_AGENT_PORT),
1567
+ RUNEYA_AGENT_HOST: env.RUNEYA_AGENT_HOST
1568
+ };
1569
+ if (env.MODE === "local-simple") {
1570
+ const passphrase = deriveStableSecret("local-agent-passphrase");
1571
+ if (!passphrase) {
1572
+ return null;
1573
+ }
1574
+ agentEnv.PASSPHRASE_OVERRIDE = passphrase;
1575
+ }
1576
+ for (const key of safeKeys) {
1577
+ if (process.env[key]) {
1578
+ agentEnv[key] = process.env[key];
1579
+ }
1580
+ }
1581
+ return agentEnv;
1582
+ }
1583
+ var LOCAL_AGENT_ID = "local-agent";
1584
+ var LOCAL_AGENT_NAME = "local";
1585
+ var AgentSpawner = class {
1586
+ agentProcess = null;
1587
+ exitHandlerRegistered = false;
1588
+ fileWatcher = null;
1589
+ isRestarting = false;
1590
+ agentDistPath = null;
1591
+ async spawnLocalAgent() {
1592
+ const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;
1593
+ const agentEnv = buildAgentEnv();
1594
+ if (!agentEnv) {
1595
+ console.log("[agent-spawner] \u26A0\uFE0F Cannot spawn local agent: encryption key not available");
1596
+ console.log("[agent-spawner] Please provide the encryption key via the setup UI");
1597
+ return;
1598
+ }
1599
+ console.log(`[agent-spawner] Spawning local agent on ${agentUrl}...`);
1600
+ const isDev = process.env.NODE_ENV === "development";
1601
+ this.agentDistPath = env.AGENT_BINARY_PATH || join4(process.cwd(), "..", "agent", "dist", "index.js");
1602
+ this.agentProcess = spawn("node", [this.agentDistPath], {
1603
+ env: agentEnv,
1604
+ stdio: ["ignore", "pipe", "pipe"]
1605
+ });
1606
+ if (isDev) {
1607
+ this.setupFileWatcher();
1608
+ }
1609
+ if (!this.exitHandlerRegistered) {
1610
+ this.exitHandlerRegistered = true;
1611
+ process.on("exit", () => {
1612
+ if (this.agentProcess && !this.agentProcess.killed) {
1613
+ this.agentProcess.kill("SIGKILL");
1614
+ }
1615
+ });
1616
+ }
1617
+ const passphrase = await this.waitForPassphrase();
1618
+ if (!passphrase) {
1619
+ console.error("[agent-spawner] Failed to get passphrase from local agent");
1620
+ return;
1621
+ }
1622
+ console.log("[agent-spawner] Local agent started, passphrase received");
1623
+ await agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase, passthroughEnv: [] });
1624
+ console.log(`[agent-spawner] Local agent registered as ${LOCAL_AGENT_ID}`);
1625
+ }
1626
+ waitForPassphrase() {
1627
+ return new Promise((resolve) => {
1628
+ if (!this.agentProcess?.stdout) {
1629
+ resolve(null);
1630
+ return;
1631
+ }
1632
+ const rl = createInterface({ input: this.agentProcess.stdout });
1633
+ const timeout = setTimeout(() => {
1634
+ resolve(null);
1635
+ }, PASSPHRASE_TIMEOUT);
1636
+ let initialPassphraseFound = false;
1637
+ rl.on("line", (line) => {
1638
+ const match = line.match(/^RUNEYA_PASSPHRASE=([a-f0-9]{64})$/);
1639
+ if (match) {
1640
+ const newPassphrase = match[1];
1641
+ if (!initialPassphraseFound) {
1642
+ initialPassphraseFound = true;
1643
+ clearTimeout(timeout);
1644
+ resolve(newPassphrase);
1645
+ } else {
1646
+ console.log("[agent-spawner] \u{1F504} Agent reloaded, updating passphrase...");
1647
+ this.updateAgentPassphrase(newPassphrase).catch((err) => {
1648
+ console.error("[agent-spawner] Failed to update passphrase after reload:", err);
1649
+ });
1650
+ }
1651
+ return;
1652
+ }
1653
+ console.log(`[agent] ${line}`);
1654
+ });
1655
+ if (this.agentProcess?.stderr) {
1656
+ const stderrRl = createInterface({ input: this.agentProcess.stderr });
1657
+ stderrRl.on("line", (line) => {
1658
+ console.error(`[agent:err] ${line}`);
1659
+ });
1660
+ }
1661
+ this.agentProcess?.on("error", (err) => {
1662
+ console.error("[agent-spawner] Failed to spawn agent:", err);
1663
+ clearTimeout(timeout);
1664
+ if (!initialPassphraseFound) resolve(null);
1665
+ });
1666
+ this.agentProcess?.on("exit", (code) => {
1667
+ if (code !== null && code !== 0) {
1668
+ console.error(`[agent-spawner] Agent exited with code ${code}`);
1669
+ }
1670
+ clearTimeout(timeout);
1671
+ if (!initialPassphraseFound) resolve(null);
1672
+ });
1673
+ });
1674
+ }
1675
+ async shutdown() {
1676
+ if (this.fileWatcher) {
1677
+ this.fileWatcher.close();
1678
+ this.fileWatcher = null;
1679
+ }
1680
+ if (this.agentProcess) {
1681
+ console.log("[agent-spawner] Stopping local agent...");
1682
+ this.agentProcess.kill("SIGKILL");
1683
+ await new Promise((resolve) => {
1684
+ this.agentProcess?.on("exit", () => resolve());
1685
+ setTimeout(resolve, 2e3);
1686
+ });
1687
+ this.agentProcess = null;
1688
+ }
1689
+ }
1690
+ getLocalAgentId() {
1691
+ return LOCAL_AGENT_ID;
1692
+ }
1693
+ async updateAgentPassphrase(newPassphrase) {
1694
+ const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;
1695
+ await agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase: newPassphrase, passthroughEnv: [] });
1696
+ console.log(`[agent-spawner] \u2705 Agent passphrase updated, re-registered as ${LOCAL_AGENT_ID}`);
1697
+ }
1698
+ setupFileWatcher() {
1699
+ if (!this.agentDistPath) return;
1700
+ if (this.fileWatcher) {
1701
+ this.fileWatcher.close();
1702
+ }
1703
+ console.log("[agent-spawner] \u{1F50D} Watching agent dist for changes (hot reload enabled)");
1704
+ const distDir = join4(this.agentDistPath, "..");
1705
+ this.fileWatcher = watch(distDir, { persistent: false }, (eventType, filename) => {
1706
+ if ((eventType === "change" || eventType === "rename") && filename === "index.js" && !this.isRestarting) {
1707
+ this.isRestarting = true;
1708
+ console.log("[agent-spawner] \u{1F504} Agent code changed, restarting...");
1709
+ if (this.agentProcess && !this.agentProcess.killed) {
1710
+ const oldProcess = this.agentProcess;
1711
+ oldProcess.once("exit", () => {
1712
+ setTimeout(() => {
1713
+ this.restartAgent().catch((err) => {
1714
+ console.error("[agent-spawner] Failed to restart agent:", err);
1715
+ this.isRestarting = false;
1716
+ });
1717
+ }, 100);
1718
+ });
1719
+ oldProcess.kill("SIGKILL");
1720
+ }
1721
+ }
1722
+ });
1723
+ }
1724
+ async restartAgent() {
1725
+ if (!this.agentDistPath) return;
1726
+ const agentEnv = buildAgentEnv();
1727
+ if (!agentEnv) {
1728
+ console.log("[agent-spawner] Cannot restart agent: encryption key not available");
1729
+ this.isRestarting = false;
1730
+ return;
1731
+ }
1732
+ const agentUrl = `http://${env.RUNEYA_AGENT_HOST}:${env.RUNEYA_AGENT_PORT}`;
1733
+ const isDev = process.env.NODE_ENV === "development";
1734
+ this.agentProcess = spawn("node", [this.agentDistPath], {
1735
+ env: agentEnv,
1736
+ stdio: ["ignore", "pipe", "pipe"]
1737
+ });
1738
+ if (this.agentProcess?.stdout) {
1739
+ const rl = createInterface({ input: this.agentProcess.stdout });
1740
+ rl.on("line", (line) => {
1741
+ const match = line.match(/^RUNEYA_PASSPHRASE=([a-f0-9]{64})$/);
1742
+ if (match) {
1743
+ const newPassphrase = match[1];
1744
+ agentManager.registerLocal({ id: LOCAL_AGENT_ID, name: LOCAL_AGENT_NAME, url: agentUrl, passphrase: newPassphrase, passthroughEnv: [] }).then(() => {
1745
+ console.log(`[agent-spawner] \u2705 Agent restarted and re-registered as ${LOCAL_AGENT_ID}`);
1746
+ this.isRestarting = false;
1747
+ if (isDev) {
1748
+ this.setupFileWatcher();
1749
+ }
1750
+ }).catch((err) => {
1751
+ console.error("[agent-spawner] Failed to register agent after restart:", err);
1752
+ this.isRestarting = false;
1753
+ });
1754
+ return;
1755
+ }
1756
+ console.log(`[agent] ${line}`);
1757
+ });
1758
+ }
1759
+ if (this.agentProcess?.stderr) {
1760
+ const stderrRl = createInterface({ input: this.agentProcess.stderr });
1761
+ stderrRl.on("line", (line) => {
1762
+ console.error(`[agent:err] ${line}`);
1763
+ });
1764
+ }
1765
+ }
1766
+ };
1767
+ var agentSpawner = new AgentSpawner();
1768
+ var TraefikManager = class {
1769
+ encryptDnsCredentials(credentials) {
1770
+ const result = {};
1771
+ for (const [key, value] of Object.entries(credentials)) {
1772
+ result[key] = isEncryptedValue(value) ? value : stableEncryptValue(value);
1773
+ }
1774
+ return result;
1775
+ }
1776
+ decryptDnsCredentials(credentials) {
1777
+ const result = {};
1778
+ for (const [key, value] of Object.entries(credentials)) {
1779
+ if (isEncryptedValue(value)) {
1780
+ try {
1781
+ result[key] = decryptValue(value);
1782
+ } catch {
1783
+ console.error(`[traefik-manager] Failed to decrypt credential: ${key}`);
1784
+ result[key] = "";
1785
+ }
1786
+ } else {
1787
+ result[key] = value;
1788
+ }
1789
+ }
1790
+ return result;
1791
+ }
1792
+ };
1793
+ var traefikManager = new TraefikManager();
1794
+ var SUBSCRIPTION_PATHS = [
1795
+ { path: "process.onStatus", eventType: "process:status" },
1796
+ { path: "process.onLog", eventType: "process:log" },
1797
+ { path: "process.onHealth", eventType: "process:health" },
1798
+ { path: "process.onMetrics", eventType: "process:metrics" }
1799
+ ];
1800
+ var PING_INTERVAL_MS = 3e4;
1801
+ var PONG_TIMEOUT_MS = 1e4;
1802
+ var AgentWSBridge = class extends EventEmitter {
1803
+ constructor(agentId, url, passphrase) {
1804
+ super();
1805
+ this.agentId = agentId;
1806
+ this.url = url;
1807
+ this.passphrase = passphrase;
1808
+ }
1809
+ ws = null;
1810
+ nextId = 1;
1811
+ subscriptions = /* @__PURE__ */ new Map();
1812
+ reconnectTimer = null;
1813
+ currentReconnectDelay = WS_RECONNECT_MIN;
1814
+ closed = false;
1815
+ pingInterval = null;
1816
+ pongTimeout = null;
1817
+ connect() {
1818
+ if (this.closed) return;
1819
+ this.cleanup();
1820
+ const httpUrl = new URL(this.url);
1821
+ const wsProtocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
1822
+ const wsUrl = `${wsProtocol}//${httpUrl.host}`;
1823
+ try {
1824
+ this.ws = new WebSocket(wsUrl, {
1825
+ headers: { "Authorization": `Bearer ${this.passphrase}` }
1826
+ });
1827
+ } catch {
1828
+ this.scheduleReconnect();
1829
+ return;
1830
+ }
1831
+ this.ws.on("open", () => {
1832
+ console.log(`[agent-ws-bridge] \u2705 Connected to agent ${this.agentId}`);
1833
+ this.currentReconnectDelay = WS_RECONNECT_MIN;
1834
+ this.startHeartbeat();
1835
+ this.subscribeAll();
1836
+ console.log(`[agent-ws-bridge] Created ${SUBSCRIPTION_PATHS.length} subscriptions for agent ${this.agentId}`);
1837
+ });
1838
+ this.ws.on("message", (raw) => {
1839
+ try {
1840
+ const msg = JSON.parse(
1841
+ typeof raw === "string" ? raw : raw.toString()
1842
+ );
1843
+ this.handleMessage(msg);
1844
+ } catch {
1845
+ }
1846
+ });
1847
+ this.ws.on("pong", () => {
1848
+ if (this.pongTimeout) {
1849
+ clearTimeout(this.pongTimeout);
1850
+ this.pongTimeout = null;
1851
+ }
1852
+ });
1853
+ this.ws.on("close", () => {
1854
+ console.log(`[agent-ws-bridge] \u274C Disconnected from agent ${this.agentId}`);
1855
+ this.stopHeartbeat();
1856
+ this.ws = null;
1857
+ this.subscriptions.clear();
1858
+ if (!this.closed) {
1859
+ console.log(`[agent-ws-bridge] Scheduling reconnect for agent ${this.agentId} in ${this.currentReconnectDelay}ms`);
1860
+ this.scheduleReconnect();
1861
+ }
1862
+ });
1863
+ this.ws.on("error", (err) => {
1864
+ console.debug("[agent-ws-bridge] WS error for agent", this.agentId + ":", err.message);
1865
+ });
1866
+ }
1867
+ /**
1868
+ * Subscribe to a dynamic tRPC path with an input payload.
1869
+ * Returns an unsubscribe function that cleans up on the agent side.
1870
+ * Used for per-session subscriptions (e.g. terminal.onData).
1871
+ */
1872
+ subscribeWithInput(path, input, onData, onStop) {
1873
+ const id = this.nextId++;
1874
+ this.subscriptions.set(id, { onData, onStop });
1875
+ if (this.ws?.readyState === WebSocket.OPEN) {
1876
+ const msg = { id, method: "subscription", params: { path, input } };
1877
+ this.ws.send(JSON.stringify(msg));
1878
+ }
1879
+ return () => {
1880
+ this.subscriptions.delete(id);
1881
+ if (this.ws?.readyState === WebSocket.OPEN) {
1882
+ this.ws.send(JSON.stringify({ id, method: "subscription.stop" }));
1883
+ }
1884
+ };
1885
+ }
1886
+ disconnect() {
1887
+ this.closed = true;
1888
+ this.cleanup();
1889
+ }
1890
+ startHeartbeat() {
1891
+ this.stopHeartbeat();
1892
+ this.pingInterval = setInterval(() => {
1893
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
1894
+ this.pongTimeout = setTimeout(() => {
1895
+ console.log(`[agent-ws-bridge] Pong timeout for agent ${this.agentId}, closing connection`);
1896
+ this.ws?.terminate();
1897
+ }, PONG_TIMEOUT_MS);
1898
+ this.ws.ping();
1899
+ }, PING_INTERVAL_MS);
1900
+ }
1901
+ stopHeartbeat() {
1902
+ if (this.pingInterval) {
1903
+ clearInterval(this.pingInterval);
1904
+ this.pingInterval = null;
1905
+ }
1906
+ if (this.pongTimeout) {
1907
+ clearTimeout(this.pongTimeout);
1908
+ this.pongTimeout = null;
1909
+ }
1910
+ }
1911
+ cleanup() {
1912
+ this.stopHeartbeat();
1913
+ if (this.reconnectTimer) {
1914
+ clearTimeout(this.reconnectTimer);
1915
+ this.reconnectTimer = null;
1916
+ }
1917
+ if (this.ws) {
1918
+ this.ws.removeAllListeners();
1919
+ this.ws.on("error", () => {
1920
+ });
1921
+ this.ws.close();
1922
+ this.ws = null;
1923
+ }
1924
+ this.subscriptions.clear();
1925
+ }
1926
+ subscribeAll() {
1927
+ for (const { path, eventType } of SUBSCRIPTION_PATHS) {
1928
+ this.sendSubscription(path, eventType);
1929
+ }
1930
+ }
1931
+ sendSubscription(path, eventType) {
1932
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
1933
+ const id = this.nextId++;
1934
+ this.subscriptions.set(id, { eventType });
1935
+ const msg = {
1936
+ id,
1937
+ method: "subscription",
1938
+ params: { path }
1939
+ };
1940
+ this.ws.send(JSON.stringify(msg));
1941
+ }
1942
+ handleMessage(msg) {
1943
+ const sub = this.subscriptions.get(msg.id);
1944
+ if (!sub) return;
1945
+ if (msg.result?.type === "data" && msg.result.data !== void 0) {
1946
+ const data = msg.result.data;
1947
+ if (sub.onData) {
1948
+ sub.onData(data);
1949
+ } else if (sub.eventType) {
1950
+ if (typeof data !== "object" || data === null) return;
1951
+ if (Array.isArray(data)) {
1952
+ this.emit(sub.eventType, { batch: data, agentId: this.agentId });
1953
+ } else {
1954
+ this.emit(sub.eventType, {
1955
+ ...data,
1956
+ agentId: this.agentId
1957
+ });
1958
+ }
1959
+ }
1960
+ } else if (msg.result?.type === "started") {
1961
+ const label = sub.eventType ?? "(dynamic)";
1962
+ console.log(`[agent-ws-bridge] \u2705 Subscription ${label} started for agent ${this.agentId}`);
1963
+ } else if (msg.result?.type === "stopped") {
1964
+ const label = sub.eventType ?? "(dynamic)";
1965
+ console.log(`[agent-ws-bridge] \u23F9\uFE0F Subscription ${label} stopped for agent ${this.agentId}`);
1966
+ if (sub.onStop) sub.onStop();
1967
+ this.subscriptions.delete(msg.id);
1968
+ } else if (msg.error) {
1969
+ const label = sub.eventType ?? "(dynamic)";
1970
+ console.debug("[agent-ws-bridge] Subscription error for", label, "on agent", this.agentId + ":", msg.error);
1971
+ if (sub.onStop) sub.onStop();
1972
+ this.subscriptions.delete(msg.id);
1973
+ }
1974
+ }
1975
+ scheduleReconnect() {
1976
+ if (this.closed || this.reconnectTimer) return;
1977
+ this.reconnectTimer = setTimeout(() => {
1978
+ this.reconnectTimer = null;
1979
+ if (!this.closed) {
1980
+ this.connect();
1981
+ }
1982
+ }, this.currentReconnectDelay);
1983
+ this.currentReconnectDelay = Math.min(this.currentReconnectDelay * 2, WS_RECONNECT_MAX);
1984
+ }
1985
+ };
1986
+ var HealthMessageSchema = z20.object({
1987
+ status: z20.string(),
1988
+ uptime: z20.number(),
1989
+ runnersAvailable: z20.array(z20.enum(["native", "docker"])),
1990
+ processCount: z20.number(),
1991
+ runningCount: z20.number(),
1992
+ lanIp: z20.string().optional()
1993
+ });
1994
+ var MIN_RECONNECT_DELAY = WS_RECONNECT_MIN;
1995
+ var MAX_RECONNECT_DELAY = WS_RECONNECT_MAX;
1996
+ var ALIVE_TIMEOUT = AGENT_HEALTH_POLL_INTERVAL;
1997
+ function resolvePassthroughValues(names) {
1998
+ const values = {};
1999
+ for (const name of names) {
2000
+ if (process.env[name] !== void 0) {
2001
+ values[name] = process.env[name];
2002
+ }
2003
+ }
2004
+ return values;
2005
+ }
2006
+ var AgentManager = class extends EventEmitter2 {
2007
+ connections = /* @__PURE__ */ new Map();
2008
+ constructor() {
2009
+ super();
2010
+ this.setMaxListeners(50);
2011
+ }
2012
+ // Make an authenticated fetch call to an agent's tRPC endpoint
2013
+ async agentCall(agent, path, input, method = "GET", timeout = AGENT_FETCH_TIMEOUT) {
2014
+ const url = new URL(`/api/trpc/${path}`, agent.url);
2015
+ const headers = {
2016
+ "Authorization": `Bearer ${agent.passphrase}`,
2017
+ "Content-Type": "application/json"
2018
+ };
2019
+ let response;
2020
+ try {
2021
+ if (method === "GET") {
2022
+ if (input !== void 0) {
2023
+ url.searchParams.set("input", JSON.stringify(input));
2024
+ }
2025
+ response = await fetch(url.toString(), { headers, signal: AbortSignal.timeout(timeout) });
2026
+ } else {
2027
+ response = await fetch(url.toString(), {
2028
+ method: "POST",
2029
+ headers,
2030
+ body: JSON.stringify(input !== void 0 ? input : {}),
2031
+ signal: AbortSignal.timeout(timeout)
2032
+ });
2033
+ }
2034
+ } catch (err) {
2035
+ console.error("[agent-manager] Agent call failed (" + method + " " + path + "):", err instanceof Error ? err.message : String(err));
2036
+ throw err;
2037
+ }
2038
+ if (!response.ok) {
2039
+ let agentMessage = "";
2040
+ try {
2041
+ const errBody = await response.json();
2042
+ agentMessage = errBody?.error?.message ?? errBody?.message ?? "";
2043
+ } catch {
2044
+ }
2045
+ const detail = agentMessage ? `: ${agentMessage}` : "";
2046
+ console.error("[agent-manager] Agent call failed: HTTP", response.status, "for", method, path, detail);
2047
+ throw new Error(`Agent call failed: HTTP ${response.status}${detail}`);
2048
+ }
2049
+ const json = await response.json();
2050
+ if (json.error) {
2051
+ console.error("[agent-manager] Agent error on", path + ":", json.error);
2052
+ throw new Error(`Agent error on ${path}: ${json.error.message ?? "unknown"}`);
2053
+ }
2054
+ return json.result?.data;
2055
+ }
2056
+ // Query wrapper
2057
+ async agentQuery(agentId, path, input) {
2058
+ const agent = this.getAgent(agentId);
2059
+ if (!agent) throw new Error(`Agent ${agentId} not found`);
2060
+ return this.agentCall(agent.config, path, input, "GET");
2061
+ }
2062
+ // Mutation wrapper
2063
+ async agentMutation(agentId, path, input, timeout) {
2064
+ const agent = this.getAgent(agentId);
2065
+ if (!agent) throw new Error(`Agent ${agentId} not found`);
2066
+ return this.agentCall(agent.config, path, input, "POST", timeout);
2067
+ }
2068
+ /**
2069
+ * Open a dynamic tRPC subscription on the agent's WS bridge.
2070
+ * Returns an unsubscribe function.
2071
+ * Used for per-session streaming (e.g. terminal.onData).
2072
+ */
2073
+ agentSubscribe(agentId, path, input, onData, onStop) {
2074
+ const conn = this.connections.get(agentId);
2075
+ if (!conn?.bridge) throw new Error(`Agent ${agentId} not connected via WebSocket`);
2076
+ return conn.bridge.subscribeWithInput(path, input, onData, onStop);
2077
+ }
2078
+ async init() {
2079
+ const agents = await agentStore.list();
2080
+ for (const agent of agents) {
2081
+ this.connectAgent(agent);
2082
+ }
2083
+ }
2084
+ async register(name, url, passphrase) {
2085
+ const config = await agentStore.register(name, url, passphrase);
2086
+ const isReRegistration = this.connections.has(config.id);
2087
+ if (isReRegistration) {
2088
+ console.log(`[agent-manager] Re-registering agent ${config.name} (${config.id})`);
2089
+ this.disconnectAgent(config.id);
2090
+ await new Promise((resolve) => setTimeout(resolve, 200));
2091
+ }
2092
+ this.connectAgent(config);
2093
+ console.log(`[agent-manager] Agent ${config.name} (${config.id}) connection established`);
2094
+ return config;
2095
+ }
2096
+ /**
2097
+ * Register a local agent in-memory only (no persistence to agentStore).
2098
+ * The local agent is always auto-spawned with deterministic config,
2099
+ * so it doesn't need disk persistence.
2100
+ * Also reassigns orphaned services (from old UUID-based agent IDs) to this agent.
2101
+ */
2102
+ async registerLocal(config) {
2103
+ const isReRegistration = this.connections.has(config.id);
2104
+ if (isReRegistration) {
2105
+ console.log(`[agent-manager] Re-registering local agent ${config.name} (${config.id})`);
2106
+ this.disconnectAgent(config.id);
2107
+ await new Promise((resolve) => setTimeout(resolve, 200));
2108
+ }
2109
+ const storedPassthrough = agentStore.getPassthroughEnv(config.id);
2110
+ const configWithPassthrough = { ...config, passthroughEnv: storedPassthrough };
2111
+ await this.reassignOrphanedServices(config.id);
2112
+ this.connectAgent(configWithPassthrough);
2113
+ console.log(`[agent-manager] Local agent ${config.name} (${config.id}) connected (in-memory only)`);
2114
+ }
2115
+ /**
2116
+ * Find services assigned to unknown agent IDs and reassign them to the given agent.
2117
+ * This handles the migration from random UUID-based agent IDs to the stable 'local-agent' ID.
2118
+ */
2119
+ async reassignOrphanedServices(localAgentId) {
2120
+ const allServices = await serviceStore.list();
2121
+ const persistedAgents = await agentStore.list();
2122
+ const knownIds = /* @__PURE__ */ new Set([localAgentId, ...persistedAgents.map((a) => a.id)]);
2123
+ for (const service of allServices) {
2124
+ if (service.agentId && !knownIds.has(service.agentId)) {
2125
+ console.log(`[agent-manager] Reassigning orphaned service ${service.name} (${service.id}) from ${service.agentId} \u2192 ${localAgentId}`);
2126
+ await serviceStore.update({ id: service.id, agentId: localAgentId });
2127
+ }
2128
+ }
2129
+ }
2130
+ async update(agentId, fields) {
2131
+ const isLocal = agentSpawner.getLocalAgentId() === agentId;
2132
+ if (isLocal) {
2133
+ const hasOtherFields = fields.name !== void 0 || fields.url !== void 0 || fields.passphrase !== void 0;
2134
+ if (hasOtherFields) {
2135
+ throw new Error("Cannot modify the local agent");
2136
+ }
2137
+ }
2138
+ if (fields.passthroughEnv !== void 0) {
2139
+ await agentStore.savePassthroughEnv(agentId, fields.passthroughEnv);
2140
+ const conn2 = this.connections.get(agentId);
2141
+ if (conn2) {
2142
+ conn2.config = { ...conn2.config, passthroughEnv: fields.passthroughEnv };
2143
+ try {
2144
+ await this.agentMutation(agentId, "config.setPassthroughEnv", {
2145
+ names: fields.passthroughEnv,
2146
+ values: resolvePassthroughValues(fields.passthroughEnv)
2147
+ });
2148
+ } catch (err) {
2149
+ console.error("[agent-manager] Failed to push passthroughEnv to agent", agentId + ":", err instanceof Error ? err.message : String(err));
2150
+ }
2151
+ }
2152
+ }
2153
+ if (isLocal) {
2154
+ const conn2 = this.connections.get(agentId);
2155
+ if (!conn2) throw new Error(`Agent ${agentId} not found`);
2156
+ return conn2.config;
2157
+ }
2158
+ const { passthroughEnv: _pt, ...storeFields } = fields;
2159
+ const hasStoreFields = storeFields.name !== void 0 || storeFields.url !== void 0 || storeFields.passphrase !== void 0;
2160
+ if (hasStoreFields) {
2161
+ const updated = await agentStore.update(agentId, storeFields);
2162
+ if (!updated) {
2163
+ throw new Error(`Agent ${agentId} not found`);
2164
+ }
2165
+ const conn2 = this.connections.get(agentId);
2166
+ if (conn2) {
2167
+ conn2.config = updated;
2168
+ if (storeFields.url !== void 0 || storeFields.passphrase !== void 0) {
2169
+ this.disconnectAgent(agentId);
2170
+ this.connectAgent(updated);
2171
+ }
2172
+ }
2173
+ return updated;
2174
+ }
2175
+ const conn = this.connections.get(agentId);
2176
+ if (!conn) throw new Error(`Agent ${agentId} not found`);
2177
+ return conn.config;
2178
+ }
2179
+ async remove(agentId) {
2180
+ if (agentSpawner.getLocalAgentId() === agentId) {
2181
+ throw new Error("Cannot remove the local agent");
2182
+ }
2183
+ this.disconnectAgent(agentId);
2184
+ return agentStore.remove(agentId);
2185
+ }
2186
+ connectAgent(config) {
2187
+ const connection = {
2188
+ config,
2189
+ status: {
2190
+ connected: false,
2191
+ lastSeen: null,
2192
+ uptime: 0,
2193
+ runnersAvailable: []
2194
+ },
2195
+ reconnectDelay: MIN_RECONNECT_DELAY
2196
+ };
2197
+ this.connections.set(config.id, connection);
2198
+ this.openHealthWs(config.id);
2199
+ this.openBridge(config.id);
2200
+ }
2201
+ openBridge(agentId) {
2202
+ const conn = this.connections.get(agentId);
2203
+ if (!conn) return;
2204
+ console.log(`[agent-manager] Opening tRPC bridge for agent ${conn.config.name} (${agentId})`);
2205
+ const bridge = new AgentWSBridge(agentId, conn.config.url, conn.config.passphrase);
2206
+ conn.bridge = bridge;
2207
+ const eventTypes = ["process:status", "process:log", "process:health", "process:metrics"];
2208
+ for (const eventType of eventTypes) {
2209
+ bridge.on(eventType, (data) => {
2210
+ this.emit(eventType, data);
2211
+ });
2212
+ }
2213
+ bridge.connect();
2214
+ }
2215
+ disconnectAgent(agentId) {
2216
+ const conn = this.connections.get(agentId);
2217
+ if (!conn) return;
2218
+ console.log(`[agent-manager] Disconnecting agent ${conn.config.name} (${agentId})`);
2219
+ if (conn.healthWs) {
2220
+ conn.healthWs.removeAllListeners();
2221
+ conn.healthWs.close();
2222
+ conn.healthWs = void 0;
2223
+ }
2224
+ if (conn.bridge) {
2225
+ conn.bridge.disconnect();
2226
+ conn.bridge = void 0;
2227
+ }
2228
+ if (conn.reconnectTimer) {
2229
+ clearTimeout(conn.reconnectTimer);
2230
+ conn.reconnectTimer = void 0;
2231
+ }
2232
+ if (conn.aliveTimer) {
2233
+ clearTimeout(conn.aliveTimer);
2234
+ conn.aliveTimer = void 0;
2235
+ }
2236
+ this.connections.delete(agentId);
2237
+ this.emit("status", { agentId, status: { connected: false, lastSeen: null, uptime: 0, runnersAvailable: [] } });
2238
+ }
2239
+ openHealthWs(agentId) {
2240
+ const conn = this.connections.get(agentId);
2241
+ if (!conn) return;
2242
+ const httpUrl = new URL(conn.config.url);
2243
+ const wsProtocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
2244
+ const wsUrl = `${wsProtocol}//${httpUrl.host}/ws/health`;
2245
+ const ws = new WebSocket2(wsUrl, {
2246
+ headers: { "Authorization": `Bearer ${conn.config.passphrase}` }
2247
+ });
2248
+ conn.healthWs = ws;
2249
+ const resetAliveTimer = () => {
2250
+ if (conn.aliveTimer) clearTimeout(conn.aliveTimer);
2251
+ conn.aliveTimer = setTimeout(() => {
2252
+ console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) alive timeout \u2014 closing WS`);
2253
+ ws.close();
2254
+ }, ALIVE_TIMEOUT);
2255
+ };
2256
+ ws.on("open", () => {
2257
+ console.debug(`[agent-manager] WS open to agent ${conn.config.name} (${agentId})`);
2258
+ resetAliveTimer();
2259
+ });
2260
+ ws.on("message", (raw) => {
2261
+ try {
2262
+ const health = HealthMessageSchema.parse(
2263
+ JSON.parse(typeof raw === "string" ? raw : raw.toString())
2264
+ );
2265
+ resetAliveTimer();
2266
+ const wasConnected = conn.status.connected;
2267
+ conn.status = {
2268
+ connected: true,
2269
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
2270
+ uptime: health.uptime,
2271
+ runnersAvailable: health.runnersAvailable,
2272
+ lanIp: health.lanIp
2273
+ };
2274
+ conn.reconnectDelay = MIN_RECONNECT_DELAY;
2275
+ this.emit("status", { agentId, status: { ...conn.status } });
2276
+ if (!wasConnected) {
2277
+ console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) connected`);
2278
+ this.pushPassthroughEnv(agentId);
2279
+ this.reconcileAiSpawns(agentId);
2280
+ const rawTraefikConfig = agentStore.getTraefikConfig(agentId);
2281
+ if (rawTraefikConfig) {
2282
+ const parsed = TraefikConfigSchema.safeParse(rawTraefikConfig);
2283
+ if (parsed.success) {
2284
+ const configToPush = {
2285
+ ...parsed.data,
2286
+ dnsCredentials: traefikManager.decryptDnsCredentials(parsed.data.dnsCredentials ?? {})
2287
+ };
2288
+ this.pushTraefikConfig(agentId, configToPush);
2289
+ } else {
2290
+ console.error("[agent-manager] Invalid Traefik config in store for agent", agentId);
2291
+ }
2292
+ }
2293
+ }
2294
+ } catch (err) {
2295
+ console.debug("[agent-manager] Failed to parse health message for agent", agentId + ":", err);
2296
+ }
2297
+ });
2298
+ ws.on("close", () => {
2299
+ if (conn.aliveTimer) {
2300
+ clearTimeout(conn.aliveTimer);
2301
+ conn.aliveTimer = void 0;
2302
+ }
2303
+ conn.healthWs = void 0;
2304
+ if (conn.status.connected) {
2305
+ console.debug(`[agent-manager] Agent ${conn.config.name} (${agentId}) disconnected`);
2306
+ conn.status.connected = false;
2307
+ this.emit("status", { agentId, status: { ...conn.status } });
2308
+ }
2309
+ if (this.connections.has(agentId)) {
2310
+ this.scheduleReconnect(agentId);
2311
+ }
2312
+ });
2313
+ ws.on("error", (err) => {
2314
+ if (conn.status.connected) {
2315
+ console.debug("[agent-manager] WS error for agent", conn.config.name, "(" + agentId + "):", err.message);
2316
+ }
2317
+ });
2318
+ }
2319
+ scheduleReconnect(agentId) {
2320
+ const conn = this.connections.get(agentId);
2321
+ if (!conn) return;
2322
+ if (conn.reconnectTimer) {
2323
+ clearTimeout(conn.reconnectTimer);
2324
+ }
2325
+ conn.reconnectTimer = setTimeout(() => {
2326
+ conn.reconnectTimer = void 0;
2327
+ if (this.connections.has(agentId)) {
2328
+ this.openHealthWs(agentId);
2329
+ }
2330
+ }, conn.reconnectDelay);
2331
+ conn.reconnectDelay = Math.min(conn.reconnectDelay * 2, MAX_RECONNECT_DELAY);
2332
+ }
2333
+ /**
2334
+ * On agent (re)connect, discover any AI spawns still alive on the agent.
2335
+ * Log them for now — full rehydration of ActiveSessionRegistry is a follow-up.
2336
+ */
2337
+ async reconcileAiSpawns(agentId) {
2338
+ try {
2339
+ const spawns = await this.agentQuery(agentId, "ai.list");
2340
+ if (spawns.length > 0) {
2341
+ const summary = spawns.map((s) => `${s.spawnId} (${s.cli}, ${s.status}, buf:${s.bufferLength})`).join(", ");
2342
+ console.log("[agent-manager] Agent %s has %d AI spawn(s): %s", agentId, spawns.length, summary);
2343
+ for (const spawn2 of spawns) {
2344
+ if (spawn2.status !== "running") {
2345
+ this.agentMutation(agentId, "ai.ack", { spawnId: spawn2.spawnId }).catch(() => {
2346
+ });
2347
+ }
2348
+ }
2349
+ }
2350
+ } catch (err) {
2351
+ console.debug("[agent-manager] Failed to reconcile AI spawns for agent", agentId + ":", err instanceof Error ? err.message : String(err));
2352
+ }
2353
+ }
2354
+ // Push passthrough env variables to agent on (re)connection
2355
+ async pushPassthroughEnv(agentId) {
2356
+ const conn = this.connections.get(agentId);
2357
+ if (conn && conn.config.passthroughEnv && conn.config.passthroughEnv.length > 0) {
2358
+ try {
2359
+ await this.agentMutation(agentId, "config.setPassthroughEnv", {
2360
+ names: conn.config.passthroughEnv,
2361
+ values: resolvePassthroughValues(conn.config.passthroughEnv)
2362
+ });
2363
+ console.log("[agent-manager] Pushed passthroughEnv to agent", agentId + ":", conn.config.passthroughEnv.join(", "));
2364
+ } catch (err) {
2365
+ console.error("[agent-manager] Failed to push passthroughEnv to agent", agentId + ":", err instanceof Error ? err.message : String(err));
2366
+ }
2367
+ }
2368
+ }
2369
+ // Push Traefik config to agent on (re)connection or after update
2370
+ async pushTraefikConfig(agentId, config) {
2371
+ const conn = this.connections.get(agentId);
2372
+ if (!conn) return;
2373
+ try {
2374
+ await this.agentMutation(agentId, "config.setTraefikConfig", config);
2375
+ } catch (err) {
2376
+ console.error("[agent-manager] Failed to push Traefik config to agent", agentId + ":", err instanceof Error ? err.message : String(err));
2377
+ }
2378
+ }
2379
+ /** Returns the local agent ID, or first connected agent, or null. */
2380
+ getDefaultAgentId() {
2381
+ const localId = agentSpawner.getLocalAgentId();
2382
+ if (localId && this.connections.get(localId)?.status.connected) return localId;
2383
+ for (const [id, conn] of this.connections) {
2384
+ if (conn.status.connected) return id;
2385
+ }
2386
+ return null;
2387
+ }
2388
+ getAgent(agentId) {
2389
+ return this.connections.get(agentId);
2390
+ }
2391
+ getAgentConfig(agentId) {
2392
+ return this.connections.get(agentId)?.config;
2393
+ }
2394
+ listAgents() {
2395
+ const localId = agentSpawner.getLocalAgentId();
2396
+ return Array.from(this.connections.values()).map((conn) => ({
2397
+ config: { ...conn.config, passphrase: "***" },
2398
+ // Don't expose passphrase
2399
+ status: conn.status,
2400
+ isLocal: conn.config.id === localId
2401
+ }));
2402
+ }
2403
+ getStatus(agentId) {
2404
+ return this.connections.get(agentId)?.status ?? null;
2405
+ }
2406
+ async shutdown() {
2407
+ const ids = [...this.connections.keys()];
2408
+ for (const id of ids) {
2409
+ this.disconnectAgent(id);
2410
+ }
2411
+ }
2412
+ };
2413
+ var agentManager = new AgentManager();
2414
+
2415
+ export {
2416
+ agentStore,
2417
+ ScenarioGroupSchema,
2418
+ ScenarioNodeSchema,
2419
+ ScenarioSchema,
2420
+ AIProviderSchema,
2421
+ JSLogParserSchema,
2422
+ NotificationPreferencesSchema,
2423
+ SettingsSchema,
2424
+ TraefikConfigSchema,
2425
+ SettingsPrivateSchema,
2426
+ SchemaVersionSchema,
2427
+ LogSourceConfigSchema,
2428
+ HealthCheckSchema,
2429
+ DockerConfigSchema,
2430
+ RestartStrategySchema,
2431
+ ServiceShortcutSchema,
2432
+ ServiceCommandSchema,
2433
+ VariableSchema,
2434
+ CreateEnvironmentSchema,
2435
+ UpdateEnvironmentSchema,
2436
+ ApiKeyScopeSchema,
2437
+ ApiKeysFileSchema,
2438
+ KanbanColumnSchema,
2439
+ nativeParsers,
2440
+ JSLogParser,
2441
+ LOG_BUFFER_SIZE,
2442
+ AGENT_LONG_FETCH_TIMEOUT,
2443
+ AGENT_STOP_FETCH_TIMEOUT,
2444
+ SUPPORTED_EDITORS,
2445
+ EDITOR_BY_KEY,
2446
+ environmentStore,
2447
+ serviceStore,
2448
+ agentSpawner,
2449
+ traefikManager,
2450
+ agentManager
2451
+ };
2452
+ //# sourceMappingURL=chunk-XZPCEGWS.js.map