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