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