@runeya/runeya 2.0.44 → 2.0.46
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 +5 -5
- package/agent/index.js.map +1 -1
- package/agent-manager-JJYZDLNB.js +7 -0
- package/{chunk-NJJMESIY.js → chunk-5PRQBES2.js} +288 -72
- package/chunk-5PRQBES2.js.map +1 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/{src-7CJJBMY3.js → src-5QPLS5TQ.js} +1254 -483
- package/src-5QPLS5TQ.js.map +1 -0
- package/web/assets/AgentScalarChatInterface.vue-Z3BLbola.js +13 -0
- package/web/assets/AgentTerminalPage-BlQ3k7VX.css +1 -0
- package/web/assets/AgentTerminalPage-Dg4kOMbK.js +1 -0
- package/web/assets/AiChatPage-0igbt0Ie.js +1 -0
- package/web/assets/AppLayout-BQpUnN2N.js +1 -0
- package/web/assets/{AppLayout-BBifPBJu.css → AppLayout-D1bdzemo.css} +1 -1
- package/web/assets/AuditPage-zfim5OLa.js +1 -0
- package/web/assets/CloudAccountPage-BD-L27Wl.js +1 -0
- package/web/assets/CloudAccountPage-CHZG-vv-.js +1 -0
- package/web/assets/CloudMessagingPage-BnaGxzNK.js +2 -0
- package/web/assets/CloudMessagingPage-x4bDmgyb.css +1 -0
- package/web/assets/DashboardPage-ByzcpJV7.js +1 -0
- package/web/assets/{EditorIcon-CNesALrK.js → EditorIcon-jk0Nz8Mr.js} +2 -2
- package/web/assets/{EnvironmentForm-vcLpp9mR.js → EnvironmentForm-BH4EMxM6.js} +2 -2
- package/web/assets/EnvironmentFormPage-Bwjr8SsV.js +1 -0
- package/web/assets/EnvironmentListPage-BgqHe-4p.js +1 -0
- package/web/assets/EnvironmentListPage-BwgJscFQ.css +1 -0
- package/web/assets/EnvironmentSelector-CBh21uuY.js +1 -0
- package/web/assets/KanbanBoardPage-Dg0Od24D.js +4 -0
- package/web/assets/KanbanListPage-C6bsD-gJ.js +1 -0
- package/web/assets/LaunchProjectPage-BZe2kaRt.js +1 -0
- package/web/assets/LaunchProjectPage-Bqm88kwd.css +1 -0
- package/web/assets/LaunchServicesPage-B4xd4APa.js +1 -0
- package/web/assets/LaunchServicesPage-pWREsZTD.css +1 -0
- package/web/assets/LogViewer-BhaSYgvw.js +3 -0
- package/web/assets/ProcessControls-lDF1qNIM.js +1 -0
- package/web/assets/ProcessDetailPage-DwaApuHs.js +1 -0
- package/web/assets/{ProcessMetrics-dy3TGmKO.js → ProcessMetrics-rGmADRtS.js} +2 -2
- package/web/assets/ProjectDetailPage-BoIcnBMH.js +1 -0
- package/web/assets/ProjectDetailPage-hcU-rKhJ.css +1 -0
- package/web/assets/ProjectListPage-C3JtBL-6.js +1 -0
- package/web/assets/ProjectListPage-DYs6X7r2.css +1 -0
- package/web/assets/RandomJobTestPage-CndKGKYR.js +2 -0
- package/web/assets/ScenarioEditorPage-DU7tb4M-.js +14 -0
- package/web/assets/SendToChatDialog-CCRCitBB.js +1 -0
- package/web/assets/ServiceControlsBar-BjyMYoiN.js +1 -0
- package/web/assets/{ServiceDetailPage-BvoD3SB6.css → ServiceDetailPage-BRg1byDZ.css} +1 -1
- package/web/assets/ServiceDetailPage-JqpmujmY.js +2 -0
- package/web/assets/ServiceFormPage-B8jcMT3b.css +1 -0
- package/web/assets/ServiceFormPage-DgNwiYzM.js +1 -0
- package/web/assets/SettingsPage-BZM3WWqc.js +2062 -0
- package/web/assets/UnlockPage-BcZ2vL57.js +1 -0
- package/web/assets/UnlockPage-DE_HJy8h.css +1 -0
- package/web/assets/VariablesTable-0Gz2ffiL.js +1 -0
- package/web/assets/VaultMigrationPage-BGJ2PP-7.css +1 -0
- package/web/assets/VaultMigrationPage-BxTBDL19.js +1 -0
- package/web/assets/VersionBadge-DvsaGqvh.js +1 -0
- package/web/assets/WikiPage-DkEQXkzp.js +1 -0
- package/web/assets/WikiView-e81ejcA8.js +1 -0
- package/web/assets/add-scalar-classes-Bh8ghfnQ.js +387 -0
- package/web/assets/auth-errors-C1B7h06f.js +1 -0
- package/web/assets/{autocomplete-egYkKgXN.js → autocomplete-CDWkaHAi.js} +4 -4
- package/web/assets/{basic-shape-diamond.svg-B4ql31Pl.js → basic-shape-diamond.svg-7zqPzGW0.js} +1 -1
- package/web/assets/{basic-shape-hexagon.svg-Dse435xU.js → basic-shape-hexagon.svg-DQwxqMd2.js} +1 -1
- package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-Ca80MM2U.js → basic-shape-primary-circle-ellipse-round.svg-Bqm796vK.js} +1 -1
- package/web/assets/{basic-shape-primary-square-rectangle.svg-BzMOacCe.js → basic-shape-primary-square-rectangle.svg-D7eyO04i.js} +1 -1
- package/web/assets/{basic-shape-shield.svg-CDpbzQoc.js → basic-shape-shield.svg-Bnc_pFVA.js} +1 -1
- package/web/assets/{browser-CASFdUlr.js → browser-DeXg6zUY.js} +1 -1
- package/web/assets/{browser-B6yA2X2D.js → browser-LxxhMX-Z.js} +1 -1
- package/web/assets/card-RaXQSIfv.js +32 -0
- package/web/assets/{checkbox-DX42-aCG.js → checkbox-DnpUXs7B.js} +3 -3
- package/web/assets/{chip-CDVNzEoh.js → chip-0f0r_-zR.js} +2 -2
- package/web/assets/{client-wwEU7lJc.js → client-CTzGkOjO.js} +2 -2
- package/web/assets/cloud-chat.store-D2oJ_DFn.js +1 -0
- package/web/assets/cloud-session-BLn7b3ke.js +1 -0
- package/web/assets/column-DqAehgUU.js +907 -0
- package/web/assets/{computer-device-desktop-monitor.svg-QvdUlZtb.js → computer-device-desktop-monitor.svg-k2x9Oiiu.js} +1 -1
- package/web/assets/{computer-device-desktop.svg-C9Sr1yFe.js → computer-device-desktop.svg-CyFljvrh.js} +1 -1
- package/web/assets/{computer-device-laptop.svg-CQbvy4qG.js → computer-device-laptop.svg-ZKpV0gtW.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-DRwNxxcl.js → computer-device-mobile-phone-android-samsung-back.svg-BkRkRpCh.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-android-samsung.svg-DZiXwi21.js → computer-device-mobile-phone-android-samsung.svg-gpUXFo9R.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-D1eMrnSS.js → computer-device-mobile-phone-iphone-x-back.svg-CJwr0WJ3.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-iphone-x.svg-MiK72cdg.js → computer-device-mobile-phone-iphone-x.svg-BsPF_njN.js} +1 -1
- package/web/assets/{computer-device-mobile-tablet-touch.svg-DkPhnoi4.js → computer-device-mobile-tablet-touch.svg-DON7qRSP.js} +1 -1
- package/web/assets/{computer-device-mobile-tablet.svg-CDuR2wiu.js → computer-device-mobile-tablet.svg-BPnT3MvS.js} +1 -1
- package/web/assets/{computer-device-network-ethernet-cat6.svg-BUBM3jji.js → computer-device-network-ethernet-cat6.svg-Bpjv_2ub.js} +1 -1
- package/web/assets/{computer-device-network-lan-www.svg-28lu85cP.js → computer-device-network-lan-www.svg-BhXBIyrf.js} +1 -1
- package/web/assets/{computer-device-network-wifi-connection.svg-BGEvw9iY.js → computer-device-network-wifi-connection.svg-DUaA1GcU.js} +1 -1
- package/web/assets/{computer-device-network-wifi-router.svg-D8xYZAWK.js → computer-device-network-wifi-router.svg-k_DqQho1.js} +1 -1
- package/web/assets/{cssMode-Dvq-MecZ.js → cssMode-CfVTS8OM.js} +1 -1
- package/web/assets/{divider-B5VXYGmb.js → divider-C1huit3M.js} +2 -2
- package/web/assets/{ecology-science-erlenmeyer-flask.svg-Ct1P3ToA.js → ecology-science-erlenmeyer-flask.svg-D7pbgLTT.js} +1 -1
- package/web/assets/{fieldset-CIscNGD5.js → fieldset-BVRjgmg4.js} +2 -2
- package/web/assets/{freemarker2-CqFs1Djg.js → freemarker2-DsY_gjrP.js} +1 -1
- package/web/assets/{handlebars-i2b7C1iO.js → handlebars-mh1HrGq7.js} +1 -1
- package/web/assets/{html-BBmJW8Cg.js → html-BsaAhzOa.js} +1 -1
- package/web/assets/{htmlMode-DPAh_Y5y.js → htmlMode-pIhI3cAi.js} +1 -1
- package/web/assets/{image-flash-lightning.svg-BSAfy_cZ.js → image-flash-lightning.svg-DDbhd-eu.js} +1 -1
- package/web/assets/{image-picture-flower.svg-4AEEapQs.js → image-picture-flower.svg-Bbx1pu02.js} +1 -1
- package/web/assets/{index-BqCmf99K.css → index-D7XqcLhE.css} +1 -1
- package/web/assets/{index-Br0Stb0u.js → index-Do0zA8cN.js} +291 -291
- package/web/assets/{interface-alert-exclamation-diamond.svg-DclDPUml.js → interface-alert-exclamation-diamond.svg-DUJeN1hs.js} +1 -1
- package/web/assets/{interface-alert-exclamation-triangle-warning.svg-AQlrdI6-.js → interface-alert-exclamation-triangle-warning.svg-5y99yZSY.js} +1 -1
- package/web/assets/{interface-alert-information-circle.svg-C70b_ORM.js → interface-alert-information-circle.svg-DPGP01ZU.js} +1 -1
- package/web/assets/{interface-award-crown.svg-D-uMX_D5.js → interface-award-crown.svg-GDQ8ZRUh.js} +1 -1
- package/web/assets/{interface-bookmark.svg-DdNPAQF4.js → interface-bookmark-tag.svg-D1NUMLB5.js} +1 -1
- package/web/assets/{interface-bookmark-tag.svg-BBO8UdsI.js → interface-bookmark.svg-CTZlXD4R.js} +1 -1
- package/web/assets/{interface-calendar-date-one.svg-C5m8QsAn.js → interface-calendar-date-one.svg-DXo4raY8.js} +1 -1
- package/web/assets/{interface-content-book-open-pages.svg-D9gk_nRv.js → interface-content-book-open-pages.svg-DTsCVw_D.js} +1 -1
- package/web/assets/{interface-content-book-page.svg-z_oQ-Kg8.js → interface-content-book-page.svg-w9azHLUe.js} +1 -1
- package/web/assets/{interface-content-file.svg-CLt_X895.js → interface-content-file.svg-69CCnbNb.js} +1 -1
- package/web/assets/{interface-content-folder.svg-BW_H9jDi.js → interface-content-folder.svg-85PnJNaK.js} +1 -1
- package/web/assets/interface-content-folder.svg-Db3hJMdF.js +1 -0
- package/web/assets/{interface-copy-clipboard.svg-D89FD0hT.js → interface-copy-clipboard.svg-B0AfVljN.js} +1 -1
- package/web/assets/{interface-edit-attachment.svg-CSax0FWg.js → interface-edit-attachment.svg-mFLLqqcw.js} +1 -1
- package/web/assets/{interface-edit-binocular.svg-C1t3PxkA.js → interface-edit-binocular.svg-CHzgny3z.js} +1 -1
- package/web/assets/{interface-edit-magic-wand.svg-P-HVaqcv.js → interface-edit-magic-wand.svg-BbJg9LMT.js} +1 -1
- package/web/assets/{interface-edit-tool-paint-roller.svg-BCd-B5Li.js → interface-edit-tool-paint-roller.svg-YKE28Xpc.js} +1 -1
- package/web/assets/{interface-edit-tool-pencil.svg-Dy_8C148.js → interface-edit-tool-pencil.svg-Cy12MfEi.js} +1 -1
- package/web/assets/{interface-favorite-award.svg-BOUSPQD8.js → interface-favorite-award.svg-COiLJu4w.js} +1 -1
- package/web/assets/{interface-favorite-flag.svg-yj88TNA3.js → interface-favorite-flag.svg-C0NtaPD_.js} +1 -1
- package/web/assets/{interface-favorite-heart.svg-CZCH_6x9.js → interface-favorite-heart.svg-CcKJOjOh.js} +1 -1
- package/web/assets/{interface-favorite-star.svg-CTyJilUu.js → interface-favorite-star.svg-C3vRaRtM.js} +1 -1
- package/web/assets/{interface-favorite-stars-sparkles.svg-BOBjnmtV.js → interface-favorite-stars-sparkles.svg-DN9WOkFs.js} +1 -1
- package/web/assets/{interface-hierarchy-flowchart.svg-B0S-kTTJ.js → interface-hierarchy-flowchart.svg-CffgUWNS.js} +1 -1
- package/web/assets/{interface-home-house.svg-Dku5UxpT.js → interface-home-house.svg-B9c4N9Fi.js} +1 -1
- package/web/assets/{interface-hyperlink.svg-CJwD4AsW.js → interface-hyperlink.svg-B8dXB276.js} +1 -1
- package/web/assets/{interface-lighting-brightness.svg-DwLV7f-n.js → interface-lighting-brightness.svg-BjFVAOW3.js} +1 -1
- package/web/assets/{interface-lock-closed.svg-9XB_WC1w.js → interface-lock-closed.svg-DwNcKmwu.js} +1 -1
- package/web/assets/{interface-lock-open-unlock.svg-DppiuvYV.js → interface-lock-open-unlock.svg-BXZeKlHt.js} +1 -1
- package/web/assets/{interface-login-key.svg-C4pLwiam.js → interface-login-key.svg-CQnZGVMJ.js} +1 -1
- package/web/assets/{interface-search.svg-BfLZwjYy.js → interface-search.svg-CXre9Ja4.js} +1 -1
- package/web/assets/{interface-setting-cog.svg-Cv8hVyGq.js → interface-setting-cog.svg-CGK2QxqV.js} +1 -1
- package/web/assets/{interface-share-megaphone-bullhorn.svg-BVlc-dhY.js → interface-share-megaphone-bullhorn.svg-BcdTvuin.js} +1 -1
- package/web/assets/{interface-share-rocket.svg-ChHMNOi9.js → interface-share-rocket.svg-DIvKBIQX.js} +1 -1
- package/web/assets/{interface-share-satellite.svg-Bh7sMl8A.js → interface-share-satellite.svg-DNbqAorz.js} +1 -1
- package/web/assets/{interface-share-space-ship.svg-CcQ-3-tc.js → interface-share-space-ship.svg-Biobo33q.js} +1 -1
- package/web/assets/{interface-share.svg-CCg2aMOy.js → interface-share.svg-DWjt5rBb.js} +1 -1
- package/web/assets/{interface-signal-square.svg-CU31YeuE.js → interface-signal-square.svg--hmv5CmE.js} +1 -1
- package/web/assets/{interface-time-clock-circle.svg-BUUuMbzp.js → interface-time-clock-circle.svg-C1GnMIQD.js} +1 -1
- package/web/assets/{interface-time-hour-glass.svg-W7cfslag.js → interface-time-hour-glass.svg-BCRsHIbm.js} +1 -1
- package/web/assets/{interface-users-multiple.svg-DjyQMEmj.js → interface-users-multiple.svg-CUpeUldF.js} +1 -1
- package/web/assets/{interface-weather-moon.svg-C2sv5n1P.js → interface-weather-moon.svg-QXMY6B27.js} +1 -1
- package/web/assets/{javascript-D-ps7-Fj.js → javascript-DKeyayKI.js} +1 -1
- package/web/assets/{jsonMode-C8spHQzS.js → jsonMode-I9zemAn2.js} +1 -1
- package/web/assets/{liquid-DqSbumgt.js → liquid-xEdzGxP1.js} +1 -1
- package/web/assets/{lspLanguageFeatures-CY8c9PkF.js → lspLanguageFeatures-DOVjCuiu.js} +1 -1
- package/web/assets/{mail-chat-bubble-square.svg-C3X6jNwQ.js → mail-chat-bubble-square.svg-CR8byO78.js} +1 -1
- package/web/assets/{mail-send-email-paper-airplane.svg-DAIGQOyP.js → mail-send-email-paper-airplane.svg-B0cIMUKH.js} +1 -1
- package/web/assets/{mail-send-envelope.svg-CA0S-i50.js → mail-send-envelope.svg-El4e7bXy.js} +1 -1
- package/web/assets/{mdx-DvZHY6Gn.js → mdx-AWoH37r-.js} +1 -1
- package/web/assets/{message-Cj-E8qHk.js → message-CATQqbP_.js} +2 -2
- package/web/assets/{minus-Bk1xGw-l.js → minus-Sd1wO9DO.js} +2 -2
- package/web/assets/modal-BbDElcmR.js +1 -0
- package/web/assets/modal-ZXFHQ6vP.js +5 -0
- package/web/assets/{money-cashier-receipt.svg-TBP6hXoT.js → money-cashier-receipt.svg-BIEGQvlX.js} +1 -1
- package/web/assets/{money-currency-dollar-pay.svg-DYwjJWCD.js → money-currency-dollar-pay.svg-BgCHAYq5.js} +1 -1
- package/web/assets/{money-graph-arrow-increase.svg-BR1Gr97f.js → money-graph-arrow-increase.svg-q6aDu3og.js} +1 -1
- package/web/assets/{money-graph-bar-chart-increase.svg-BNAoJq2R.js → money-graph-bar-chart-increase.svg-mRvG_dWg.js} +1 -1
- package/web/assets/multiselect-BK7hJt7W.js +251 -0
- package/web/assets/{nature-ecology-leaf.svg-CHJE8I4R.js → nature-ecology-leaf.svg-DdhvC8mq.js} +1 -1
- package/web/assets/{password-tS13MOUm.js → password-Dnf9oU5a.js} +4 -4
- package/web/assets/{phone-telephone.svg-CJQLiuXQ.js → phone-telephone.svg-CjbyxKZA.js} +1 -1
- package/web/assets/{plus-CB_mX6Lf.js → plus-jYeRYaw9.js} +6 -6
- package/web/assets/process.store-C5LXrPXo.js +1 -0
- package/web/assets/{programming-bug.svg-BXjb-M36.js → programming-bug.svg-DvDCWw4P.js} +1 -1
- package/web/assets/{programming-cloud.svg-gt7FIwaa.js → programming-cloud.svg-2prWAqB7.js} +1 -1
- package/web/assets/{programming-computer-database-server.svg-BtO4lpAh.js → programming-computer-database-server.svg-CT7k5d_p.js} +1 -1
- package/web/assets/{programming-computer-database.svg-DQ-dZqw4.js → programming-computer-database.svg-Dm7fpvII.js} +1 -1
- package/web/assets/{programming-module-four-layout.svg-laiWKIf7.js → programming-module-four-layout.svg-DBv6QvEO.js} +1 -1
- package/web/assets/{programming-module-three.svg--oNt7CzW.js → programming-module-three.svg-BnP96bPJ.js} +1 -1
- package/web/assets/{programming-module.svg-C9Ai98wU.js → programming-module.svg-Bdr4ZdZO.js} +1 -1
- package/web/assets/{programming-script-code.svg-DhbU3qfH.js → programming-script-code.svg-DIcr6sF-.js} +1 -1
- package/web/assets/{python-BQIuDGub.js → python-C554d-pk.js} +1 -1
- package/web/assets/{razor-C_wqRSVy.js → razor-BvOiTjBp.js} +1 -1
- package/web/assets/router-CCEIu6U5.js +1 -0
- package/web/assets/settings.store-DISlY-48.js +1 -0
- package/web/assets/{shopping-cart.svg-D-mwPXiz.js → shopping-cart.svg-DoCVSOin.js} +1 -1
- package/web/assets/{shopping-gift-present.svg-CplnpU1q.js → shopping-gift-present.svg-CgNqOSQr.js} +1 -1
- package/web/assets/{shopping-shipping-box-parcel-package.svg-D0k-fVoU.js → shopping-shipping-box-parcel-package.svg-YkVPEp7J.js} +1 -1
- package/web/assets/src-Cvt5pswj.js +1 -0
- package/web/assets/{src-D0-roPyX.js → src-DKVQlKvZ.js} +33 -33
- package/web/assets/{tabpanel-BXlQXZ2Q.js → tabpanel-DFoLVdFR.js} +3 -3
- package/web/assets/{tag-new-circle.svg-msSLdpvt.js → tag-new-circle.svg-BeUe12X2.js} +1 -1
- package/web/assets/{travel-map-earth-globe.svg-BfNt3fj_.js → travel-map-earth-globe.svg-B1I1Ed-v.js} +1 -1
- package/web/assets/trpc-CVPkysoa.js +1 -0
- package/web/assets/{tsMode-B-iW-fA7.js → tsMode-BPD-0Vep.js} +1 -1
- package/web/assets/{typescript-X0FI4nNI.js → typescript-Q2uKnndS.js} +1 -1
- package/web/assets/useAiChatDrawer-B70XYuU8.js +1 -0
- package/web/assets/useCloudOrg-CZ68epfW.js +1 -0
- package/web/assets/useProjectCatalog-Kh_sHbSD.js +1 -0
- package/web/assets/{xml-BGZfBXt_.js → xml-B0CfAsFk.js} +1 -1
- package/web/assets/{yaml-Bu_QXl8g.js → yaml-B3zRI6AS.js} +1 -1
- package/web/index.html +2 -2
- package/agent-manager-2I47P7CA.js +0 -7
- package/chunk-NJJMESIY.js.map +0 -1
- package/src-7CJJBMY3.js.map +0 -1
- package/web/assets/AgentScalarChatInterface.vue-DJWCM29h.js +0 -13
- package/web/assets/AgentTerminalPage-CQxk6jVs.css +0 -1
- package/web/assets/AgentTerminalPage-DStCvPsn.js +0 -1
- package/web/assets/AiChatPage-B74k4w7z.js +0 -1
- package/web/assets/AppLayout-Crr8vrnA.js +0 -1
- package/web/assets/AuditPage-ya7Q_-y8.js +0 -1
- package/web/assets/CloudAccountPage--zPhBcyX.js +0 -1
- package/web/assets/CloudAccountPage-BsXQlx4x.js +0 -1
- package/web/assets/CloudMessagingPage-DbhpPX4q.css +0 -1
- package/web/assets/CloudMessagingPage-sXT8SLY4.js +0 -2
- package/web/assets/DashboardPage-y4tWdya9.js +0 -1
- package/web/assets/EnvironmentFormPage-BSeIsEH-.js +0 -1
- package/web/assets/EnvironmentListPage-BDqcug66.css +0 -1
- package/web/assets/EnvironmentListPage-DtSTzbmv.js +0 -1
- package/web/assets/EnvironmentSelector-Dbe1Ilq8.js +0 -1
- package/web/assets/KanbanBoardPage-PkAwHbvp.js +0 -4
- package/web/assets/KanbanListPage-BphN58qM.js +0 -1
- package/web/assets/LaunchProjectPage-ClVBkFsC.js +0 -1
- package/web/assets/LaunchProjectPage-CxXC2U3O.css +0 -1
- package/web/assets/LaunchServicesPage-BAvD32G1.css +0 -1
- package/web/assets/LaunchServicesPage-C4C8l5wP.js +0 -1
- package/web/assets/LogViewer-DeWk02W9.js +0 -3
- package/web/assets/ProcessControls-uUzThwLe.js +0 -1
- package/web/assets/ProcessDetailPage-Dj9-KZqI.js +0 -1
- package/web/assets/ProjectDetailPage-B42imx75.css +0 -1
- package/web/assets/ProjectDetailPage-Prcd6a6y.js +0 -1
- package/web/assets/ProjectListPage-BGU6SKAr.css +0 -1
- package/web/assets/ProjectListPage-C3-QvMAf.js +0 -1
- package/web/assets/RandomJobTestPage-B5USJ2CQ.js +0 -2
- package/web/assets/ScenarioEditorPage-D0XY_WOp.js +0 -14
- package/web/assets/SendToChatDialog-BSa5s8PE.js +0 -1
- package/web/assets/ServiceControlsBar-CRVx5mMr.js +0 -1
- package/web/assets/ServiceDetailPage-DzxAxNdH.js +0 -2
- package/web/assets/ServiceFormPage-D31sr_k0.css +0 -1
- package/web/assets/ServiceFormPage-_3cU1hqt.js +0 -1
- package/web/assets/SettingsPage-B0mpjswp.js +0 -2062
- package/web/assets/UnlockPage-DAvZd_hY.js +0 -1
- package/web/assets/UnlockPage-cWvMhfyi.css +0 -1
- package/web/assets/VariablesTable-CoeTpHQw.js +0 -1
- package/web/assets/VersionBadge-D_oFrbj6.js +0 -1
- package/web/assets/WikiPage-HEXd9iFN.js +0 -1
- package/web/assets/WikiView-D_gNl0pw.js +0 -1
- package/web/assets/add-scalar-classes-B5XCUsCH.js +0 -387
- package/web/assets/auth-errors-C44F3O24.js +0 -1
- package/web/assets/card-DLRYSKgj.js +0 -32
- package/web/assets/cloud-chat.store-2Mksi6iL.js +0 -1
- package/web/assets/cloud-session-C0DzgONu.js +0 -1
- package/web/assets/column-TZbNKWtE.js +0 -907
- package/web/assets/interface-content-folder.svg-BJQbuSNW.js +0 -1
- package/web/assets/modal-Bi7DAFyz.js +0 -1
- package/web/assets/modal-BtlOliEo.js +0 -5
- package/web/assets/multiselect-DI4UWQQc.js +0 -251
- package/web/assets/process.store-DCXMZqHz.js +0 -1
- package/web/assets/router-B5tVgvg3.js +0 -1
- package/web/assets/settings.store-p3x3oG-N.js +0 -1
- package/web/assets/src-B_ytlQOt.js +0 -1
- package/web/assets/trpc--zmPP0fK.js +0 -1
- package/web/assets/useAiChatDrawer-CmZm2O_C.js +0 -1
- package/web/assets/useCloudOrg-BsMHgwHL.js +0 -1
- /package/{agent-manager-2I47P7CA.js.map → agent-manager-JJYZDLNB.js.map} +0 -0
- /package/web/assets/{abap-B8xnHZJW.js → abap-DMmvWZUy.js} +0 -0
- /package/web/assets/{apex-B69W3sGq.js → apex-D89PiTzU.js} +0 -0
- /package/web/assets/{azcli-Eaai8vgm.js → azcli-ChnjTTA5.js} +0 -0
- /package/web/assets/{bat-CCyh2P9V.js → bat-uLzQ_FhK.js} +0 -0
- /package/web/assets/{bicep-CyAc2Run.js → bicep-CMOK7Q1v.js} +0 -0
- /package/web/assets/{cameligo-CJVuMDnv.js → cameligo-D1AyojFH.js} +0 -0
- /package/web/assets/{clojure-DCWtDuXT.js → clojure-D9JoUgXn.js} +0 -0
- /package/web/assets/{coffee-93zwCqZu.js → coffee-D-5sSToL.js} +0 -0
- /package/web/assets/{cpp-t1qXcBbB.js → cpp-BqT_t_y9.js} +0 -0
- /package/web/assets/{csharp-Bu0-wvSx.js → csharp-CuToYI91.js} +0 -0
- /package/web/assets/{csp-AaiYnRi_.js → csp-CffcxvlO.js} +0 -0
- /package/web/assets/{css-BS-T3bCT.js → css-CUD0biQW.js} +0 -0
- /package/web/assets/{cypher-QYdwWDet.js → cypher-ByyWL_9U.js} +0 -0
- /package/web/assets/{dart-DRQfRz5u.js → dart-B__ipcUn.js} +0 -0
- /package/web/assets/{dockerfile-Cf422HaY.js → dockerfile-DS-_8bWw.js} +0 -0
- /package/web/assets/{ecl-DKmCxEZC.js → ecl-DWsNCBUj.js} +0 -0
- /package/web/assets/{elixir-DXxuFQwb.js → elixir-OfjEy-3g.js} +0 -0
- /package/web/assets/{flow9-BhnEDmps.js → flow9-BPX22hxS.js} +0 -0
- /package/web/assets/{fsharp-BcdMpySh.js → fsharp-CVJcfIUn.js} +0 -0
- /package/web/assets/{go-CUNzU43j.js → go-CfRHZ3yg.js} +0 -0
- /package/web/assets/{graphql-0GEdoWdT.js → graphql-BZaO3ttK.js} +0 -0
- /package/web/assets/{hcl-7aT0BoBu.js → hcl-BF9-qUku.js} +0 -0
- /package/web/assets/{ini-C2uC_Kkd.js → ini-BUvw6xhr.js} +0 -0
- /package/web/assets/{java-BGh_orDD.js → java-BZ5BNd2x.js} +0 -0
- /package/web/assets/{julia-BMkYL7l7.js → julia-DN38cgYT.js} +0 -0
- /package/web/assets/{kotlin-CaStfl8T.js → kotlin-CjEJKT8q.js} +0 -0
- /package/web/assets/{less-CpP708lq.js → less-DMEH_Nwq.js} +0 -0
- /package/web/assets/{lexon-n74w-VMa.js → lexon-CKQUX8Ov.js} +0 -0
- /package/web/assets/{lua-DV3MQq-H.js → lua-CuWjKLJ0.js} +0 -0
- /package/web/assets/{m3-Cclze1bC.js → m3-CEp1bVrM.js} +0 -0
- /package/web/assets/{markdown-C8b0Ckdo.js → markdown-DxtQ_puq.js} +0 -0
- /package/web/assets/{mips-pWj6zxei.js → mips-Dx7qydpO.js} +0 -0
- /package/web/assets/{msdax-DvyywuD0.js → msdax-DpNCaSiO.js} +0 -0
- /package/web/assets/{mysql-CZlMFPhf.js → mysql-C3xsBDPP.js} +0 -0
- /package/web/assets/{objective-c-Dfzs1cqa.js → objective-c-BbaVHCX1.js} +0 -0
- /package/web/assets/{pascal-DnvBS6YC.js → pascal-sDju2faq.js} +0 -0
- /package/web/assets/{pascaligo-BqQrNWHm.js → pascaligo-ChCdOH6b.js} +0 -0
- /package/web/assets/{perl-PmXI7PaC.js → perl-BCgLFj01.js} +0 -0
- /package/web/assets/{pgsql-BSIv6vTg.js → pgsql-DtKby69f.js} +0 -0
- /package/web/assets/{php-DFl1Flu9.js → php-NYvR17Qb.js} +0 -0
- /package/web/assets/{pla-D85akHEG.js → pla-gSg-RBG0.js} +0 -0
- /package/web/assets/{postiats-BENv2q_G.js → postiats-BQxHyDfy.js} +0 -0
- /package/web/assets/{powerquery-BFkjDbIZ.js → powerquery-DzDmbVZN.js} +0 -0
- /package/web/assets/{powershell-YrWnOFV5.js → powershell-yy4Q5BEQ.js} +0 -0
- /package/web/assets/{protobuf-Defuvypk.js → protobuf-B_hIk9q0.js} +0 -0
- /package/web/assets/{pug-U5vTkfZW.js → pug-CdeEb1ft.js} +0 -0
- /package/web/assets/{qsharp-CYAWcALE.js → qsharp-CiSquEgT.js} +0 -0
- /package/web/assets/{r-DfDvKIQI.js → r-D1Ocz1x4.js} +0 -0
- /package/web/assets/{redis-CfxXb5P7.js → redis-D42Y12yz.js} +0 -0
- /package/web/assets/{redshift-C79hPgxo.js → redshift-CS62MYfG.js} +0 -0
- /package/web/assets/{restructuredtext-8wPbRyeC.js → restructuredtext-CKFSMEXw.js} +0 -0
- /package/web/assets/{ruby-QxUAWasq.js → ruby-C4gTaeU3.js} +0 -0
- /package/web/assets/{rust-HuSf37VN.js → rust-C_f2AiHQ.js} +0 -0
- /package/web/assets/{sb-Sv7lt4AB.js → sb-CdNdpP1r.js} +0 -0
- /package/web/assets/{scala-DKemzKKU.js → scala-N4EWaFTL.js} +0 -0
- /package/web/assets/{scheme-BzHyg2h9.js → scheme-CttIARxx.js} +0 -0
- /package/web/assets/{scss-rEyGzHl_.js → scss-v0wtbxaE.js} +0 -0
- /package/web/assets/{shell-B0M_8Ipp.js → shell-BJdEWXbc.js} +0 -0
- /package/web/assets/{solidity-DBlqOy8H.js → solidity-DKDa59zZ.js} +0 -0
- /package/web/assets/{sophia-CvF25coV.js → sophia--Kw-vDpr.js} +0 -0
- /package/web/assets/{sparql-Bh-bEONh.js → sparql-BI8jS9Pc.js} +0 -0
- /package/web/assets/{sql-b2efFD_k.js → sql-Dz6z--6O.js} +0 -0
- /package/web/assets/{st-CZYKif4r.js → st-CT8D05gv.js} +0 -0
- /package/web/assets/{swift--G_pPFZi.js → swift-Ayb3KzWx.js} +0 -0
- /package/web/assets/{systemverilog-lKrXkwoz.js → systemverilog-BtWJM9O5.js} +0 -0
- /package/web/assets/{tcl-BBD-NhPH.js → tcl-BJvUWpbV.js} +0 -0
- /package/web/assets/{twig-Cp-9q602.js → twig-c3YaBCU6.js} +0 -0
- /package/web/assets/{typespec-CFPGsTQu.js → typespec-gFCWLBoZ.js} +0 -0
- /package/web/assets/{uuid-2Kb5PDpq.js → uuid-B3B8HIK9.js} +0 -0
- /package/web/assets/{vb-CF5A6m2D.js → vb-CVtfWXV2.js} +0 -0
- /package/web/assets/{w3c-keyname-B2B-WOb1.js → w3c-keyname-nn0G3QC2.js} +0 -0
- /package/web/assets/{wgsl-DwzoWMFM.js → wgsl-C_huaGQy.js} +0 -0
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
AGENT_LONG_FETCH_TIMEOUT,
|
|
3
3
|
AGENT_STOP_FETCH_TIMEOUT,
|
|
4
4
|
AIProviderSchema,
|
|
5
|
+
AmbiguousProjectError,
|
|
5
6
|
ApiKeyScopeSchema,
|
|
6
7
|
ApiKeysFileSchema,
|
|
7
8
|
CreateEnvironmentSchema,
|
|
@@ -35,11 +36,14 @@ import {
|
|
|
35
36
|
clearEncryptionKey,
|
|
36
37
|
clearPendingLegacyKey,
|
|
37
38
|
decryptValue,
|
|
39
|
+
decryptValueWith,
|
|
38
40
|
decryptVariableSlots,
|
|
39
41
|
encryptValue,
|
|
42
|
+
encryptValueWith,
|
|
40
43
|
encryptVariableSlots,
|
|
41
44
|
ensureEncryptionKey,
|
|
42
45
|
ensureLocalVault,
|
|
46
|
+
ensureProjectsLoaded,
|
|
43
47
|
env,
|
|
44
48
|
environmentStore,
|
|
45
49
|
extractEncryptedVariableSlots,
|
|
@@ -54,6 +58,7 @@ import {
|
|
|
54
58
|
keyRoot,
|
|
55
59
|
keyedFilePath,
|
|
56
60
|
legacyMachineFilePath,
|
|
61
|
+
legacyVaultRoot,
|
|
57
62
|
listCloudVaults,
|
|
58
63
|
loadFromEnv,
|
|
59
64
|
localVaultRoot,
|
|
@@ -61,10 +66,13 @@ import {
|
|
|
61
66
|
machineRoot,
|
|
62
67
|
nativeParsers,
|
|
63
68
|
orgVaultRoot,
|
|
69
|
+
projectKey,
|
|
64
70
|
projectVaultRoot,
|
|
65
71
|
readEntity,
|
|
72
|
+
readOrgClouds,
|
|
73
|
+
refOfProjectAmong,
|
|
66
74
|
registerProjectLoader,
|
|
67
|
-
|
|
75
|
+
rootOfProjectAmong,
|
|
68
76
|
serviceStore,
|
|
69
77
|
setEncryptionKey,
|
|
70
78
|
setEncryptionKeyInMemory,
|
|
@@ -72,16 +80,17 @@ import {
|
|
|
72
80
|
setProjectServices,
|
|
73
81
|
setProjectVault,
|
|
74
82
|
stableEncryptValue,
|
|
83
|
+
stampOrgCloud,
|
|
75
84
|
traefikManager,
|
|
76
85
|
vaultFor,
|
|
77
86
|
writeEntity
|
|
78
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-5PRQBES2.js";
|
|
79
88
|
|
|
80
89
|
// ../server/src/create-server.ts
|
|
81
90
|
import express from "express";
|
|
82
91
|
import cors from "cors";
|
|
83
92
|
import { createServer } from "http";
|
|
84
|
-
import { resolve as
|
|
93
|
+
import { resolve as resolve6 } from "path";
|
|
85
94
|
import { WebSocketServer } from "ws";
|
|
86
95
|
import { createExpressMiddleware } from "@trpc/server/adapters/express";
|
|
87
96
|
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
|
@@ -542,6 +551,12 @@ var LocalProjectExistsError = class extends Error {
|
|
|
542
551
|
code = "LOCAL_PROJECT_EXISTS";
|
|
543
552
|
};
|
|
544
553
|
var ProjectStore = class {
|
|
554
|
+
/**
|
|
555
|
+
* Keyed by the (organization, project) couple, never by the project id alone:
|
|
556
|
+
* the same id can name a different project in each organization, and keying
|
|
557
|
+
* by id let the second one read from disk overwrite the first — then `save()`
|
|
558
|
+
* wrote that survivor back into both vaults.
|
|
559
|
+
*/
|
|
545
560
|
projects = /* @__PURE__ */ new Map();
|
|
546
561
|
loaded = false;
|
|
547
562
|
/** Emits `'saved'` after every persisted mutation, for the cloud sync bridge. */
|
|
@@ -559,15 +574,15 @@ var ProjectStore = class {
|
|
|
559
574
|
async absorb(p, root) {
|
|
560
575
|
const projectEnvs = await environmentStore.getByProjectId(p.id).catch(() => []);
|
|
561
576
|
const defaultEnv = projectEnvs.find((e) => e.default);
|
|
562
|
-
this.projects.set(p
|
|
577
|
+
this.projects.set(projectKey(p), {
|
|
563
578
|
...p,
|
|
564
579
|
environmentIds: p.environmentIds ?? [],
|
|
565
580
|
groupOrder: p.groupOrder ?? [],
|
|
566
581
|
// Prefer the env marked default, fall back to first env
|
|
567
582
|
activeEnvironmentId: defaultEnv?.id ?? p.environmentIds?.[0]
|
|
568
583
|
});
|
|
569
|
-
setProjectVault(p
|
|
570
|
-
setProjectServices(p
|
|
584
|
+
setProjectVault(p, root);
|
|
585
|
+
setProjectServices(p, p.serviceIds ?? []);
|
|
571
586
|
}
|
|
572
587
|
/**
|
|
573
588
|
* Read every vault: the launch directory, then each organization's projects.
|
|
@@ -633,17 +648,40 @@ var ProjectStore = class {
|
|
|
633
648
|
for (const { project, root } of cloud) {
|
|
634
649
|
await mkdir2(root, { recursive: true });
|
|
635
650
|
await this.writeAtomic(this.getFilePath(root), JSON.stringify(toDiskForm(project), null, 2));
|
|
636
|
-
setProjectVault(project
|
|
651
|
+
setProjectVault(project, root);
|
|
637
652
|
}
|
|
638
653
|
this.events.emit("saved");
|
|
639
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* The project a caller means, from a couple or from a bare id.
|
|
657
|
+
*
|
|
658
|
+
* A ref carrying its `orgId` — even an explicit `null` for a local project —
|
|
659
|
+
* names exactly one project and is looked up directly. A bare id is what a
|
|
660
|
+
* contained entity (an environment, a conversation) still carries, and it is
|
|
661
|
+
* resolved only while it answers for a single project: electing one of two
|
|
662
|
+
* would write into the wrong organization's vault.
|
|
663
|
+
*/
|
|
664
|
+
resolveKey(target) {
|
|
665
|
+
if (typeof target !== "string" && target.orgId !== void 0) return projectKey(target);
|
|
666
|
+
const id = typeof target === "string" ? target : target.id;
|
|
667
|
+
const matches = Array.from(this.projects.values()).filter((p) => p.id === id);
|
|
668
|
+
if (matches.length > 1) {
|
|
669
|
+
throw new AmbiguousProjectError(id, matches.map((p) => p.orgId ?? null));
|
|
670
|
+
}
|
|
671
|
+
return matches[0] ? projectKey(matches[0]) : null;
|
|
672
|
+
}
|
|
673
|
+
find(target) {
|
|
674
|
+
const key = this.resolveKey(target);
|
|
675
|
+
const project = key ? this.projects.get(key) : void 0;
|
|
676
|
+
return key && project ? { key, project } : null;
|
|
677
|
+
}
|
|
640
678
|
async list() {
|
|
641
679
|
await this.load();
|
|
642
680
|
return Array.from(this.projects.values());
|
|
643
681
|
}
|
|
644
|
-
async get(
|
|
682
|
+
async get(target) {
|
|
645
683
|
await this.load();
|
|
646
|
-
return this.
|
|
684
|
+
return this.find(target)?.project ?? null;
|
|
647
685
|
}
|
|
648
686
|
async create(input) {
|
|
649
687
|
await this.load();
|
|
@@ -663,16 +701,22 @@ var ProjectStore = class {
|
|
|
663
701
|
activeEnvironmentId: input.activeEnvironmentId,
|
|
664
702
|
groupOrder: input.groupOrder ?? []
|
|
665
703
|
};
|
|
666
|
-
this.projects.set(project
|
|
667
|
-
setProjectVault(project
|
|
668
|
-
setProjectServices(project
|
|
704
|
+
this.projects.set(projectKey(project), project);
|
|
705
|
+
setProjectVault(project, vaultFor(project).root);
|
|
706
|
+
setProjectServices(project, project.serviceIds);
|
|
669
707
|
await this.save();
|
|
670
708
|
return project;
|
|
671
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* `input.orgId` names which project is being updated, it does not move it:
|
|
712
|
+
* a project is born local or in an organization and stays there, so treating
|
|
713
|
+
* it as a new value would silently relocate a vault instead of editing it.
|
|
714
|
+
*/
|
|
672
715
|
async update(input) {
|
|
673
716
|
await this.load();
|
|
674
|
-
const
|
|
675
|
-
if (!
|
|
717
|
+
const found = this.find(input);
|
|
718
|
+
if (!found) return null;
|
|
719
|
+
const { key, project: existing } = found;
|
|
676
720
|
const updated = {
|
|
677
721
|
...existing,
|
|
678
722
|
...input.name !== void 0 && { name: input.name },
|
|
@@ -680,20 +724,20 @@ var ProjectStore = class {
|
|
|
680
724
|
...input.serviceIds !== void 0 && { serviceIds: input.serviceIds },
|
|
681
725
|
...input.environmentIds !== void 0 && { environmentIds: input.environmentIds },
|
|
682
726
|
...input.activeEnvironmentId !== void 0 && { activeEnvironmentId: input.activeEnvironmentId },
|
|
683
|
-
...input.orgId !== void 0 && { orgId: input.orgId },
|
|
684
727
|
...input.groupOrder !== void 0 && { groupOrder: input.groupOrder }
|
|
685
728
|
};
|
|
686
|
-
this.projects.set(
|
|
687
|
-
setProjectVault(updated
|
|
688
|
-
setProjectServices(updated
|
|
729
|
+
this.projects.set(key, updated);
|
|
730
|
+
setProjectVault(updated, vaultFor(updated).root);
|
|
731
|
+
setProjectServices(updated, updated.serviceIds);
|
|
689
732
|
await this.save();
|
|
690
733
|
return updated;
|
|
691
734
|
}
|
|
692
|
-
async delete(
|
|
735
|
+
async delete(target) {
|
|
693
736
|
await this.load();
|
|
694
|
-
const
|
|
695
|
-
if (!
|
|
696
|
-
|
|
737
|
+
const found = this.find(target);
|
|
738
|
+
if (!found) return false;
|
|
739
|
+
const { key, project } = found;
|
|
740
|
+
this.projects.delete(key);
|
|
697
741
|
await this.save();
|
|
698
742
|
await this.discardVault(project);
|
|
699
743
|
return true;
|
|
@@ -707,7 +751,7 @@ var ProjectStore = class {
|
|
|
707
751
|
* are only removed from the list.
|
|
708
752
|
*/
|
|
709
753
|
async discardVault(project) {
|
|
710
|
-
forgetProject(project
|
|
754
|
+
forgetProject(project);
|
|
711
755
|
if (!project.orgId) return;
|
|
712
756
|
await rm(vaultFor(project).root, { recursive: true, force: true }).catch(() => {
|
|
713
757
|
});
|
|
@@ -720,14 +764,15 @@ var ProjectStore = class {
|
|
|
720
764
|
* back on the next save — and, its project being unknown by then, into the
|
|
721
765
|
* local vault.
|
|
722
766
|
*/
|
|
723
|
-
async forgetLocally(
|
|
767
|
+
async forgetLocally(target) {
|
|
724
768
|
await this.load();
|
|
725
|
-
const
|
|
726
|
-
if (!
|
|
727
|
-
|
|
728
|
-
|
|
769
|
+
const found = this.find(target);
|
|
770
|
+
if (!found) return;
|
|
771
|
+
const { key, project } = found;
|
|
772
|
+
this.projects.delete(key);
|
|
773
|
+
forgetProject(project);
|
|
729
774
|
await serviceStore.forgetLocally(project.serviceIds ?? []);
|
|
730
|
-
await environmentStore.forgetByProject(
|
|
775
|
+
await environmentStore.forgetByProject(project.id);
|
|
731
776
|
}
|
|
732
777
|
/** All projects linked to a given organization (for the sync bridge). */
|
|
733
778
|
async listByOrg(orgId) {
|
|
@@ -756,92 +801,99 @@ var ProjectStore = class {
|
|
|
756
801
|
return null;
|
|
757
802
|
}
|
|
758
803
|
const incoming = toDiskForm(parsed.data);
|
|
759
|
-
const
|
|
804
|
+
const key = projectKey(parsed.data);
|
|
805
|
+
const existing = this.projects.get(key);
|
|
760
806
|
if (existing) {
|
|
761
807
|
if (JSON.stringify(toDiskForm(existing)) === JSON.stringify(incoming)) {
|
|
762
808
|
return { project: existing, created: false };
|
|
763
809
|
}
|
|
764
810
|
const updated = { ...incoming, activeEnvironmentId: existing.activeEnvironmentId };
|
|
765
|
-
this.projects.set(
|
|
766
|
-
setProjectVault(
|
|
767
|
-
setProjectServices(
|
|
811
|
+
this.projects.set(key, updated);
|
|
812
|
+
setProjectVault(updated, vaultFor(updated).root);
|
|
813
|
+
setProjectServices(updated, updated.serviceIds);
|
|
768
814
|
await this.save();
|
|
769
815
|
return { project: updated, created: false };
|
|
770
816
|
}
|
|
771
817
|
const project = { ...incoming, activeEnvironmentId: void 0 };
|
|
772
|
-
this.projects.set(
|
|
773
|
-
setProjectVault(
|
|
774
|
-
setProjectServices(
|
|
818
|
+
this.projects.set(key, project);
|
|
819
|
+
setProjectVault(project, vaultFor(project).root);
|
|
820
|
+
setProjectServices(project, project.serviceIds);
|
|
775
821
|
await this.save();
|
|
776
822
|
return { project, created: true };
|
|
777
823
|
}
|
|
778
824
|
/** Delete a project that was removed on another peer. */
|
|
779
|
-
async applyRemoteDelete(
|
|
825
|
+
async applyRemoteDelete(target) {
|
|
780
826
|
await this.load();
|
|
781
|
-
const
|
|
782
|
-
if (!
|
|
783
|
-
|
|
827
|
+
const found = this.find(target);
|
|
828
|
+
if (!found) return false;
|
|
829
|
+
const { key, project } = found;
|
|
830
|
+
this.projects.delete(key);
|
|
784
831
|
await this.save();
|
|
785
832
|
await this.discardVault(project);
|
|
786
833
|
return true;
|
|
787
834
|
}
|
|
788
|
-
async addService(
|
|
835
|
+
async addService(target, serviceId) {
|
|
789
836
|
await this.load();
|
|
790
|
-
const
|
|
791
|
-
if (!
|
|
837
|
+
const found = this.find(target);
|
|
838
|
+
if (!found) return null;
|
|
839
|
+
const { key, project } = found;
|
|
792
840
|
if (project.serviceIds.includes(serviceId)) return project;
|
|
793
841
|
const updated = { ...project, serviceIds: [...project.serviceIds, serviceId] };
|
|
794
|
-
this.projects.set(
|
|
795
|
-
setProjectServices(
|
|
842
|
+
this.projects.set(key, updated);
|
|
843
|
+
setProjectServices(updated, updated.serviceIds);
|
|
796
844
|
await this.save();
|
|
797
845
|
return updated;
|
|
798
846
|
}
|
|
799
|
-
async removeService(
|
|
847
|
+
async removeService(target, serviceId) {
|
|
800
848
|
await this.load();
|
|
801
|
-
const
|
|
802
|
-
if (!
|
|
849
|
+
const found = this.find(target);
|
|
850
|
+
if (!found) return null;
|
|
851
|
+
const { key, project } = found;
|
|
803
852
|
const updated = { ...project, serviceIds: project.serviceIds.filter((id) => id !== serviceId) };
|
|
804
|
-
this.projects.set(
|
|
805
|
-
setProjectServices(
|
|
853
|
+
this.projects.set(key, updated);
|
|
854
|
+
setProjectServices(updated, updated.serviceIds);
|
|
806
855
|
await this.save();
|
|
807
856
|
return updated;
|
|
808
857
|
}
|
|
809
|
-
async addEnvironment(
|
|
858
|
+
async addEnvironment(target, environmentId) {
|
|
810
859
|
await this.load();
|
|
811
|
-
const
|
|
812
|
-
if (!
|
|
860
|
+
const found = this.find(target);
|
|
861
|
+
if (!found) return null;
|
|
862
|
+
const { key, project } = found;
|
|
813
863
|
if (project.environmentIds?.includes(environmentId)) return project;
|
|
814
864
|
const updated = {
|
|
815
865
|
...project,
|
|
816
866
|
environmentIds: [...project.environmentIds ?? [], environmentId]
|
|
817
867
|
};
|
|
818
|
-
this.projects.set(
|
|
868
|
+
this.projects.set(key, updated);
|
|
819
869
|
await this.save();
|
|
820
870
|
return updated;
|
|
821
871
|
}
|
|
822
|
-
async removeEnvironment(
|
|
872
|
+
async removeEnvironment(target, environmentId) {
|
|
823
873
|
await this.load();
|
|
824
|
-
const
|
|
825
|
-
if (!
|
|
874
|
+
const found = this.find(target);
|
|
875
|
+
if (!found) return null;
|
|
876
|
+
const { key, project } = found;
|
|
826
877
|
const updated = {
|
|
827
878
|
...project,
|
|
828
879
|
environmentIds: (project.environmentIds ?? []).filter((id) => id !== environmentId),
|
|
829
880
|
// Clear activeEnvironmentId if it was the removed environment
|
|
830
881
|
...project.activeEnvironmentId === environmentId && { activeEnvironmentId: void 0 }
|
|
831
882
|
};
|
|
832
|
-
this.projects.set(
|
|
883
|
+
this.projects.set(key, updated);
|
|
833
884
|
await this.save();
|
|
834
885
|
return updated;
|
|
835
886
|
}
|
|
836
|
-
async setActiveEnvironment(
|
|
887
|
+
async setActiveEnvironment(target, environmentId) {
|
|
837
888
|
await this.load();
|
|
838
|
-
const
|
|
839
|
-
if (!
|
|
889
|
+
const found = this.find(target);
|
|
890
|
+
if (!found) return null;
|
|
891
|
+
const { key, project } = found;
|
|
840
892
|
const updated = {
|
|
841
893
|
...project,
|
|
842
894
|
activeEnvironmentId: environmentId
|
|
843
895
|
};
|
|
844
|
-
this.projects.set(
|
|
896
|
+
this.projects.set(key, updated);
|
|
845
897
|
return updated;
|
|
846
898
|
}
|
|
847
899
|
};
|
|
@@ -873,8 +925,8 @@ var EnvironmentOverridesStore = class {
|
|
|
873
925
|
* may simply not be loaded yet.
|
|
874
926
|
*/
|
|
875
927
|
async rootFor(variableId) {
|
|
876
|
-
const
|
|
877
|
-
if (
|
|
928
|
+
const root = await environmentStore.rootOfVariable(variableId);
|
|
929
|
+
if (root) return root;
|
|
878
930
|
return this.roots.get(variableId) ?? dirname2(await keyedFilePath(FILENAME2));
|
|
879
931
|
}
|
|
880
932
|
/** Where earlier versions kept them: read once, rewritten to their vaults. */
|
|
@@ -891,6 +943,7 @@ var EnvironmentOverridesStore = class {
|
|
|
891
943
|
if (this.loaded) return;
|
|
892
944
|
if (!getEncryptionKey()) return;
|
|
893
945
|
this.loaded = true;
|
|
946
|
+
await ensureProjectsLoaded();
|
|
894
947
|
const roots = await allVaultRoots();
|
|
895
948
|
const sources = [
|
|
896
949
|
this.getLegacyFilePath(),
|
|
@@ -1456,13 +1509,98 @@ var AuditService = class {
|
|
|
1456
1509
|
};
|
|
1457
1510
|
var auditService = new AuditService();
|
|
1458
1511
|
|
|
1512
|
+
// ../server/src/services/project-path-store.ts
|
|
1513
|
+
import { readFile as readFile5, writeFile as writeFile5, rename as rename5, mkdir as mkdir5, chmod as chmod4, stat as stat2 } from "fs/promises";
|
|
1514
|
+
import { isAbsolute, join as join4, resolve } from "path";
|
|
1515
|
+
var FILENAME3 = "project-paths.json";
|
|
1516
|
+
var ProjectPathStore = class {
|
|
1517
|
+
paths = null;
|
|
1518
|
+
async load() {
|
|
1519
|
+
if (this.paths) return this.paths;
|
|
1520
|
+
try {
|
|
1521
|
+
const raw = await readFile5(machineFilePath(FILENAME3), "utf-8");
|
|
1522
|
+
const parsed = JSON.parse(raw);
|
|
1523
|
+
this.paths = isPathMap(parsed) ? parsed : {};
|
|
1524
|
+
} catch {
|
|
1525
|
+
this.paths = {};
|
|
1526
|
+
}
|
|
1527
|
+
return this.paths;
|
|
1528
|
+
}
|
|
1529
|
+
async persist() {
|
|
1530
|
+
await mkdir5(machineRoot(), { recursive: true });
|
|
1531
|
+
const filePath = machineFilePath(FILENAME3);
|
|
1532
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1533
|
+
await writeFile5(tmpPath, JSON.stringify(this.paths ?? {}, null, 2), "utf-8");
|
|
1534
|
+
await rename5(tmpPath, filePath);
|
|
1535
|
+
await chmod4(filePath, 384);
|
|
1536
|
+
}
|
|
1537
|
+
/** The directory chosen for this project, or null while it has none. */
|
|
1538
|
+
async get(ref) {
|
|
1539
|
+
const paths = await this.load();
|
|
1540
|
+
return paths[projectKey(ref)] ?? paths[ref.id] ?? null;
|
|
1541
|
+
}
|
|
1542
|
+
async all() {
|
|
1543
|
+
return { ...await this.load() };
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* The project already placed in this directory, if any other one is.
|
|
1547
|
+
*
|
|
1548
|
+
* Answers with the key as stored — the couple, or the bare id for an entry an
|
|
1549
|
+
* older version wrote — which is enough to say "taken" and to name it.
|
|
1550
|
+
*/
|
|
1551
|
+
async projectAt(path, exclude) {
|
|
1552
|
+
const target = resolve(path);
|
|
1553
|
+
const excluded = exclude ? [projectKey(exclude), exclude.id] : [];
|
|
1554
|
+
for (const [key, dir] of Object.entries(await this.load())) {
|
|
1555
|
+
if (!excluded.includes(key) && resolve(dir) === target) return key;
|
|
1556
|
+
}
|
|
1557
|
+
return null;
|
|
1558
|
+
}
|
|
1559
|
+
async set(ref, path) {
|
|
1560
|
+
if (!isAbsolute(path)) throw new Error("Le chemin du projet doit \xEAtre absolu");
|
|
1561
|
+
const paths = await this.load();
|
|
1562
|
+
delete paths[ref.id];
|
|
1563
|
+
paths[projectKey(ref)] = resolve(path);
|
|
1564
|
+
await this.persist();
|
|
1565
|
+
}
|
|
1566
|
+
async clear(ref) {
|
|
1567
|
+
const paths = await this.load();
|
|
1568
|
+
const keys = [projectKey(ref), ref.id].filter((k) => k in paths);
|
|
1569
|
+
if (keys.length === 0) return;
|
|
1570
|
+
for (const key of keys) delete paths[key];
|
|
1571
|
+
await this.persist();
|
|
1572
|
+
}
|
|
1573
|
+
/** Test seam: forget what was read from disk. */
|
|
1574
|
+
reset() {
|
|
1575
|
+
this.paths = null;
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
function isPathMap(value) {
|
|
1579
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
|
|
1580
|
+
}
|
|
1581
|
+
async function holdsRuneyaProject(path) {
|
|
1582
|
+
for (const candidate of ["project.json", "projects.json"]) {
|
|
1583
|
+
try {
|
|
1584
|
+
await stat2(join4(path, ".runeya", candidate));
|
|
1585
|
+
return true;
|
|
1586
|
+
} catch {
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return false;
|
|
1590
|
+
}
|
|
1591
|
+
var projectPathStore = new ProjectPathStore();
|
|
1592
|
+
|
|
1459
1593
|
// ../server/src/trpc/routers/project.ts
|
|
1460
1594
|
var projectRouter = router({
|
|
1461
1595
|
list: protectedProcedure.query(async () => {
|
|
1462
1596
|
return projectStore.list();
|
|
1463
1597
|
}),
|
|
1464
|
-
|
|
1465
|
-
|
|
1598
|
+
/**
|
|
1599
|
+
* `orgId` names which project — the same id can exist in several
|
|
1600
|
+
* organizations. Left out, the id must answer for exactly one project.
|
|
1601
|
+
*/
|
|
1602
|
+
get: protectedProcedure.input(z2.object({ id: z2.string().max(128), orgId: z2.string().max(128).nullable().optional() })).query(async ({ input }) => {
|
|
1603
|
+
const project = await projectStore.get(input);
|
|
1466
1604
|
if (!project) {
|
|
1467
1605
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
1468
1606
|
}
|
|
@@ -1474,8 +1612,8 @@ var projectRouter = router({
|
|
|
1474
1612
|
* Environments are intentionally excluded — they carry secret values that
|
|
1475
1613
|
* stay local.
|
|
1476
1614
|
*/
|
|
1477
|
-
export: protectedProcedure.input(z2.object({ id: z2.string().max(128) })).query(async ({ input }) => {
|
|
1478
|
-
const project = await projectStore.get(input
|
|
1615
|
+
export: protectedProcedure.input(z2.object({ id: z2.string().max(128), orgId: z2.string().max(128).nullable().optional() })).query(async ({ input }) => {
|
|
1616
|
+
const project = await projectStore.get(input);
|
|
1479
1617
|
if (!project) {
|
|
1480
1618
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
1481
1619
|
}
|
|
@@ -1510,9 +1648,10 @@ var projectRouter = router({
|
|
|
1510
1648
|
description: "Default local environment (immutable)",
|
|
1511
1649
|
parentIds: [],
|
|
1512
1650
|
immutable: true
|
|
1513
|
-
});
|
|
1651
|
+
}, project.orgId);
|
|
1514
1652
|
const result = await projectStore.update({
|
|
1515
1653
|
id: project.id,
|
|
1654
|
+
orgId: project.orgId,
|
|
1516
1655
|
environmentIds: [localEnv.id],
|
|
1517
1656
|
activeEnvironmentId: localEnv.id
|
|
1518
1657
|
});
|
|
@@ -1536,22 +1675,27 @@ var projectRouter = router({
|
|
|
1536
1675
|
await auditService.log({ userId: "local", action: "update", entityType: "project", entityId: input.id });
|
|
1537
1676
|
return project;
|
|
1538
1677
|
}),
|
|
1539
|
-
delete: protectedProcedure.input(z2.object({ id: z2.string().max(128) })).mutation(async ({ input }) => {
|
|
1540
|
-
const deleted = await projectStore.delete(input
|
|
1678
|
+
delete: protectedProcedure.input(z2.object({ id: z2.string().max(128), orgId: z2.string().max(128).nullable().optional() })).mutation(async ({ input }) => {
|
|
1679
|
+
const deleted = await projectStore.delete(input);
|
|
1541
1680
|
if (!deleted) {
|
|
1542
1681
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.id} not found` });
|
|
1543
1682
|
}
|
|
1544
1683
|
await auditService.log({ userId: "local", action: "delete", entityType: "project", entityId: input.id });
|
|
1545
1684
|
return { success: true };
|
|
1546
1685
|
}),
|
|
1547
|
-
addService: protectedProcedure.input(z2.object({
|
|
1548
|
-
|
|
1686
|
+
addService: protectedProcedure.input(z2.object({
|
|
1687
|
+
projectId: z2.string().max(128),
|
|
1688
|
+
orgId: z2.string().max(128).nullable().optional(),
|
|
1689
|
+
serviceId: z2.string().max(128)
|
|
1690
|
+
})).mutation(async ({ input }) => {
|
|
1691
|
+
const target = { id: input.projectId, orgId: input.orgId };
|
|
1692
|
+
const project = await projectStore.addService(target, input.serviceId);
|
|
1549
1693
|
if (!project) {
|
|
1550
1694
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
1551
1695
|
}
|
|
1552
1696
|
const service = await serviceStore.get(input.serviceId);
|
|
1553
1697
|
if (service) {
|
|
1554
|
-
const globalEnvs = await environmentStore.listByScope(input.projectId, "global");
|
|
1698
|
+
const globalEnvs = await environmentStore.listByScope(input.projectId, "global", input.orgId);
|
|
1555
1699
|
for (const globalEnv of globalEnvs) {
|
|
1556
1700
|
const svcEnv = await environmentStore.create({
|
|
1557
1701
|
projectId: input.projectId,
|
|
@@ -1561,8 +1705,8 @@ var projectRouter = router({
|
|
|
1561
1705
|
activateOn: globalEnv.id,
|
|
1562
1706
|
parentIds: [globalEnv.id],
|
|
1563
1707
|
variables: {}
|
|
1564
|
-
});
|
|
1565
|
-
await projectStore.addEnvironment(
|
|
1708
|
+
}, input.orgId);
|
|
1709
|
+
await projectStore.addEnvironment(target, svcEnv.id);
|
|
1566
1710
|
}
|
|
1567
1711
|
}
|
|
1568
1712
|
await auditService.log({
|
|
@@ -1574,8 +1718,15 @@ var projectRouter = router({
|
|
|
1574
1718
|
});
|
|
1575
1719
|
return project;
|
|
1576
1720
|
}),
|
|
1577
|
-
removeService: protectedProcedure.input(z2.object({
|
|
1578
|
-
|
|
1721
|
+
removeService: protectedProcedure.input(z2.object({
|
|
1722
|
+
projectId: z2.string().max(128),
|
|
1723
|
+
orgId: z2.string().max(128).nullable().optional(),
|
|
1724
|
+
serviceId: z2.string().max(128)
|
|
1725
|
+
})).mutation(async ({ input }) => {
|
|
1726
|
+
const project = await projectStore.removeService(
|
|
1727
|
+
{ id: input.projectId, orgId: input.orgId },
|
|
1728
|
+
input.serviceId
|
|
1729
|
+
);
|
|
1579
1730
|
if (!project) {
|
|
1580
1731
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
1581
1732
|
}
|
|
@@ -1588,8 +1739,12 @@ var projectRouter = router({
|
|
|
1588
1739
|
});
|
|
1589
1740
|
return project;
|
|
1590
1741
|
}),
|
|
1591
|
-
setActiveEnvironment: protectedProcedure.input(z2.object({
|
|
1592
|
-
|
|
1742
|
+
setActiveEnvironment: protectedProcedure.input(z2.object({
|
|
1743
|
+
projectId: z2.string().max(128),
|
|
1744
|
+
orgId: z2.string().max(128).nullable().optional(),
|
|
1745
|
+
environmentId: z2.string().max(128)
|
|
1746
|
+
})).mutation(async ({ input }) => {
|
|
1747
|
+
const project = await projectStore.get({ id: input.projectId, orgId: input.orgId });
|
|
1593
1748
|
if (!project) {
|
|
1594
1749
|
throw new TRPCError2({ code: "NOT_FOUND", message: `Project ${input.projectId} not found` });
|
|
1595
1750
|
}
|
|
@@ -1618,7 +1773,10 @@ var projectRouter = router({
|
|
|
1618
1773
|
} catch {
|
|
1619
1774
|
}
|
|
1620
1775
|
}
|
|
1621
|
-
const updated = await projectStore.setActiveEnvironment(
|
|
1776
|
+
const updated = await projectStore.setActiveEnvironment(
|
|
1777
|
+
{ id: input.projectId, orgId: input.orgId },
|
|
1778
|
+
input.environmentId
|
|
1779
|
+
);
|
|
1622
1780
|
if (!updated) {
|
|
1623
1781
|
throw new TRPCError2({ code: "INTERNAL_SERVER_ERROR", message: "Failed to set active environment" });
|
|
1624
1782
|
}
|
|
@@ -1653,6 +1811,53 @@ var projectRouter = router({
|
|
|
1653
1811
|
}
|
|
1654
1812
|
}
|
|
1655
1813
|
return updated;
|
|
1814
|
+
}),
|
|
1815
|
+
/**
|
|
1816
|
+
* Where this project lives on this machine, for every project at once.
|
|
1817
|
+
*
|
|
1818
|
+
* Machine-local by design: a directory of mine means nothing on a teammate's
|
|
1819
|
+
* machine, and the cloud sync pushes projects wholesale — a path stored on the
|
|
1820
|
+
* project itself would travel with it.
|
|
1821
|
+
*/
|
|
1822
|
+
localPaths: protectedProcedure.query(async () => {
|
|
1823
|
+
return projectPathStore.all();
|
|
1824
|
+
}),
|
|
1825
|
+
getLocalPath: protectedProcedure.input(z2.object({
|
|
1826
|
+
id: z2.string().min(1).max(128),
|
|
1827
|
+
orgId: z2.string().max(128).nullable().optional()
|
|
1828
|
+
})).query(async ({ input }) => {
|
|
1829
|
+
return { path: await projectPathStore.get(input) };
|
|
1830
|
+
}),
|
|
1831
|
+
/**
|
|
1832
|
+
* Place a project in a directory of this machine.
|
|
1833
|
+
*
|
|
1834
|
+
* Refuses a directory that already holds a Runeya project, or one another
|
|
1835
|
+
* project was already placed in: two projects sharing a directory is what
|
|
1836
|
+
* makes services run against the wrong code.
|
|
1837
|
+
*/
|
|
1838
|
+
setLocalPath: protectedProcedure.input(z2.object({
|
|
1839
|
+
id: z2.string().min(1).max(128),
|
|
1840
|
+
orgId: z2.string().max(128).nullable().optional(),
|
|
1841
|
+
path: z2.string().min(1).max(4096)
|
|
1842
|
+
})).mutation(async ({ input }) => {
|
|
1843
|
+
const project = await projectStore.get(input);
|
|
1844
|
+
if (!project) throw new TRPCError2({ code: "NOT_FOUND", message: "Projet introuvable" });
|
|
1845
|
+
if (await holdsRuneyaProject(input.path)) {
|
|
1846
|
+
throw new TRPCError2({
|
|
1847
|
+
code: "CONFLICT",
|
|
1848
|
+
message: "Ce r\xE9pertoire contient d\xE9j\xE0 un projet Runeya"
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
const taken = await projectPathStore.projectAt(input.path, project);
|
|
1852
|
+
if (taken) {
|
|
1853
|
+
throw new TRPCError2({
|
|
1854
|
+
code: "CONFLICT",
|
|
1855
|
+
message: "Un autre projet est d\xE9j\xE0 rattach\xE9 \xE0 ce r\xE9pertoire"
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
await projectPathStore.set(project, input.path);
|
|
1859
|
+
await auditService.log("local", "update", "project", input.id, { localPath: input.path });
|
|
1860
|
+
return { path: input.path };
|
|
1656
1861
|
})
|
|
1657
1862
|
});
|
|
1658
1863
|
|
|
@@ -1661,7 +1866,7 @@ import { z as z3 } from "zod";
|
|
|
1661
1866
|
import { TRPCError as TRPCError3 } from "@trpc/server";
|
|
1662
1867
|
|
|
1663
1868
|
// ../server/src/services/settings-manager.ts
|
|
1664
|
-
import { readFile as
|
|
1869
|
+
import { readFile as readFile6, writeFile as writeFile6, rename as rename6, mkdir as mkdir6, chmod as chmod5 } from "fs/promises";
|
|
1665
1870
|
var SETTINGS_FILE = "settings.json";
|
|
1666
1871
|
var SETTINGS_PRIVATE_FILE = "settings.private.json";
|
|
1667
1872
|
var SCHEMA_VERSION_FILE = "schema-version.json";
|
|
@@ -1671,21 +1876,21 @@ var SettingsManager = class {
|
|
|
1671
1876
|
settingsPrivate = null;
|
|
1672
1877
|
schemaVersion = null;
|
|
1673
1878
|
async ensureDir() {
|
|
1674
|
-
await
|
|
1879
|
+
await mkdir6(machineRoot(), { recursive: true });
|
|
1675
1880
|
}
|
|
1676
1881
|
async atomicWrite(filename, data) {
|
|
1677
1882
|
await this.ensureDir();
|
|
1678
1883
|
const filePath = machineFilePath(filename);
|
|
1679
1884
|
const tmpPath = filePath + ".tmp";
|
|
1680
|
-
await
|
|
1681
|
-
await
|
|
1682
|
-
await
|
|
1885
|
+
await writeFile6(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
1886
|
+
await rename6(tmpPath, filePath);
|
|
1887
|
+
await chmod5(filePath, 384);
|
|
1683
1888
|
}
|
|
1684
1889
|
/** Read the machine file, falling back to the launch directory of old. */
|
|
1685
1890
|
async readJson(filename) {
|
|
1686
1891
|
for (const path of [machineFilePath(filename), legacyMachineFilePath(filename)]) {
|
|
1687
1892
|
try {
|
|
1688
|
-
return JSON.parse(await
|
|
1893
|
+
return JSON.parse(await readFile6(path, "utf-8"));
|
|
1689
1894
|
} catch {
|
|
1690
1895
|
}
|
|
1691
1896
|
}
|
|
@@ -1748,6 +1953,62 @@ var SettingsManager = class {
|
|
|
1748
1953
|
};
|
|
1749
1954
|
var settingsManager = new SettingsManager();
|
|
1750
1955
|
|
|
1956
|
+
// ../server/src/trpc/routers/service.ts
|
|
1957
|
+
import { isAbsolute as isAbsolute3 } from "path";
|
|
1958
|
+
|
|
1959
|
+
// ../server/src/services/workspace-cwd.ts
|
|
1960
|
+
import { isAbsolute as isAbsolute2, join as join5, dirname as dirname3 } from "path";
|
|
1961
|
+
async function projectRoot(ref) {
|
|
1962
|
+
const project = await projectStore.get(ref);
|
|
1963
|
+
if (!project?.orgId) return null;
|
|
1964
|
+
return projectPathStore.get(project);
|
|
1965
|
+
}
|
|
1966
|
+
async function resolveWorkspaceCwd(target) {
|
|
1967
|
+
const launchDir = localVaultRootParent();
|
|
1968
|
+
if (target.serviceId) {
|
|
1969
|
+
const service = await serviceStore.get(target.serviceId);
|
|
1970
|
+
if (service) {
|
|
1971
|
+
const raw = (target.override ?? service.cwd ?? "").trim();
|
|
1972
|
+
const projects = await projectStore.list();
|
|
1973
|
+
const owner = projects.find((p) => p.serviceIds.includes(service.id));
|
|
1974
|
+
const root = owner ? await projectRoot(owner) : null;
|
|
1975
|
+
return root ? anchor(root, raw) : raw || ".";
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
if (target.projectId) {
|
|
1979
|
+
const root = await projectRoot({ id: target.projectId, orgId: target.orgId });
|
|
1980
|
+
const raw = (target.override ?? "").trim();
|
|
1981
|
+
return root ? anchor(root, raw) : raw || launchDir;
|
|
1982
|
+
}
|
|
1983
|
+
return anchor(launchDir, (target.override ?? "").trim());
|
|
1984
|
+
}
|
|
1985
|
+
function anchor(base, raw) {
|
|
1986
|
+
if (!raw || raw === ".") return base;
|
|
1987
|
+
if (raw.startsWith("~") || raw.startsWith("$") || isAbsolute2(raw)) return raw;
|
|
1988
|
+
return join5(base, raw);
|
|
1989
|
+
}
|
|
1990
|
+
function localVaultRootParent() {
|
|
1991
|
+
return dirname3(localVaultRoot());
|
|
1992
|
+
}
|
|
1993
|
+
async function resolveAgentCwd(target) {
|
|
1994
|
+
if (target.serviceId) {
|
|
1995
|
+
const service = await serviceStore.get(target.serviceId);
|
|
1996
|
+
if (service) {
|
|
1997
|
+
const projects = await projectStore.list();
|
|
1998
|
+
const owner = projects.find((p) => p.serviceIds.includes(service.id));
|
|
1999
|
+
const root = owner ? await projectRoot(owner) : null;
|
|
2000
|
+
if (!root) return void 0;
|
|
2001
|
+
return anchor(root, (target.override ?? service.cwd ?? "").trim());
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
if (target.projectId) {
|
|
2005
|
+
const root = await projectRoot({ id: target.projectId, orgId: target.orgId });
|
|
2006
|
+
if (!root) return void 0;
|
|
2007
|
+
return anchor(root, (target.override ?? "").trim());
|
|
2008
|
+
}
|
|
2009
|
+
return void 0;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1751
2012
|
// ../server/src/trpc/routers/service.ts
|
|
1752
2013
|
async function resolveEffectiveAgentId(service) {
|
|
1753
2014
|
if (service.agentId) return service.agentId;
|
|
@@ -1761,6 +2022,10 @@ async function resolveServiceVariables(service) {
|
|
|
1761
2022
|
const projects = await projectStore.list();
|
|
1762
2023
|
const project = projects.find((p) => p.serviceIds.includes(service.id));
|
|
1763
2024
|
let resolvedService = service;
|
|
2025
|
+
const anchored = await resolveWorkspaceCwd({ serviceId: service.id });
|
|
2026
|
+
if (anchored !== (service.cwd ?? "").trim() && isAbsolute3(anchored)) {
|
|
2027
|
+
resolvedService = { ...resolvedService, resolvedCwd: anchored };
|
|
2028
|
+
}
|
|
1764
2029
|
try {
|
|
1765
2030
|
if (service.parserIds && service.parserIds.length > 0) {
|
|
1766
2031
|
const settings = await settingsManager.getSettings();
|
|
@@ -1972,17 +2237,19 @@ var serviceRouter = router({
|
|
|
1972
2237
|
throw new TRPCError3({ code: "BAD_REQUEST", message: "No agent configured for this service" });
|
|
1973
2238
|
}
|
|
1974
2239
|
const agentResult = await agentManager.agentQuery(effectiveAgentId, "git.checkCwd", {
|
|
1975
|
-
|
|
2240
|
+
// Anchored on the project, so the check answers about the directory the
|
|
2241
|
+
// service will actually run in.
|
|
2242
|
+
cwd: await resolveWorkspaceCwd({ serviceId: service.id }),
|
|
1976
2243
|
remote: service.git?.remote,
|
|
1977
2244
|
checkReachability: input.checkReachability
|
|
1978
2245
|
});
|
|
1979
|
-
const { exists, actualRemote, unreachable, resolvedCwd, unresolvedVars } = agentResult;
|
|
2246
|
+
const { exists: exists2, actualRemote, unreachable, resolvedCwd, unresolvedVars } = agentResult;
|
|
1980
2247
|
if (unresolvedVars.length > 0) {
|
|
1981
2248
|
return { exists: false, canClone: false, unresolvedVars, resolvedCwd, gitMismatch: null };
|
|
1982
2249
|
}
|
|
1983
|
-
const canClone = !!service.git?.remote && (!
|
|
2250
|
+
const canClone = !!service.git?.remote && (!exists2 || actualRemote === null);
|
|
1984
2251
|
let gitMismatch = null;
|
|
1985
|
-
if (
|
|
2252
|
+
if (exists2 && service.git?.remote && !canClone) {
|
|
1986
2253
|
if (actualRemote === null) {
|
|
1987
2254
|
gitMismatch = { actual: null, configured: service.git.remote };
|
|
1988
2255
|
} else if (actualRemote !== service.git.remote) {
|
|
@@ -1991,7 +2258,7 @@ var serviceRouter = router({
|
|
|
1991
2258
|
gitMismatch = { actual: actualRemote, configured: service.git.remote, unreachable: true };
|
|
1992
2259
|
}
|
|
1993
2260
|
}
|
|
1994
|
-
return { exists, canClone, unresolvedVars: [], resolvedCwd, gitMismatch };
|
|
2261
|
+
return { exists: exists2, canClone, unresolvedVars: [], resolvedCwd, gitMismatch };
|
|
1995
2262
|
}),
|
|
1996
2263
|
cloneRepository: protectedProcedure.input(z3.object({ id: z3.string().max(128) })).mutation(async ({ input }) => {
|
|
1997
2264
|
const service = await serviceStore.get(input.id);
|
|
@@ -2007,7 +2274,9 @@ var serviceRouter = router({
|
|
|
2007
2274
|
}
|
|
2008
2275
|
await agentManager.agentMutation(effectiveAgentId, "git.clone", {
|
|
2009
2276
|
remote: service.git.remote,
|
|
2010
|
-
|
|
2277
|
+
// Clones WRITE: landing this in the launch directory instead of the
|
|
2278
|
+
// project's is how a repository ends up in the wrong place.
|
|
2279
|
+
cwd: await resolveWorkspaceCwd({ serviceId: service.id })
|
|
2011
2280
|
}, 5 * 60 * 1e3);
|
|
2012
2281
|
return { success: true };
|
|
2013
2282
|
})
|
|
@@ -2141,13 +2410,13 @@ var agentRouter = router({
|
|
|
2141
2410
|
yield { agentId: agent.config.id, status: agent.status };
|
|
2142
2411
|
}
|
|
2143
2412
|
const queue = [];
|
|
2144
|
-
let
|
|
2413
|
+
let resolve7 = null;
|
|
2145
2414
|
const handler = (event) => {
|
|
2146
2415
|
if (queue.length >= SUBSCRIPTION_QUEUE_MAX) queue.shift();
|
|
2147
2416
|
queue.push(event);
|
|
2148
|
-
if (
|
|
2149
|
-
|
|
2150
|
-
|
|
2417
|
+
if (resolve7) {
|
|
2418
|
+
resolve7();
|
|
2419
|
+
resolve7 = null;
|
|
2151
2420
|
}
|
|
2152
2421
|
};
|
|
2153
2422
|
agentManager.on("status", handler);
|
|
@@ -2157,7 +2426,7 @@ var agentRouter = router({
|
|
|
2157
2426
|
yield queue.shift();
|
|
2158
2427
|
} else {
|
|
2159
2428
|
await new Promise((r) => {
|
|
2160
|
-
|
|
2429
|
+
resolve7 = r;
|
|
2161
2430
|
});
|
|
2162
2431
|
}
|
|
2163
2432
|
}
|
|
@@ -2173,14 +2442,14 @@ import { TRPCError as TRPCError5 } from "@trpc/server";
|
|
|
2173
2442
|
var SUBSCRIPTION_QUEUE_MAX2 = LOG_BUFFER_SIZE;
|
|
2174
2443
|
async function* forwardEvents(eventName, filter) {
|
|
2175
2444
|
const queue = [];
|
|
2176
|
-
let
|
|
2445
|
+
let resolve7 = null;
|
|
2177
2446
|
const handler = (data) => {
|
|
2178
2447
|
if (filter && !filter(data)) return;
|
|
2179
2448
|
if (queue.length >= SUBSCRIPTION_QUEUE_MAX2) queue.shift();
|
|
2180
2449
|
queue.push(data);
|
|
2181
|
-
if (
|
|
2182
|
-
|
|
2183
|
-
|
|
2450
|
+
if (resolve7) {
|
|
2451
|
+
resolve7();
|
|
2452
|
+
resolve7 = null;
|
|
2184
2453
|
}
|
|
2185
2454
|
};
|
|
2186
2455
|
agentManager.on(eventName, handler);
|
|
@@ -2190,7 +2459,7 @@ async function* forwardEvents(eventName, filter) {
|
|
|
2190
2459
|
yield queue.shift();
|
|
2191
2460
|
} else {
|
|
2192
2461
|
await new Promise((r) => {
|
|
2193
|
-
|
|
2462
|
+
resolve7 = r;
|
|
2194
2463
|
});
|
|
2195
2464
|
}
|
|
2196
2465
|
}
|
|
@@ -2200,7 +2469,7 @@ async function* forwardEvents(eventName, filter) {
|
|
|
2200
2469
|
}
|
|
2201
2470
|
async function* forwardLogBatches(agentId, processId) {
|
|
2202
2471
|
const queue = [];
|
|
2203
|
-
let
|
|
2472
|
+
let resolve7 = null;
|
|
2204
2473
|
const handler = (data) => {
|
|
2205
2474
|
if (data.agentId !== agentId) return;
|
|
2206
2475
|
const batch = data.batch;
|
|
@@ -2209,9 +2478,9 @@ async function* forwardLogBatches(agentId, processId) {
|
|
|
2209
2478
|
if (filtered.length === 0) return;
|
|
2210
2479
|
if (queue.length >= SUBSCRIPTION_QUEUE_MAX2) queue.shift();
|
|
2211
2480
|
queue.push(filtered);
|
|
2212
|
-
if (
|
|
2213
|
-
|
|
2214
|
-
|
|
2481
|
+
if (resolve7) {
|
|
2482
|
+
resolve7();
|
|
2483
|
+
resolve7 = null;
|
|
2215
2484
|
}
|
|
2216
2485
|
};
|
|
2217
2486
|
agentManager.on("process:log", handler);
|
|
@@ -2221,7 +2490,7 @@ async function* forwardLogBatches(agentId, processId) {
|
|
|
2221
2490
|
yield queue.shift();
|
|
2222
2491
|
} else {
|
|
2223
2492
|
await new Promise((r) => {
|
|
2224
|
-
|
|
2493
|
+
resolve7 = r;
|
|
2225
2494
|
});
|
|
2226
2495
|
}
|
|
2227
2496
|
}
|
|
@@ -2402,7 +2671,14 @@ var processRouter = router({
|
|
|
2402
2671
|
const result = await agentManager.agentMutation(
|
|
2403
2672
|
input.agentId,
|
|
2404
2673
|
"process.executeAdHocCommand",
|
|
2405
|
-
{
|
|
2674
|
+
{
|
|
2675
|
+
id: input.processId,
|
|
2676
|
+
command: input.command,
|
|
2677
|
+
// An explicit cwd wins; otherwise the service's directory, anchored on
|
|
2678
|
+
// its project rather than on the server's launch directory.
|
|
2679
|
+
cwd: input.cwd ?? await resolveAgentCwd({ serviceId: input.processId }),
|
|
2680
|
+
commandId: input.commandId
|
|
2681
|
+
},
|
|
2406
2682
|
AGENT_LONG_FETCH_TIMEOUT
|
|
2407
2683
|
);
|
|
2408
2684
|
await auditService.log({
|
|
@@ -2579,25 +2855,36 @@ var setupRouter = router({
|
|
|
2579
2855
|
import { z as z8 } from "zod";
|
|
2580
2856
|
import { TRPCError as TRPCError6 } from "@trpc/server";
|
|
2581
2857
|
var environmentRouter = router({
|
|
2582
|
-
list: protectedProcedure.input(z8.object({
|
|
2583
|
-
|
|
2858
|
+
list: protectedProcedure.input(z8.object({
|
|
2859
|
+
projectId: z8.string().max(128),
|
|
2860
|
+
orgId: z8.string().max(128).nullable().optional()
|
|
2861
|
+
})).query(async ({ input }) => {
|
|
2862
|
+
return await environmentStore.list(input.projectId, input.orgId);
|
|
2584
2863
|
}),
|
|
2585
2864
|
get: protectedProcedure.input(z8.object({ id: z8.string().max(128) })).query(async ({ input }) => {
|
|
2586
2865
|
const env2 = await environmentStore.get(input.id);
|
|
2587
2866
|
if (!env2) throw new TRPCError6({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2588
2867
|
return env2;
|
|
2589
2868
|
}),
|
|
2590
|
-
listByScope: protectedProcedure.input(z8.object({
|
|
2591
|
-
|
|
2869
|
+
listByScope: protectedProcedure.input(z8.object({
|
|
2870
|
+
projectId: z8.string().max(128),
|
|
2871
|
+
orgId: z8.string().max(128).nullable().optional(),
|
|
2872
|
+
scope: z8.enum(["global", "service"])
|
|
2873
|
+
})).query(async ({ input }) => {
|
|
2874
|
+
return await environmentStore.listByScope(input.projectId, input.scope, input.orgId);
|
|
2592
2875
|
}),
|
|
2593
2876
|
findServiceEnvironment: protectedProcedure.input(z8.object({ serviceId: z8.string().max(128), globalEnvId: z8.string().max(128) })).query(async ({ input }) => {
|
|
2594
2877
|
return await environmentStore.findServiceEnvironment(input.serviceId, input.globalEnvId);
|
|
2595
2878
|
}),
|
|
2596
|
-
create: protectedProcedure.input(CreateEnvironmentSchema
|
|
2597
|
-
|
|
2598
|
-
|
|
2879
|
+
create: protectedProcedure.input(CreateEnvironmentSchema.extend({
|
|
2880
|
+
orgId: z8.string().max(128).nullable().optional()
|
|
2881
|
+
})).mutation(async ({ input }) => {
|
|
2882
|
+
const { orgId, ...environment } = input;
|
|
2883
|
+
const target = { id: input.projectId, orgId };
|
|
2884
|
+
const env2 = await environmentStore.create(environment, orgId);
|
|
2885
|
+
const project = await projectStore.get(target);
|
|
2599
2886
|
if (project && !project.environmentIds?.includes(env2.id)) {
|
|
2600
|
-
await projectStore.addEnvironment(
|
|
2887
|
+
await projectStore.addEnvironment(target, env2.id);
|
|
2601
2888
|
}
|
|
2602
2889
|
if (env2.scope === "global" && project) {
|
|
2603
2890
|
for (const serviceId of project.serviceIds) {
|
|
@@ -2618,8 +2905,8 @@ var environmentRouter = router({
|
|
|
2618
2905
|
activateOn: env2.id,
|
|
2619
2906
|
parentIds: [env2.id, ...parentServiceEnvIds],
|
|
2620
2907
|
variables: {}
|
|
2621
|
-
});
|
|
2622
|
-
await projectStore.addEnvironment(
|
|
2908
|
+
}, orgId);
|
|
2909
|
+
await projectStore.addEnvironment(target, svcEnv.id);
|
|
2623
2910
|
}
|
|
2624
2911
|
}
|
|
2625
2912
|
return env2;
|
|
@@ -2630,7 +2917,7 @@ var environmentRouter = router({
|
|
|
2630
2917
|
const env2 = await environmentStore.update(input);
|
|
2631
2918
|
if (!env2) throw new TRPCError6({ code: "NOT_FOUND", message: `Environment ${input.id} not found` });
|
|
2632
2919
|
if (env2.scope === "global" && input.name && input.name !== existing.name) {
|
|
2633
|
-
const project = await projectStore.get(env2.projectId);
|
|
2920
|
+
const project = await projectStore.get(environmentStore.projectRefOf(env2) ?? env2.projectId);
|
|
2634
2921
|
if (project) {
|
|
2635
2922
|
const serviceNames = /* @__PURE__ */ new Map();
|
|
2636
2923
|
for (const serviceId of project.serviceIds) {
|
|
@@ -2653,7 +2940,7 @@ var environmentRouter = router({
|
|
|
2653
2940
|
}
|
|
2654
2941
|
const deleted = await environmentStore.delete(input.id);
|
|
2655
2942
|
if (!deleted) throw new TRPCError6({ code: "INTERNAL_SERVER_ERROR", message: "Failed to delete environment" });
|
|
2656
|
-
await projectStore.removeEnvironment(env2.projectId, input.id);
|
|
2943
|
+
await projectStore.removeEnvironment(environmentStore.projectRefOf(env2) ?? env2.projectId, input.id);
|
|
2657
2944
|
return { success: true };
|
|
2658
2945
|
}),
|
|
2659
2946
|
resolve: protectedProcedure.input(z8.object({ environmentId: z8.string().max(128) })).query(async ({ input, ctx }) => {
|
|
@@ -2728,11 +3015,12 @@ var environmentRouter = router({
|
|
|
2728
3015
|
*/
|
|
2729
3016
|
keyUsages: protectedProcedure.input(z8.object({
|
|
2730
3017
|
projectId: z8.string().max(128),
|
|
3018
|
+
orgId: z8.string().max(128).nullable().optional(),
|
|
2731
3019
|
key: z8.string().min(1).max(256),
|
|
2732
3020
|
/** Environment holding the variable about to be edited — enables inherited-impact detection */
|
|
2733
3021
|
environmentId: z8.string().max(128).optional()
|
|
2734
3022
|
})).query(async ({ input }) => {
|
|
2735
|
-
const environments = await environmentStore.list(input.projectId);
|
|
3023
|
+
const environments = await environmentStore.list(input.projectId, input.orgId);
|
|
2736
3024
|
const services = await serviceStore.list();
|
|
2737
3025
|
const serviceName = (id) => services.find((s) => s.id === id)?.name;
|
|
2738
3026
|
const escaped = input.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2801,7 +3089,6 @@ var environmentRouter = router({
|
|
|
2801
3089
|
|
|
2802
3090
|
// ../server/src/trpc/routers/settings.ts
|
|
2803
3091
|
import { promises as dns } from "dns";
|
|
2804
|
-
import { resolve } from "path";
|
|
2805
3092
|
import { z as z9 } from "zod";
|
|
2806
3093
|
import { TRPCError as TRPCError7 } from "@trpc/server";
|
|
2807
3094
|
var NotificationPreferencesPatchSchema = NotificationPreferencesSchema.partial().strict();
|
|
@@ -2821,9 +3108,12 @@ var settingsRouter = router({
|
|
|
2821
3108
|
* Paths the server is running from (working directory + data directory).
|
|
2822
3109
|
* Read-only, purely informational for the settings UI.
|
|
2823
3110
|
*/
|
|
2824
|
-
getPaths: protectedProcedure.query(() => ({
|
|
2825
|
-
|
|
2826
|
-
|
|
3111
|
+
getPaths: protectedProcedure.query(async () => ({
|
|
3112
|
+
// The launch directory, not the process's current one — they are the same
|
|
3113
|
+
// today only because nothing calls chdir, which is exactly what must not be
|
|
3114
|
+
// relied on.
|
|
3115
|
+
cwd: await resolveWorkspaceCwd({}),
|
|
3116
|
+
dataDir: env.DATA_DIR,
|
|
2827
3117
|
// Where cloud projects live: their vaults belong to the machine, not to
|
|
2828
3118
|
// whichever directory the server happened to be started in.
|
|
2829
3119
|
machineDir: machineRoot()
|
|
@@ -3187,7 +3477,7 @@ var settingsRouter = router({
|
|
|
3187
3477
|
}
|
|
3188
3478
|
} catch {
|
|
3189
3479
|
}
|
|
3190
|
-
await new Promise((
|
|
3480
|
+
await new Promise((resolve7) => setTimeout(resolve7, 500));
|
|
3191
3481
|
}
|
|
3192
3482
|
}),
|
|
3193
3483
|
/**
|
|
@@ -3373,16 +3663,16 @@ var settingsRouter = router({
|
|
|
3373
3663
|
import { z as z11 } from "zod";
|
|
3374
3664
|
import { TRPCError as TRPCError8 } from "@trpc/server";
|
|
3375
3665
|
import { execFile as execFile2, spawn } from "child_process";
|
|
3376
|
-
import { stat as
|
|
3377
|
-
import { resolve as resolve2, join as
|
|
3666
|
+
import { stat as stat3 } from "fs/promises";
|
|
3667
|
+
import { resolve as resolve2, join as join7 } from "path";
|
|
3378
3668
|
import { homedir as homedir2 } from "os";
|
|
3379
3669
|
|
|
3380
3670
|
// ../server/src/services/user-config-manager.ts
|
|
3381
|
-
import { readFile as
|
|
3382
|
-
import { join as
|
|
3671
|
+
import { readFile as readFile7, writeFile as writeFile7, rename as rename7, mkdir as mkdir7, chmod as chmod6 } from "fs/promises";
|
|
3672
|
+
import { join as join6 } from "path";
|
|
3383
3673
|
import { homedir } from "os";
|
|
3384
3674
|
import { z as z10 } from "zod";
|
|
3385
|
-
var CONFIG_DIR =
|
|
3675
|
+
var CONFIG_DIR = join6(homedir(), ".runeya");
|
|
3386
3676
|
var CONFIG_FILE = "runeya.conf";
|
|
3387
3677
|
var EditorPreferencesSchema = z10.object({
|
|
3388
3678
|
default: z10.string().max(64).optional()
|
|
@@ -3396,23 +3686,26 @@ var UserConfigSchema = z10.record(
|
|
|
3396
3686
|
z10.string().max(128),
|
|
3397
3687
|
ProjectSettingsSchema
|
|
3398
3688
|
).refine((obj) => Object.keys(obj).length <= 100, "Too many projects in config");
|
|
3689
|
+
function configKey(ref) {
|
|
3690
|
+
return projectKey(ref);
|
|
3691
|
+
}
|
|
3399
3692
|
var UserConfigManager = class {
|
|
3400
3693
|
config = null;
|
|
3401
3694
|
async ensureDir() {
|
|
3402
|
-
await
|
|
3695
|
+
await mkdir7(CONFIG_DIR, { recursive: true });
|
|
3403
3696
|
}
|
|
3404
3697
|
async atomicWrite(data) {
|
|
3405
3698
|
await this.ensureDir();
|
|
3406
|
-
const filePath =
|
|
3699
|
+
const filePath = join6(CONFIG_DIR, CONFIG_FILE);
|
|
3407
3700
|
const tmpPath = filePath + ".tmp";
|
|
3408
|
-
await
|
|
3409
|
-
await
|
|
3410
|
-
await
|
|
3701
|
+
await writeFile7(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
3702
|
+
await rename7(tmpPath, filePath);
|
|
3703
|
+
await chmod6(filePath, 384);
|
|
3411
3704
|
}
|
|
3412
3705
|
async getConfig() {
|
|
3413
3706
|
if (!this.config) {
|
|
3414
3707
|
try {
|
|
3415
|
-
const raw = await
|
|
3708
|
+
const raw = await readFile7(join6(CONFIG_DIR, CONFIG_FILE), "utf-8");
|
|
3416
3709
|
const parsed = UserConfigSchema.safeParse(JSON.parse(raw));
|
|
3417
3710
|
this.config = parsed.success ? parsed.data : {};
|
|
3418
3711
|
} catch {
|
|
@@ -3425,21 +3718,22 @@ var UserConfigManager = class {
|
|
|
3425
3718
|
this.config = config;
|
|
3426
3719
|
await this.atomicWrite(config);
|
|
3427
3720
|
}
|
|
3428
|
-
async getEditorPreferences(
|
|
3721
|
+
async getEditorPreferences(ref) {
|
|
3429
3722
|
const config = await this.getConfig();
|
|
3430
|
-
return config[
|
|
3723
|
+
return config[configKey(ref)]?.settings?.editors ?? config[ref.id]?.settings?.editors ?? {};
|
|
3431
3724
|
}
|
|
3432
|
-
async setEditorPreference(
|
|
3725
|
+
async setEditorPreference(ref, target, editorKey) {
|
|
3433
3726
|
const config = await this.getConfig();
|
|
3434
|
-
|
|
3435
|
-
if (!config[
|
|
3436
|
-
if (!config[
|
|
3437
|
-
config[
|
|
3727
|
+
const key = configKey(ref);
|
|
3728
|
+
if (!config[key]) config[key] = { settings: config[ref.id]?.settings };
|
|
3729
|
+
if (!config[key].settings) config[key].settings = {};
|
|
3730
|
+
if (!config[key].settings.editors) config[key].settings.editors = {};
|
|
3731
|
+
config[key].settings.editors[target] = editorKey;
|
|
3438
3732
|
await this.saveConfig(config);
|
|
3439
3733
|
}
|
|
3440
|
-
async removeEditorPreference(
|
|
3734
|
+
async removeEditorPreference(ref, target) {
|
|
3441
3735
|
const config = await this.getConfig();
|
|
3442
|
-
const editors = config[
|
|
3736
|
+
const editors = config[configKey(ref)]?.settings?.editors;
|
|
3443
3737
|
if (editors && target in editors) {
|
|
3444
3738
|
delete editors[target];
|
|
3445
3739
|
await this.saveConfig(config);
|
|
@@ -3462,13 +3756,12 @@ async function getServiceCwd(serviceId) {
|
|
|
3462
3756
|
if (!service) {
|
|
3463
3757
|
throw new TRPCError8({ code: "NOT_FOUND", message: "Service not found" });
|
|
3464
3758
|
}
|
|
3465
|
-
|
|
3466
|
-
return resolveCwd(rawCwd);
|
|
3759
|
+
return resolveCwd(await resolveWorkspaceCwd({ serviceId }));
|
|
3467
3760
|
}
|
|
3468
3761
|
async function isCommandAvailable(command) {
|
|
3469
|
-
return new Promise((
|
|
3762
|
+
return new Promise((resolve7) => {
|
|
3470
3763
|
execFile2("which", [command], { timeout: 5e3 }, (err) => {
|
|
3471
|
-
|
|
3764
|
+
resolve7(!err);
|
|
3472
3765
|
});
|
|
3473
3766
|
});
|
|
3474
3767
|
}
|
|
@@ -3482,35 +3775,52 @@ var editorRouter = router({
|
|
|
3482
3775
|
);
|
|
3483
3776
|
return results;
|
|
3484
3777
|
}),
|
|
3485
|
-
getPreferences: protectedProcedure.input(z11.object({
|
|
3486
|
-
|
|
3778
|
+
getPreferences: protectedProcedure.input(z11.object({
|
|
3779
|
+
projectId: z11.string().max(128),
|
|
3780
|
+
orgId: z11.string().max(128).nullable().optional()
|
|
3781
|
+
})).query(async ({ input }) => {
|
|
3782
|
+
return userConfigManager.getEditorPreferences({ id: input.projectId, orgId: input.orgId });
|
|
3487
3783
|
}),
|
|
3488
3784
|
setDefault: protectedProcedure.input(z11.object({
|
|
3489
3785
|
projectId: z11.string().max(128),
|
|
3786
|
+
orgId: z11.string().max(128).nullable().optional(),
|
|
3490
3787
|
editorKey: z11.string().max(64)
|
|
3491
3788
|
})).mutation(async ({ input }) => {
|
|
3492
3789
|
if (!EDITOR_BY_KEY.has(input.editorKey)) {
|
|
3493
3790
|
throw new TRPCError8({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3494
3791
|
}
|
|
3495
|
-
await userConfigManager.setEditorPreference(
|
|
3792
|
+
await userConfigManager.setEditorPreference(
|
|
3793
|
+
{ id: input.projectId, orgId: input.orgId },
|
|
3794
|
+
"default",
|
|
3795
|
+
input.editorKey
|
|
3796
|
+
);
|
|
3496
3797
|
return { success: true };
|
|
3497
3798
|
}),
|
|
3498
3799
|
setServiceEditor: protectedProcedure.input(z11.object({
|
|
3499
3800
|
projectId: z11.string().max(128),
|
|
3801
|
+
orgId: z11.string().max(128).nullable().optional(),
|
|
3500
3802
|
serviceId: z11.string().max(128),
|
|
3501
3803
|
editorKey: z11.string().max(64)
|
|
3502
3804
|
})).mutation(async ({ input }) => {
|
|
3503
3805
|
if (!EDITOR_BY_KEY.has(input.editorKey)) {
|
|
3504
3806
|
throw new TRPCError8({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3505
3807
|
}
|
|
3506
|
-
await userConfigManager.setEditorPreference(
|
|
3808
|
+
await userConfigManager.setEditorPreference(
|
|
3809
|
+
{ id: input.projectId, orgId: input.orgId },
|
|
3810
|
+
input.serviceId,
|
|
3811
|
+
input.editorKey
|
|
3812
|
+
);
|
|
3507
3813
|
return { success: true };
|
|
3508
3814
|
}),
|
|
3509
3815
|
removeServiceEditor: protectedProcedure.input(z11.object({
|
|
3510
3816
|
projectId: z11.string().max(128),
|
|
3817
|
+
orgId: z11.string().max(128).nullable().optional(),
|
|
3511
3818
|
serviceId: z11.string().max(128)
|
|
3512
3819
|
})).mutation(async ({ input }) => {
|
|
3513
|
-
await userConfigManager.removeEditorPreference(
|
|
3820
|
+
await userConfigManager.removeEditorPreference(
|
|
3821
|
+
{ id: input.projectId, orgId: input.orgId },
|
|
3822
|
+
input.serviceId
|
|
3823
|
+
);
|
|
3514
3824
|
return { success: true };
|
|
3515
3825
|
}),
|
|
3516
3826
|
open: protectedProcedure.input(z11.object({
|
|
@@ -3524,7 +3834,7 @@ var editorRouter = router({
|
|
|
3524
3834
|
const expanded = input.path.replace(/^~(\/|$)/, `${process.env["HOME"] ?? "/tmp"}$1`).replace(/\$\{(\w+)\}|\$(\w+)/g, (_, a, b) => process.env[a || b] ?? "");
|
|
3525
3835
|
const resolvedPath = resolve2(expanded || ".");
|
|
3526
3836
|
try {
|
|
3527
|
-
await
|
|
3837
|
+
await stat3(resolvedPath);
|
|
3528
3838
|
} catch (err) {
|
|
3529
3839
|
if (err instanceof TRPCError8) throw err;
|
|
3530
3840
|
throw new TRPCError8({ code: "BAD_REQUEST", message: `Path does not exist: ${resolvedPath}` });
|
|
@@ -3548,12 +3858,12 @@ var editorRouter = router({
|
|
|
3548
3858
|
throw new TRPCError8({ code: "BAD_REQUEST", message: `Unknown editor: ${input.editorKey}` });
|
|
3549
3859
|
}
|
|
3550
3860
|
const cwd = await getServiceCwd(input.serviceId);
|
|
3551
|
-
const filePath =
|
|
3861
|
+
const filePath = join7(cwd, input.relativePath);
|
|
3552
3862
|
if (!filePath.startsWith(cwd)) {
|
|
3553
3863
|
throw new TRPCError8({ code: "BAD_REQUEST", message: "Path traversal not allowed" });
|
|
3554
3864
|
}
|
|
3555
3865
|
try {
|
|
3556
|
-
await
|
|
3866
|
+
await stat3(filePath);
|
|
3557
3867
|
} catch {
|
|
3558
3868
|
throw new TRPCError8({ code: "BAD_REQUEST", message: `File does not exist: ${filePath}` });
|
|
3559
3869
|
}
|
|
@@ -3571,8 +3881,8 @@ var editorRouter = router({
|
|
|
3571
3881
|
// ../server/src/trpc/routers/api-keys.ts
|
|
3572
3882
|
import { z as z12 } from "zod";
|
|
3573
3883
|
import { TRPCError as TRPCError9 } from "@trpc/server";
|
|
3574
|
-
import { mkdir as
|
|
3575
|
-
import { join as
|
|
3884
|
+
import { mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
|
|
3885
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
3576
3886
|
var ALLOWED_SKILL_PATHS = /* @__PURE__ */ new Set([
|
|
3577
3887
|
".claude/skills/runeya/SKILL.md",
|
|
3578
3888
|
".cursor/rules/runeya.mdc",
|
|
@@ -3619,9 +3929,9 @@ var apiKeysRouter = router({
|
|
|
3619
3929
|
if (!ALLOWED_SKILL_PATHS.has(input.relativePath)) {
|
|
3620
3930
|
throw new TRPCError9({ code: "BAD_REQUEST", message: "Chemin non autoris\xE9" });
|
|
3621
3931
|
}
|
|
3622
|
-
const abs =
|
|
3623
|
-
await
|
|
3624
|
-
await
|
|
3932
|
+
const abs = join8(await resolveWorkspaceCwd({}), input.relativePath);
|
|
3933
|
+
await mkdir8(dirname4(abs), { recursive: true });
|
|
3934
|
+
await writeFile8(abs, input.content, { encoding: "utf-8", mode: 420 });
|
|
3625
3935
|
return { path: input.relativePath };
|
|
3626
3936
|
})
|
|
3627
3937
|
});
|
|
@@ -3947,17 +4257,17 @@ function buildApiDocs(apiBaseUrl, apiToken) {
|
|
|
3947
4257
|
}
|
|
3948
4258
|
|
|
3949
4259
|
// ../server/src/services/claude-transcript-sync.ts
|
|
3950
|
-
import { readdir as readdir2, access, readFile as
|
|
4260
|
+
import { readdir as readdir2, access, readFile as readFile9 } from "fs/promises";
|
|
3951
4261
|
import { watch } from "fs";
|
|
3952
4262
|
import { homedir as homedir3 } from "os";
|
|
3953
|
-
import { join as
|
|
4263
|
+
import { join as join10 } from "path";
|
|
3954
4264
|
|
|
3955
4265
|
// ../server/src/services/conversation-store.ts
|
|
3956
4266
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3957
|
-
import { readFile as
|
|
3958
|
-
import { dirname as
|
|
4267
|
+
import { readFile as readFile8, writeFile as writeFile9, rename as rename8, mkdir as mkdir9, chmod as chmod7 } from "fs/promises";
|
|
4268
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
3959
4269
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
3960
|
-
var
|
|
4270
|
+
var FILENAME4 = "conversations.private.json";
|
|
3961
4271
|
async function localProjectId() {
|
|
3962
4272
|
const projects = await projectStore.list().catch(() => []);
|
|
3963
4273
|
return projects.find((p) => !p.orgId)?.id ?? null;
|
|
@@ -3977,11 +4287,11 @@ var ConversationStore = class {
|
|
|
3977
4287
|
* root, but a launch directory owning its own key keeps both together.
|
|
3978
4288
|
*/
|
|
3979
4289
|
getFilePath() {
|
|
3980
|
-
return keyedFilePath(
|
|
4290
|
+
return keyedFilePath(FILENAME4);
|
|
3981
4291
|
}
|
|
3982
4292
|
/** Where older versions kept it — read only, so an older Runeya keeps working. */
|
|
3983
4293
|
getLegacyFilePath() {
|
|
3984
|
-
return legacyMachineFilePath(
|
|
4294
|
+
return legacyMachineFilePath(FILENAME4);
|
|
3985
4295
|
}
|
|
3986
4296
|
/**
|
|
3987
4297
|
* Read the machine file, falling back to the launch directory of old.
|
|
@@ -3991,13 +4301,13 @@ var ConversationStore = class {
|
|
|
3991
4301
|
*/
|
|
3992
4302
|
async readFromDisk() {
|
|
3993
4303
|
try {
|
|
3994
|
-
return await
|
|
4304
|
+
return await readFile8(await this.getFilePath(), "utf-8");
|
|
3995
4305
|
} catch {
|
|
3996
|
-
return
|
|
4306
|
+
return readFile8(this.getLegacyFilePath(), "utf-8");
|
|
3997
4307
|
}
|
|
3998
4308
|
}
|
|
3999
4309
|
async ensureDir() {
|
|
4000
|
-
await
|
|
4310
|
+
await mkdir9(dirname5(await this.getFilePath()), { recursive: true });
|
|
4001
4311
|
}
|
|
4002
4312
|
encryptMessages(messages) {
|
|
4003
4313
|
return encryptValue(JSON.stringify(messages));
|
|
@@ -4061,16 +4371,17 @@ var ConversationStore = class {
|
|
|
4061
4371
|
return;
|
|
4062
4372
|
}
|
|
4063
4373
|
this.loaded = true;
|
|
4374
|
+
await ensureProjectsLoaded();
|
|
4064
4375
|
const vaults = await allVaultRoots();
|
|
4065
|
-
const legacyPaths = [this.getLegacyFilePath(), await keyedFilePath(
|
|
4376
|
+
const legacyPaths = [this.getLegacyFilePath(), await keyedFilePath(FILENAME4)];
|
|
4066
4377
|
const sources = [
|
|
4067
4378
|
...legacyPaths.map((path) => ({ path, legacy: true })),
|
|
4068
|
-
...vaults.map((root) => ({ path:
|
|
4379
|
+
...vaults.map((root) => ({ path: join9(root, FILENAME4), legacy: false }))
|
|
4069
4380
|
];
|
|
4070
4381
|
for (const { path, legacy } of sources) {
|
|
4071
4382
|
let data;
|
|
4072
4383
|
try {
|
|
4073
|
-
data = JSON.parse(await
|
|
4384
|
+
data = JSON.parse(await readFile8(path, "utf-8"));
|
|
4074
4385
|
} catch {
|
|
4075
4386
|
continue;
|
|
4076
4387
|
}
|
|
@@ -4080,7 +4391,7 @@ var ConversationStore = class {
|
|
|
4080
4391
|
const conv = this.fromDisk(item);
|
|
4081
4392
|
if (!conv.projectId && inherited) conv.projectId = inherited;
|
|
4082
4393
|
this.conversations.set(item.id, conv);
|
|
4083
|
-
this.roots.set(item.id,
|
|
4394
|
+
this.roots.set(item.id, dirname5(path));
|
|
4084
4395
|
} catch {
|
|
4085
4396
|
}
|
|
4086
4397
|
}
|
|
@@ -4105,12 +4416,12 @@ var ConversationStore = class {
|
|
|
4105
4416
|
byRoot.set(root, bucket);
|
|
4106
4417
|
}
|
|
4107
4418
|
for (const [root, data] of byRoot) {
|
|
4108
|
-
await
|
|
4109
|
-
const filePath =
|
|
4419
|
+
await mkdir9(root, { recursive: true });
|
|
4420
|
+
const filePath = join9(root, FILENAME4);
|
|
4110
4421
|
const tmpPath = `${filePath}.tmp`;
|
|
4111
|
-
await
|
|
4112
|
-
await
|
|
4113
|
-
await
|
|
4422
|
+
await writeFile9(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
4423
|
+
await rename8(tmpPath, filePath);
|
|
4424
|
+
await chmod7(filePath, 384);
|
|
4114
4425
|
}
|
|
4115
4426
|
}
|
|
4116
4427
|
/**
|
|
@@ -4120,8 +4431,22 @@ var ConversationStore = class {
|
|
|
4120
4431
|
* key's own directory — never lost for want of an owner.
|
|
4121
4432
|
*/
|
|
4122
4433
|
async rootFor(conv) {
|
|
4123
|
-
|
|
4124
|
-
|
|
4434
|
+
const known = this.roots.get(conv.id) ?? null;
|
|
4435
|
+
if (conv.projectId) return rootOfProjectAmong(conv.projectId, known);
|
|
4436
|
+
return known ?? dirname5(await keyedFilePath(FILENAME4));
|
|
4437
|
+
}
|
|
4438
|
+
/**
|
|
4439
|
+
* Le projet d'une conversation, organisation comprise.
|
|
4440
|
+
*
|
|
4441
|
+
* Ce que la conversation ne dit pas d'elle-même : elle ne nomme que l'id.
|
|
4442
|
+
* Le coffre d'où elle vient fournit l'organisation, et c'est ce couple qu'il
|
|
4443
|
+
* faut pour retrouver le répertoire de travail du projet.
|
|
4444
|
+
*/
|
|
4445
|
+
async projectRefOf(conv) {
|
|
4446
|
+
if (!conv.projectId) return null;
|
|
4447
|
+
const known = this.roots.get(conv.id) ?? null;
|
|
4448
|
+
const ref = refOfProjectAmong(conv.projectId, known);
|
|
4449
|
+
return { id: ref.id, orgId: ref.orgId ?? null };
|
|
4125
4450
|
}
|
|
4126
4451
|
async save() {
|
|
4127
4452
|
const write = this.saveQueue.then(() => this.saveInternal());
|
|
@@ -4137,7 +4462,7 @@ var ConversationStore = class {
|
|
|
4137
4462
|
await this.load();
|
|
4138
4463
|
return this.conversations.get(id) ?? null;
|
|
4139
4464
|
}
|
|
4140
|
-
async create(title, isCli = false, workflowId, workflowType, runner, kanbanBoardId, kanbanCardId, providerId, projectId) {
|
|
4465
|
+
async create(title, isCli = false, workflowId, workflowType, runner, kanbanBoardId, kanbanCardId, providerId, projectId, orgId) {
|
|
4141
4466
|
await this.load();
|
|
4142
4467
|
const now = Date.now();
|
|
4143
4468
|
const conv = {
|
|
@@ -4156,6 +4481,9 @@ var ConversationStore = class {
|
|
|
4156
4481
|
updatedAt: now
|
|
4157
4482
|
};
|
|
4158
4483
|
this.conversations.set(conv.id, conv);
|
|
4484
|
+
if (projectId && orgId !== void 0) {
|
|
4485
|
+
this.roots.set(conv.id, vaultFor({ id: projectId, orgId }).root);
|
|
4486
|
+
}
|
|
4159
4487
|
await this.save();
|
|
4160
4488
|
this.events.emit("changed", { type: "created", conversation: this.snapshotConversation(conv) });
|
|
4161
4489
|
return conv;
|
|
@@ -4218,7 +4546,7 @@ var ConversationStore = class {
|
|
|
4218
4546
|
var conversationStore = new ConversationStore();
|
|
4219
4547
|
|
|
4220
4548
|
// ../server/src/services/claude-transcript-sync.ts
|
|
4221
|
-
var PROJECTS_DIR =
|
|
4549
|
+
var PROJECTS_DIR = join10(homedir3(), ".claude", "projects");
|
|
4222
4550
|
var DEFAULT_TITLES = /* @__PURE__ */ new Set(["", "New conversation", "Nouvelle conversation"]);
|
|
4223
4551
|
async function findClaudeSessionFile(sessionId) {
|
|
4224
4552
|
let entries;
|
|
@@ -4229,7 +4557,7 @@ async function findClaudeSessionFile(sessionId) {
|
|
|
4229
4557
|
}
|
|
4230
4558
|
const filename = `${sessionId}.jsonl`;
|
|
4231
4559
|
for (const dir of entries) {
|
|
4232
|
-
const candidate =
|
|
4560
|
+
const candidate = join10(PROJECTS_DIR, dir, filename);
|
|
4233
4561
|
try {
|
|
4234
4562
|
await access(candidate);
|
|
4235
4563
|
return candidate;
|
|
@@ -4244,7 +4572,7 @@ async function claudeSessionExists(sessionId) {
|
|
|
4244
4572
|
async function readClaudeTranscript(sessionId) {
|
|
4245
4573
|
const path = await findClaudeSessionFile(sessionId);
|
|
4246
4574
|
if (!path) return { found: false, path: null, entries: [] };
|
|
4247
|
-
const raw = await
|
|
4575
|
+
const raw = await readFile9(path, "utf-8");
|
|
4248
4576
|
const entries = [];
|
|
4249
4577
|
for (const line of raw.split("\n")) {
|
|
4250
4578
|
if (!line.trim()) continue;
|
|
@@ -4392,7 +4720,7 @@ async function startClaudeTranscriptWatcher() {
|
|
|
4392
4720
|
}
|
|
4393
4721
|
for (const dir of topLevel) {
|
|
4394
4722
|
try {
|
|
4395
|
-
const w = watch(
|
|
4723
|
+
const w = watch(join10(PROJECTS_DIR, dir), (_event, filename) => {
|
|
4396
4724
|
handleFsEvent(filename ? filename.toString() : null);
|
|
4397
4725
|
});
|
|
4398
4726
|
w.on("error", () => {
|
|
@@ -4418,8 +4746,8 @@ function stopClaudeTranscriptWatcher() {
|
|
|
4418
4746
|
// ../server/src/services/codex-sessions.ts
|
|
4419
4747
|
import { readdir as readdir3 } from "fs/promises";
|
|
4420
4748
|
import { homedir as homedir4 } from "os";
|
|
4421
|
-
import { join as
|
|
4422
|
-
var SESSIONS_DIR =
|
|
4749
|
+
import { join as join11 } from "path";
|
|
4750
|
+
var SESSIONS_DIR = join11(homedir4(), ".codex", "sessions");
|
|
4423
4751
|
async function findCodexSessionFile(sessionId) {
|
|
4424
4752
|
const suffix = `-${sessionId}.jsonl`;
|
|
4425
4753
|
async function walk(dir) {
|
|
@@ -4430,7 +4758,7 @@ async function findCodexSessionFile(sessionId) {
|
|
|
4430
4758
|
return null;
|
|
4431
4759
|
}
|
|
4432
4760
|
for (const entry of entries) {
|
|
4433
|
-
const full =
|
|
4761
|
+
const full = join11(dir, entry.name);
|
|
4434
4762
|
if (entry.isDirectory()) {
|
|
4435
4763
|
const found = await walk(full);
|
|
4436
4764
|
if (found) return found;
|
|
@@ -4805,7 +5133,7 @@ function parse(text) {
|
|
|
4805
5133
|
return result;
|
|
4806
5134
|
}
|
|
4807
5135
|
function getClaudeUsage() {
|
|
4808
|
-
return new Promise((
|
|
5136
|
+
return new Promise((resolve7, reject) => {
|
|
4809
5137
|
const env2 = { ...process.env };
|
|
4810
5138
|
delete env2["CLAUDECODE"];
|
|
4811
5139
|
let claudeBin = "claude";
|
|
@@ -4836,7 +5164,7 @@ function getClaudeUsage() {
|
|
|
4836
5164
|
clearTimeout(hardTimeout);
|
|
4837
5165
|
term?.kill();
|
|
4838
5166
|
const clean = buffer.replaceAll(/\x1b\[[0-9;]*X/g, "\n").replaceAll(/\x1b\[[0-9;]*[mGKHFABCDJsu]/g, "").replaceAll(/\x1b[^a-zA-Z]*[a-zA-Z]/g, "").replaceAll("\r", "");
|
|
4839
|
-
|
|
5167
|
+
resolve7(parse(clean));
|
|
4840
5168
|
}
|
|
4841
5169
|
const hardTimeout = setTimeout(() => {
|
|
4842
5170
|
if (!done) finish();
|
|
@@ -4970,7 +5298,7 @@ function parseCodexStatusText(text) {
|
|
|
4970
5298
|
return entries;
|
|
4971
5299
|
}
|
|
4972
5300
|
function getCodexUsageEntries() {
|
|
4973
|
-
return new Promise((
|
|
5301
|
+
return new Promise((resolve7, reject) => {
|
|
4974
5302
|
let term = null;
|
|
4975
5303
|
try {
|
|
4976
5304
|
term = pty2.spawn("codex", [], {
|
|
@@ -5000,7 +5328,7 @@ function getCodexUsageEntries() {
|
|
|
5000
5328
|
const debugClean = stripAnsi(buffer).slice(-6e3);
|
|
5001
5329
|
process.stdout.write("[codex-usage] raw tail:\n" + debugClean + "\n");
|
|
5002
5330
|
}
|
|
5003
|
-
|
|
5331
|
+
resolve7(parseCodexStatusText(buffer));
|
|
5004
5332
|
};
|
|
5005
5333
|
const hardTimeout = setTimeout(() => {
|
|
5006
5334
|
finish();
|
|
@@ -5390,12 +5718,12 @@ var ScenarioStore = class {
|
|
|
5390
5718
|
var scenarioStore = new ScenarioStore();
|
|
5391
5719
|
|
|
5392
5720
|
// ../server/src/services/scenario-run-store.ts
|
|
5393
|
-
import { mkdir as
|
|
5394
|
-
import { join as
|
|
5721
|
+
import { mkdir as mkdir11, rm as rm2, readFile as readFile11 } from "fs/promises";
|
|
5722
|
+
import { join as join12 } from "path";
|
|
5395
5723
|
|
|
5396
5724
|
// ../server/src/services/run-store-utils.ts
|
|
5397
|
-
import { chmod as
|
|
5398
|
-
import { dirname as
|
|
5725
|
+
import { chmod as chmod8, mkdir as mkdir10, rename as rename9, writeFile as writeFile10, readFile as readFile10 } from "fs/promises";
|
|
5726
|
+
import { dirname as dirname6, resolve as resolve3, sep } from "path";
|
|
5399
5727
|
var SAFE_PATH_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
5400
5728
|
function resolveConversationRunDir(rootDirName, conversationId) {
|
|
5401
5729
|
if (!SAFE_PATH_SEGMENT_RE.test(conversationId)) {
|
|
@@ -5409,18 +5737,18 @@ function resolveConversationRunDir(rootDirName, conversationId) {
|
|
|
5409
5737
|
return dir;
|
|
5410
5738
|
}
|
|
5411
5739
|
async function atomicWriteSecure(filePath, content) {
|
|
5412
|
-
await
|
|
5740
|
+
await mkdir10(dirname6(filePath), { recursive: true });
|
|
5413
5741
|
const tmpPath = `${filePath}.tmp`;
|
|
5414
|
-
await
|
|
5415
|
-
await
|
|
5416
|
-
await
|
|
5742
|
+
await writeFile10(tmpPath, content, "utf-8");
|
|
5743
|
+
await rename9(tmpPath, filePath);
|
|
5744
|
+
await chmod8(filePath, 384);
|
|
5417
5745
|
}
|
|
5418
5746
|
async function atomicWriteJsonSecure(filePath, data, pretty = false) {
|
|
5419
5747
|
await atomicWriteSecure(filePath, JSON.stringify(data, null, pretty ? 2 : 0));
|
|
5420
5748
|
}
|
|
5421
5749
|
async function readJsonFileSafe(filePath) {
|
|
5422
5750
|
try {
|
|
5423
|
-
const raw = await
|
|
5751
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
5424
5752
|
return JSON.parse(raw);
|
|
5425
5753
|
} catch {
|
|
5426
5754
|
return null;
|
|
@@ -5443,7 +5771,7 @@ var ScenarioRunStore = class _ScenarioRunStore {
|
|
|
5443
5771
|
}
|
|
5444
5772
|
async initializeConversationWorkspace(conversationId, scenario, userInput) {
|
|
5445
5773
|
const dir = this.getRunDir(conversationId);
|
|
5446
|
-
await
|
|
5774
|
+
await mkdir11(dir, { recursive: true });
|
|
5447
5775
|
await this.saveScenarioSnapshot(conversationId, scenario);
|
|
5448
5776
|
const nodesById = new Map(scenario.nodes.map((p) => [p.id, p]));
|
|
5449
5777
|
const teamMap = new Map(scenario.groups.map((t2) => [t2.id, t2.name]));
|
|
@@ -5468,8 +5796,8 @@ var ScenarioRunStore = class _ScenarioRunStore {
|
|
|
5468
5796
|
"- Use read-only references from linked upstream files as context.",
|
|
5469
5797
|
""
|
|
5470
5798
|
].join("\n");
|
|
5471
|
-
await atomicWriteSecure(
|
|
5472
|
-
await atomicWriteSecure(
|
|
5799
|
+
await atomicWriteSecure(join12(dir, "README.md"), overview);
|
|
5800
|
+
await atomicWriteSecure(join12(dir, "USER_REQUEST.md"), `# User request
|
|
5473
5801
|
|
|
5474
5802
|
${userInput.trim() || "(empty)"}
|
|
5475
5803
|
`);
|
|
@@ -5495,7 +5823,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
5495
5823
|
"- Waiting for assignment.",
|
|
5496
5824
|
""
|
|
5497
5825
|
].join("\n");
|
|
5498
|
-
await atomicWriteSecure(
|
|
5826
|
+
await atomicWriteSecure(join12(dir, fileName), content);
|
|
5499
5827
|
}
|
|
5500
5828
|
}
|
|
5501
5829
|
async deleteRunDir(conversationId) {
|
|
@@ -5555,7 +5883,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
5555
5883
|
if (!runState) return;
|
|
5556
5884
|
try {
|
|
5557
5885
|
const dir = this.getRunDir(conversationId);
|
|
5558
|
-
await
|
|
5886
|
+
await mkdir11(dir, { recursive: true });
|
|
5559
5887
|
const data = {
|
|
5560
5888
|
nodeStates: { ...runState.nodeStates },
|
|
5561
5889
|
nodeOutputs: { ...runState.nodeOutputs },
|
|
@@ -5563,18 +5891,18 @@ ${userInput.trim() || "(empty)"}
|
|
|
5563
5891
|
...Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {},
|
|
5564
5892
|
...Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}
|
|
5565
5893
|
};
|
|
5566
|
-
await atomicWriteJsonSecure(
|
|
5894
|
+
await atomicWriteJsonSecure(join12(dir, "run-state.json"), data);
|
|
5567
5895
|
} catch {
|
|
5568
5896
|
}
|
|
5569
5897
|
}
|
|
5570
5898
|
async loadPersistedRunState(conversationId) {
|
|
5571
5899
|
const dir = this.getRunDir(conversationId);
|
|
5572
|
-
return readJsonFileSafe(
|
|
5900
|
+
return readJsonFileSafe(join12(dir, "run-state.json"));
|
|
5573
5901
|
}
|
|
5574
5902
|
async readUserRequest(conversationId) {
|
|
5575
5903
|
try {
|
|
5576
5904
|
const dir = this.getRunDir(conversationId);
|
|
5577
|
-
const content = await
|
|
5905
|
+
const content = await readFile11(join12(dir, "USER_REQUEST.md"), "utf-8");
|
|
5578
5906
|
return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
|
|
5579
5907
|
} catch {
|
|
5580
5908
|
return null;
|
|
@@ -5583,23 +5911,23 @@ ${userInput.trim() || "(empty)"}
|
|
|
5583
5911
|
async readReadme(conversationId) {
|
|
5584
5912
|
try {
|
|
5585
5913
|
const dir = this.getRunDir(conversationId);
|
|
5586
|
-
return await
|
|
5914
|
+
return await readFile11(join12(dir, "README.md"), "utf-8");
|
|
5587
5915
|
} catch {
|
|
5588
5916
|
return null;
|
|
5589
5917
|
}
|
|
5590
5918
|
}
|
|
5591
5919
|
async saveScenarioSnapshot(conversationId, scenario) {
|
|
5592
5920
|
const dir = this.getRunDir(conversationId);
|
|
5593
|
-
await
|
|
5921
|
+
await mkdir11(dir, { recursive: true });
|
|
5594
5922
|
await atomicWriteJsonSecure(
|
|
5595
|
-
|
|
5923
|
+
join12(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
|
|
5596
5924
|
scenario,
|
|
5597
5925
|
true
|
|
5598
5926
|
);
|
|
5599
5927
|
}
|
|
5600
5928
|
async loadScenarioSnapshot(conversationId) {
|
|
5601
5929
|
const dir = this.getRunDir(conversationId);
|
|
5602
|
-
return readJsonFileSafe(
|
|
5930
|
+
return readJsonFileSafe(join12(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));
|
|
5603
5931
|
}
|
|
5604
5932
|
};
|
|
5605
5933
|
var scenarioRunStore = new ScenarioRunStore();
|
|
@@ -5645,9 +5973,9 @@ async function fetchProviderModels(provider, plaintextKey) {
|
|
|
5645
5973
|
|
|
5646
5974
|
// ../server/src/services/image-storage.service.ts
|
|
5647
5975
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5648
|
-
import { mkdir as
|
|
5976
|
+
import { mkdir as mkdir12, writeFile as writeFile11, readFile as readFile12, unlink as unlink2, readdir as readdir4 } from "fs/promises";
|
|
5649
5977
|
import { existsSync } from "fs";
|
|
5650
|
-
import { join as
|
|
5978
|
+
import { join as join13, extname, resolve as resolve4, sep as sep2 } from "path";
|
|
5651
5979
|
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
5652
5980
|
"image/png",
|
|
5653
5981
|
"image/jpeg",
|
|
@@ -5664,7 +5992,7 @@ var ImageStorageService = class {
|
|
|
5664
5992
|
this.dir = dir;
|
|
5665
5993
|
}
|
|
5666
5994
|
async ensureTmpDir() {
|
|
5667
|
-
await
|
|
5995
|
+
await mkdir12(this.dir, { recursive: true, mode: 448 });
|
|
5668
5996
|
}
|
|
5669
5997
|
async saveImage(buffer, originalName, mimeType, contextId) {
|
|
5670
5998
|
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
@@ -5684,15 +6012,15 @@ var ImageStorageService = class {
|
|
|
5684
6012
|
const imageId = randomUUID4();
|
|
5685
6013
|
const safeExt = ext.replace(/[^a-z.]/g, "");
|
|
5686
6014
|
const fileName = `${imageId}${safeExt}`;
|
|
5687
|
-
const filePath =
|
|
6015
|
+
const filePath = join13(this.dir, fileName);
|
|
5688
6016
|
const resolvedPath = resolve4(filePath);
|
|
5689
6017
|
const resolvedDir = resolve4(this.dir);
|
|
5690
6018
|
if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep2)) {
|
|
5691
6019
|
throw new Error("Invalid file path");
|
|
5692
6020
|
}
|
|
5693
|
-
await
|
|
6021
|
+
await writeFile11(filePath, buffer, { mode: 384 });
|
|
5694
6022
|
const meta = { imageId, filePath, mimeType, originalName, createdAt: Date.now(), contextId };
|
|
5695
|
-
await
|
|
6023
|
+
await writeFile11(`${filePath}.meta.json`, JSON.stringify(meta), { encoding: "utf-8", mode: 384 });
|
|
5696
6024
|
return meta;
|
|
5697
6025
|
}
|
|
5698
6026
|
/**
|
|
@@ -5710,7 +6038,7 @@ var ImageStorageService = class {
|
|
|
5710
6038
|
const files = await readdir4(this.dir);
|
|
5711
6039
|
const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
|
|
5712
6040
|
if (!metaFile) return null;
|
|
5713
|
-
const raw = JSON.parse(await
|
|
6041
|
+
const raw = JSON.parse(await readFile12(join13(this.dir, metaFile), "utf-8"));
|
|
5714
6042
|
if (expectedContextId !== void 0 && raw.contextId !== expectedContextId) return null;
|
|
5715
6043
|
if (!existsSync(raw.filePath)) return null;
|
|
5716
6044
|
return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };
|
|
@@ -5736,7 +6064,7 @@ var ImageStorageService = class {
|
|
|
5736
6064
|
await Promise.all(
|
|
5737
6065
|
files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
|
|
5738
6066
|
try {
|
|
5739
|
-
const raw = JSON.parse(await
|
|
6067
|
+
const raw = JSON.parse(await readFile12(join13(this.dir, file), "utf-8"));
|
|
5740
6068
|
if (raw.contextId === contextId) {
|
|
5741
6069
|
await unlink2(raw.filePath).catch(() => {
|
|
5742
6070
|
});
|
|
@@ -5751,7 +6079,7 @@ var ImageStorageService = class {
|
|
|
5751
6079
|
async readImageAsBase64(imageId, expectedContextId) {
|
|
5752
6080
|
const result = await this.getImagePath(imageId, expectedContextId);
|
|
5753
6081
|
if (!result) return null;
|
|
5754
|
-
const buffer = await
|
|
6082
|
+
const buffer = await readFile12(result.filePath);
|
|
5755
6083
|
return { base64: buffer.toString("base64"), mimeType: result.mimeType };
|
|
5756
6084
|
}
|
|
5757
6085
|
async listExpiredImages(ttlMs = IMAGE_TTL_MS) {
|
|
@@ -5766,7 +6094,7 @@ var ImageStorageService = class {
|
|
|
5766
6094
|
for (const file of files) {
|
|
5767
6095
|
if (!file.endsWith(".meta.json")) continue;
|
|
5768
6096
|
try {
|
|
5769
|
-
const raw = JSON.parse(await
|
|
6097
|
+
const raw = JSON.parse(await readFile12(join13(this.dir, file), "utf-8"));
|
|
5770
6098
|
if (now - raw.createdAt > ttlMs) expired.push(raw);
|
|
5771
6099
|
} catch {
|
|
5772
6100
|
}
|
|
@@ -5838,10 +6166,10 @@ import { basename } from "path";
|
|
|
5838
6166
|
import { TRPCError as TRPCError10 } from "@trpc/server";
|
|
5839
6167
|
|
|
5840
6168
|
// ../server/src/services/cloud-session-store.ts
|
|
5841
|
-
import { readFile as
|
|
5842
|
-
import { dirname as
|
|
6169
|
+
import { readFile as readFile13, writeFile as writeFile12, rename as rename10, mkdir as mkdir13, chmod as chmod9, unlink as unlink3 } from "fs/promises";
|
|
6170
|
+
import { dirname as dirname7 } from "path";
|
|
5843
6171
|
import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
5844
|
-
var
|
|
6172
|
+
var FILENAME5 = "cloud-session.json";
|
|
5845
6173
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
5846
6174
|
var CloudSessionStore = class {
|
|
5847
6175
|
session = null;
|
|
@@ -5853,11 +6181,11 @@ var CloudSessionStore = class {
|
|
|
5853
6181
|
* root, but a launch directory owning its own key keeps both together.
|
|
5854
6182
|
*/
|
|
5855
6183
|
getFilePath() {
|
|
5856
|
-
return keyedFilePath(
|
|
6184
|
+
return keyedFilePath(FILENAME5);
|
|
5857
6185
|
}
|
|
5858
6186
|
/** Where older versions kept it — read only, so an older Runeya keeps working. */
|
|
5859
6187
|
getLegacyFilePath() {
|
|
5860
|
-
return legacyMachineFilePath(
|
|
6188
|
+
return legacyMachineFilePath(FILENAME5);
|
|
5861
6189
|
}
|
|
5862
6190
|
/**
|
|
5863
6191
|
* Read the machine file, falling back to the launch directory of old.
|
|
@@ -5867,9 +6195,9 @@ var CloudSessionStore = class {
|
|
|
5867
6195
|
*/
|
|
5868
6196
|
async readFromDisk() {
|
|
5869
6197
|
try {
|
|
5870
|
-
return await
|
|
6198
|
+
return await readFile13(await this.getFilePath(), "utf-8");
|
|
5871
6199
|
} catch {
|
|
5872
|
-
return
|
|
6200
|
+
return readFile13(this.getLegacyFilePath(), "utf-8");
|
|
5873
6201
|
}
|
|
5874
6202
|
}
|
|
5875
6203
|
async load() {
|
|
@@ -5916,12 +6244,12 @@ var CloudSessionStore = class {
|
|
|
5916
6244
|
}
|
|
5917
6245
|
}
|
|
5918
6246
|
async save() {
|
|
5919
|
-
await
|
|
6247
|
+
await mkdir13(dirname7(await this.getFilePath()), { recursive: true });
|
|
5920
6248
|
const filePath = await this.getFilePath();
|
|
5921
6249
|
const tmpPath = filePath + ".tmp";
|
|
5922
|
-
await
|
|
5923
|
-
await
|
|
5924
|
-
await
|
|
6250
|
+
await writeFile12(tmpPath, JSON.stringify(this.session, null, 2), "utf-8");
|
|
6251
|
+
await rename10(tmpPath, filePath);
|
|
6252
|
+
await chmod9(filePath, 384);
|
|
5925
6253
|
}
|
|
5926
6254
|
/** Store the session token obtained from the one-time-token exchange. */
|
|
5927
6255
|
async set(cloudUrl, token) {
|
|
@@ -5939,6 +6267,18 @@ var CloudSessionStore = class {
|
|
|
5939
6267
|
return null;
|
|
5940
6268
|
}
|
|
5941
6269
|
}
|
|
6270
|
+
/**
|
|
6271
|
+
* The cloud this machine is signed into, or null when not linked.
|
|
6272
|
+
*
|
|
6273
|
+
* Needed to tell "an org I am not a member of" apart from "an org of another
|
|
6274
|
+
* cloud instance entirely" — the ids of two clouds share no namespace, so
|
|
6275
|
+
* comparing them without this would call every foreign-cloud project a
|
|
6276
|
+
* permission problem.
|
|
6277
|
+
*/
|
|
6278
|
+
async getCloudUrl() {
|
|
6279
|
+
await this.load();
|
|
6280
|
+
return this.session?.cloudUrl ?? null;
|
|
6281
|
+
}
|
|
5942
6282
|
async isLinked() {
|
|
5943
6283
|
return await this.getToken() !== null;
|
|
5944
6284
|
}
|
|
@@ -6014,9 +6354,10 @@ var cloudAuthRouter = router({
|
|
|
6014
6354
|
const url = `${getCloudAppUrl()}/link-local?callback=${encodeURIComponent(callback)}&state=${encodeURIComponent(state)}`;
|
|
6015
6355
|
return { url };
|
|
6016
6356
|
}),
|
|
6017
|
-
/** Whether this machine holds a cloud session. */
|
|
6357
|
+
/** Whether this machine holds a cloud session, and against which cloud. */
|
|
6018
6358
|
status: protectedProcedure.query(async () => {
|
|
6019
|
-
|
|
6359
|
+
const linked = await cloudSessionStore.isLinked();
|
|
6360
|
+
return { linked, cloudUrl: linked ? await cloudSessionStore.getCloudUrl() : null };
|
|
6020
6361
|
}),
|
|
6021
6362
|
/** The cloud session (user + org), fetched with the stored token. */
|
|
6022
6363
|
session: protectedProcedure.query(async () => {
|
|
@@ -6566,12 +6907,12 @@ function tailTranscript(opts) {
|
|
|
6566
6907
|
const poll = setInterval(() => {
|
|
6567
6908
|
void readMore();
|
|
6568
6909
|
}, 500);
|
|
6569
|
-
await new Promise((
|
|
6910
|
+
await new Promise((resolve7) => {
|
|
6570
6911
|
const tick = setInterval(() => {
|
|
6571
6912
|
if (stopped) {
|
|
6572
6913
|
clearInterval(tick);
|
|
6573
6914
|
clearInterval(poll);
|
|
6574
|
-
|
|
6915
|
+
resolve7();
|
|
6575
6916
|
}
|
|
6576
6917
|
}, 100);
|
|
6577
6918
|
});
|
|
@@ -6590,7 +6931,7 @@ function tailTranscript(opts) {
|
|
|
6590
6931
|
|
|
6591
6932
|
// ../server/src/services/ai-runners/claude-pty-runner.ts
|
|
6592
6933
|
async function getAgentManager() {
|
|
6593
|
-
const { agentManager: agentManager2 } = await import("./agent-manager-
|
|
6934
|
+
const { agentManager: agentManager2 } = await import("./agent-manager-JJYZDLNB.js");
|
|
6594
6935
|
return agentManager2;
|
|
6595
6936
|
}
|
|
6596
6937
|
function buildPtyArgs(params, sessionId, opts) {
|
|
@@ -6603,7 +6944,7 @@ ${apiDocs}` : systemContext;
|
|
|
6603
6944
|
const systemPrompt = skipTokenInstructions ? fullSystemContext : `${fullSystemContext}
|
|
6604
6945
|
|
|
6605
6946
|
${TOKEN_INSTRUCTIONS}`;
|
|
6606
|
-
const mcpArgs = mcpConfig && !opts.excludeMcp ? ["--mcp-config", mcpConfig] : [];
|
|
6947
|
+
const mcpArgs = mcpConfig && !opts.excludeMcp ? ["--mcp-config", mcpConfig, "--strict-mcp-config"] : [];
|
|
6607
6948
|
const args = [
|
|
6608
6949
|
...sessionArg,
|
|
6609
6950
|
"--dangerously-skip-permissions",
|
|
@@ -6669,7 +7010,7 @@ async function runClaudeViaPty(params) {
|
|
|
6669
7010
|
return a;
|
|
6670
7011
|
});
|
|
6671
7012
|
params.onCommand?.(`claude ${displayArgs.join(" ")}`);
|
|
6672
|
-
return new Promise((
|
|
7013
|
+
return new Promise((resolve7, reject) => {
|
|
6673
7014
|
let settled = false;
|
|
6674
7015
|
let spawnId = null;
|
|
6675
7016
|
const state = { assistantText: "", tools: /* @__PURE__ */ new Map() };
|
|
@@ -6692,7 +7033,7 @@ async function runClaudeViaPty(params) {
|
|
|
6692
7033
|
settled = true;
|
|
6693
7034
|
cleanup();
|
|
6694
7035
|
void killSpawn();
|
|
6695
|
-
|
|
7036
|
+
resolve7(result);
|
|
6696
7037
|
};
|
|
6697
7038
|
const fail = (err) => {
|
|
6698
7039
|
if (settled) return;
|
|
@@ -6897,7 +7238,7 @@ ${apiDocs}` : systemContext;
|
|
|
6897
7238
|
|
|
6898
7239
|
${TOKEN_INSTRUCTIONS}`;
|
|
6899
7240
|
const inputArgs = hasImages ? ["--input-format", "stream-json"] : ["-p", prompt];
|
|
6900
|
-
const mcpArgs = mcpConfig && !opts?.excludeMcp ? ["--mcp-config", mcpConfig] : [];
|
|
7241
|
+
const mcpArgs = mcpConfig && !opts?.excludeMcp ? ["--mcp-config", mcpConfig, "--strict-mcp-config"] : [];
|
|
6901
7242
|
const args = [
|
|
6902
7243
|
...sessionArg,
|
|
6903
7244
|
"--output-format",
|
|
@@ -6996,7 +7337,7 @@ var ClaudeCodeRunner = class {
|
|
|
6996
7337
|
runLocal(params) {
|
|
6997
7338
|
const { signal, skipTokenInstructions } = params;
|
|
6998
7339
|
const { args, env: env2, stdinData, sessionArg, hasImages: _hasImages } = buildClaudeSpawnConfig(params);
|
|
6999
|
-
return new Promise((
|
|
7340
|
+
return new Promise((resolve7, reject) => {
|
|
7000
7341
|
const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);
|
|
7001
7342
|
console.log(`[claude-code-runner] arg size: ${totalArgSize} bytes (${(totalArgSize / 1024 / 1024).toFixed(2)} MB), session: ${sessionArg.join(" ")}, images: ${params.images?.length ?? 0}`);
|
|
7002
7343
|
const displayArgs = args.map((a) => {
|
|
@@ -7038,7 +7379,7 @@ var ClaudeCodeRunner = class {
|
|
|
7038
7379
|
});
|
|
7039
7380
|
proc.on("close", (code) => {
|
|
7040
7381
|
if (ctx.earlyResult) {
|
|
7041
|
-
|
|
7382
|
+
resolve7({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId });
|
|
7042
7383
|
return;
|
|
7043
7384
|
}
|
|
7044
7385
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -7055,7 +7396,7 @@ var ClaudeCodeRunner = class {
|
|
|
7055
7396
|
return;
|
|
7056
7397
|
}
|
|
7057
7398
|
const { status, message, branchChoice } = skipTokenInstructions ? { status: "none", message: "", branchChoice: void 0 } : parseEndToken(streamState.fullContent);
|
|
7058
|
-
|
|
7399
|
+
resolve7({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });
|
|
7059
7400
|
});
|
|
7060
7401
|
proc.on("error", (err) => {
|
|
7061
7402
|
if (ctx.settled) return;
|
|
@@ -7111,12 +7452,12 @@ var CodexAppServerClient = class extends EventEmitter3 {
|
|
|
7111
7452
|
if (forceId !== void 0 && forceId >= this.nextId) {
|
|
7112
7453
|
this.nextId = forceId + 1;
|
|
7113
7454
|
}
|
|
7114
|
-
return new Promise((
|
|
7455
|
+
return new Promise((resolve7, reject) => {
|
|
7115
7456
|
const timer = setTimeout(() => {
|
|
7116
7457
|
this.pending.delete(id);
|
|
7117
7458
|
reject(new Error(`Request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`));
|
|
7118
7459
|
}, this.requestTimeoutMs);
|
|
7119
|
-
this.pending.set(id, { resolve:
|
|
7460
|
+
this.pending.set(id, { resolve: resolve7, reject, timer });
|
|
7120
7461
|
this.writeLine({ jsonrpc: "2.0", method, id, params: params ?? {} });
|
|
7121
7462
|
});
|
|
7122
7463
|
}
|
|
@@ -7148,8 +7489,8 @@ var CodexAppServerClient = class extends EventEmitter3 {
|
|
|
7148
7489
|
this.rl = null;
|
|
7149
7490
|
const proc = this.proc;
|
|
7150
7491
|
this.proc = null;
|
|
7151
|
-
return new Promise((
|
|
7152
|
-
proc.on("exit", () =>
|
|
7492
|
+
return new Promise((resolve7) => {
|
|
7493
|
+
proc.on("exit", () => resolve7());
|
|
7153
7494
|
proc.kill("SIGTERM");
|
|
7154
7495
|
const forceTimer = setTimeout(() => {
|
|
7155
7496
|
proc.kill("SIGKILL");
|
|
@@ -7243,7 +7584,7 @@ async function shutdownCodexAppServer() {
|
|
|
7243
7584
|
}
|
|
7244
7585
|
}
|
|
7245
7586
|
async function getAgentManager2() {
|
|
7246
|
-
const { agentManager: agentManager2 } = await import("./agent-manager-
|
|
7587
|
+
const { agentManager: agentManager2 } = await import("./agent-manager-JJYZDLNB.js");
|
|
7247
7588
|
return agentManager2;
|
|
7248
7589
|
}
|
|
7249
7590
|
function parseRuneyaCurlCommand(command) {
|
|
@@ -7332,7 +7673,7 @@ var CodexRunner = class {
|
|
|
7332
7673
|
method: "thread/start",
|
|
7333
7674
|
params: {
|
|
7334
7675
|
model: model || void 0,
|
|
7335
|
-
cwd: process.cwd(),
|
|
7676
|
+
cwd: params.cwd ?? process.cwd(),
|
|
7336
7677
|
approvalPolicy: "never",
|
|
7337
7678
|
sandbox: "danger-full-access"
|
|
7338
7679
|
},
|
|
@@ -7358,7 +7699,7 @@ ${prompt}`;
|
|
|
7358
7699
|
}
|
|
7359
7700
|
input.push({ type: "text", text: fullPrompt });
|
|
7360
7701
|
const turnRef = { id: null };
|
|
7361
|
-
return new Promise((
|
|
7702
|
+
return new Promise((resolve7, reject) => {
|
|
7362
7703
|
let settled = false;
|
|
7363
7704
|
let fullContent = "";
|
|
7364
7705
|
let thinkingEmitted = false;
|
|
@@ -7407,7 +7748,7 @@ ${prompt}`;
|
|
|
7407
7748
|
params.onDelta(delta);
|
|
7408
7749
|
const { status, message, branchChoice } = parseEndToken(fullContent);
|
|
7409
7750
|
if (status !== "none") {
|
|
7410
|
-
settle(() =>
|
|
7751
|
+
settle(() => resolve7({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
|
|
7411
7752
|
}
|
|
7412
7753
|
}
|
|
7413
7754
|
} else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
|
|
@@ -7481,7 +7822,7 @@ ${prompt}`;
|
|
|
7481
7822
|
return;
|
|
7482
7823
|
}
|
|
7483
7824
|
const { status, message, branchChoice } = parseEndToken(fullContent);
|
|
7484
|
-
settle(() =>
|
|
7825
|
+
settle(() => resolve7({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
|
|
7485
7826
|
} else if (method === "turn/errored") {
|
|
7486
7827
|
const error = p["error"];
|
|
7487
7828
|
const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
|
|
@@ -7524,7 +7865,7 @@ ${prompt}`;
|
|
|
7524
7865
|
} else {
|
|
7525
7866
|
const res = await client.sendRequest("thread/start", {
|
|
7526
7867
|
model: model || void 0,
|
|
7527
|
-
cwd: process.cwd(),
|
|
7868
|
+
cwd: params.cwd ?? process.cwd(),
|
|
7528
7869
|
approvalPolicy: "never",
|
|
7529
7870
|
sandbox: "danger-full-access"
|
|
7530
7871
|
});
|
|
@@ -7556,7 +7897,7 @@ ${prompt}`;
|
|
|
7556
7897
|
sandboxPolicy: { type: "dangerFullAccess" }
|
|
7557
7898
|
});
|
|
7558
7899
|
const turnId = turnRes.turn.id;
|
|
7559
|
-
return new Promise((
|
|
7900
|
+
return new Promise((resolve7, reject) => {
|
|
7560
7901
|
let settled = false;
|
|
7561
7902
|
let fullContent = "";
|
|
7562
7903
|
let thinkingEmitted = false;
|
|
@@ -7582,7 +7923,7 @@ ${prompt}`;
|
|
|
7582
7923
|
params.onDelta(delta);
|
|
7583
7924
|
const { status, message, branchChoice } = parseEndToken(fullContent);
|
|
7584
7925
|
if (status !== "none") {
|
|
7585
|
-
settle(() =>
|
|
7926
|
+
settle(() => resolve7({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
|
|
7586
7927
|
}
|
|
7587
7928
|
}
|
|
7588
7929
|
} else if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
|
|
@@ -7656,7 +7997,7 @@ ${prompt}`;
|
|
|
7656
7997
|
return;
|
|
7657
7998
|
}
|
|
7658
7999
|
const { status, message, branchChoice } = parseEndToken(fullContent);
|
|
7659
|
-
settle(() =>
|
|
8000
|
+
settle(() => resolve7({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));
|
|
7660
8001
|
} else if (method === "turn/errored") {
|
|
7661
8002
|
const error = p["error"];
|
|
7662
8003
|
const msg = typeof error?.["message"] === "string" ? error["message"] : "Codex turn errored";
|
|
@@ -7982,6 +8323,8 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
|
|
|
7982
8323
|
const injectedEnv = await resolveInjectedEnv(opts.environmentId);
|
|
7983
8324
|
const done = new ClaudeCodeRunner().run({
|
|
7984
8325
|
agentId: resolvedAgentId ?? void 0,
|
|
8326
|
+
// The conversation's project, not the server's launch directory.
|
|
8327
|
+
cwd: await conversationCwd(conv),
|
|
7985
8328
|
sessionId: conv.id,
|
|
7986
8329
|
prompt,
|
|
7987
8330
|
isResume,
|
|
@@ -8044,6 +8387,7 @@ async function startCodexSession(convId, prompt, opts = {}) {
|
|
|
8044
8387
|
const injectedEnv = await resolveInjectedEnv(opts.environmentId);
|
|
8045
8388
|
const done = new CodexRunner().run({
|
|
8046
8389
|
agentId: resolvedAgentId ?? void 0,
|
|
8390
|
+
cwd: await conversationCwd(conv),
|
|
8047
8391
|
sessionId: conv.codexThreadId ?? "",
|
|
8048
8392
|
prompt,
|
|
8049
8393
|
isResume,
|
|
@@ -8094,19 +8438,24 @@ var StoredMessageSchema = z15.object({
|
|
|
8094
8438
|
content: z15.string().max(1e5),
|
|
8095
8439
|
parts: z15.array(MessagePartSchema).max(200).optional()
|
|
8096
8440
|
});
|
|
8441
|
+
async function conversationCwd(conv) {
|
|
8442
|
+
const ref = await conversationStore.projectRefOf(conv);
|
|
8443
|
+
if (!ref) return resolveAgentCwd({});
|
|
8444
|
+
return resolveAgentCwd({ projectId: ref.id, orgId: ref.orgId });
|
|
8445
|
+
}
|
|
8097
8446
|
var chatRouter = router({
|
|
8098
8447
|
conversations: router({
|
|
8099
8448
|
onChanged: protectedProcedure.subscription(async function* () {
|
|
8100
8449
|
const initial = await conversationStore.list();
|
|
8101
8450
|
yield { type: "initial", conversations: initial };
|
|
8102
8451
|
const queue = [];
|
|
8103
|
-
let
|
|
8452
|
+
let resolve7 = null;
|
|
8104
8453
|
const handler = (event) => {
|
|
8105
8454
|
if (queue.length >= 500) queue.shift();
|
|
8106
8455
|
queue.push(event);
|
|
8107
|
-
if (
|
|
8108
|
-
|
|
8109
|
-
|
|
8456
|
+
if (resolve7) {
|
|
8457
|
+
resolve7();
|
|
8458
|
+
resolve7 = null;
|
|
8110
8459
|
}
|
|
8111
8460
|
};
|
|
8112
8461
|
conversationStore.onChanged(handler);
|
|
@@ -8116,7 +8465,7 @@ var chatRouter = router({
|
|
|
8116
8465
|
yield queue.shift();
|
|
8117
8466
|
} else {
|
|
8118
8467
|
await new Promise((r) => {
|
|
8119
|
-
|
|
8468
|
+
resolve7 = r;
|
|
8120
8469
|
});
|
|
8121
8470
|
}
|
|
8122
8471
|
}
|
|
@@ -8154,7 +8503,8 @@ var chatRouter = router({
|
|
|
8154
8503
|
kanbanCardId: Id128Schema.optional(),
|
|
8155
8504
|
// The project the conversation is about: only the caller knows which
|
|
8156
8505
|
// one it is looking at.
|
|
8157
|
-
projectId: Id128Schema.optional()
|
|
8506
|
+
projectId: Id128Schema.optional(),
|
|
8507
|
+
orgId: z15.string().max(128).nullable().optional()
|
|
8158
8508
|
})).mutation(async ({ input }) => {
|
|
8159
8509
|
let scenario = null;
|
|
8160
8510
|
if (input.scenarioId) {
|
|
@@ -8170,7 +8520,8 @@ var chatRouter = router({
|
|
|
8170
8520
|
input.kanbanBoardId,
|
|
8171
8521
|
input.kanbanCardId,
|
|
8172
8522
|
input.providerId,
|
|
8173
|
-
input.projectId
|
|
8523
|
+
input.projectId,
|
|
8524
|
+
input.orgId
|
|
8174
8525
|
);
|
|
8175
8526
|
if (scenario) {
|
|
8176
8527
|
await scenarioRunStore.initializeConversationWorkspace(
|
|
@@ -8397,6 +8748,7 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
|
|
|
8397
8748
|
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
8398
8749
|
new ClaudeCodeRunner().run({
|
|
8399
8750
|
agentId: resolvedAgentId ?? void 0,
|
|
8751
|
+
cwd: await conversationCwd(conv),
|
|
8400
8752
|
sessionId: conv.id,
|
|
8401
8753
|
prompt: promptWithImages,
|
|
8402
8754
|
isResume,
|
|
@@ -8497,6 +8849,7 @@ ${input.prompt}` : input.prompt;
|
|
|
8497
8849
|
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
8498
8850
|
new CodexRunner().run({
|
|
8499
8851
|
agentId: resolvedAgentId ?? void 0,
|
|
8852
|
+
cwd: await conversationCwd(conv),
|
|
8500
8853
|
sessionId: conv.codexThreadId ?? "",
|
|
8501
8854
|
prompt: cliPrompt,
|
|
8502
8855
|
isResume,
|
|
@@ -8664,6 +9017,10 @@ ${input.prompt}` : input.prompt;
|
|
|
8664
9017
|
resume: z15.boolean().default(false),
|
|
8665
9018
|
model: z15.string().max(128).optional(),
|
|
8666
9019
|
cwd: z15.string().max(4096).optional(),
|
|
9020
|
+
/** Anchors the session when no explicit cwd is given. */
|
|
9021
|
+
serviceId: z15.string().max(128).optional(),
|
|
9022
|
+
projectId: Id128Schema.optional(),
|
|
9023
|
+
orgId: z15.string().max(128).nullable().optional(),
|
|
8667
9024
|
cols: z15.number().int().min(1).max(1e3).default(120),
|
|
8668
9025
|
rows: z15.number().int().min(1).max(1e3).default(30),
|
|
8669
9026
|
enableMcp: z15.boolean().default(false),
|
|
@@ -8674,6 +9031,10 @@ ${input.prompt}` : input.prompt;
|
|
|
8674
9031
|
text: z15.string().max(1e6)
|
|
8675
9032
|
})).max(20).optional()
|
|
8676
9033
|
})).mutation(async ({ input, ctx }) => {
|
|
9034
|
+
const t0 = Date.now();
|
|
9035
|
+
const lap = (label) => {
|
|
9036
|
+
if (process.env.RUNEYA_DEBUG) console.log("[chat.pty.start] %s +%dms", label, Date.now() - t0);
|
|
9037
|
+
};
|
|
8677
9038
|
const agentId = input.agentId ?? agentManager.getDefaultAgentId();
|
|
8678
9039
|
if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
|
|
8679
9040
|
if (input.sessionId) {
|
|
@@ -8690,11 +9051,13 @@ ${input.prompt}` : input.prompt;
|
|
|
8690
9051
|
rows: input.rows
|
|
8691
9052
|
}).catch(() => {
|
|
8692
9053
|
});
|
|
9054
|
+
lap("reattached to existing pty");
|
|
8693
9055
|
return { spawnId: existing.spawnId, agentId, reattached: true };
|
|
8694
9056
|
}
|
|
8695
9057
|
} catch {
|
|
8696
9058
|
}
|
|
8697
9059
|
}
|
|
9060
|
+
lap("findPtyBySession (no reattach)");
|
|
8698
9061
|
const isCodex = input.runner === "codex";
|
|
8699
9062
|
const cli = isCodex ? "codex" : "claude";
|
|
8700
9063
|
const args = [];
|
|
@@ -8734,8 +9097,10 @@ ${apiDocs}` : systemContext;
|
|
|
8734
9097
|
} catch (err) {
|
|
8735
9098
|
console.warn("[chat.pty.start] buildCliSystemContext failed:", err.message);
|
|
8736
9099
|
}
|
|
9100
|
+
lap("buildCliSystemContext");
|
|
8737
9101
|
}
|
|
8738
9102
|
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
9103
|
+
lap("resolveInjectedEnv");
|
|
8739
9104
|
const res = await agentManager.agentMutation(
|
|
8740
9105
|
agentId,
|
|
8741
9106
|
"ai.spawnPty",
|
|
@@ -8743,7 +9108,13 @@ ${apiDocs}` : systemContext;
|
|
|
8743
9108
|
cli,
|
|
8744
9109
|
args,
|
|
8745
9110
|
env: injectedEnv,
|
|
8746
|
-
cwd
|
|
9111
|
+
// An explicit cwd wins; otherwise the service's directory, then the
|
|
9112
|
+
// project's — never the directory the server was launched in.
|
|
9113
|
+
cwd: input.cwd ?? await resolveAgentCwd({
|
|
9114
|
+
serviceId: input.serviceId,
|
|
9115
|
+
projectId: input.projectId,
|
|
9116
|
+
orgId: input.orgId
|
|
9117
|
+
}),
|
|
8747
9118
|
cols: input.cols,
|
|
8748
9119
|
rows: input.rows,
|
|
8749
9120
|
sessionId: input.sessionId,
|
|
@@ -8752,6 +9123,7 @@ ${apiDocs}` : systemContext;
|
|
|
8752
9123
|
mcpApiToken: jwt
|
|
8753
9124
|
}
|
|
8754
9125
|
);
|
|
9126
|
+
lap("agent ai.spawnPty");
|
|
8755
9127
|
const contexts = input.attachedContexts ?? [];
|
|
8756
9128
|
if (contexts.length > 0) {
|
|
8757
9129
|
const body = contexts.map((c) => `### ${c.label}
|
|
@@ -8818,25 +9190,31 @@ Context:
|
|
|
8818
9190
|
})).subscription(async function* ({ input, signal }) {
|
|
8819
9191
|
const agentId = input.agentId ?? agentManager.getDefaultAgentId();
|
|
8820
9192
|
if (!agentId) throw new TRPCError11({ code: "PRECONDITION_FAILED", message: "No agent available" });
|
|
9193
|
+
const tSub = Date.now();
|
|
9194
|
+
let firstChunk = true;
|
|
8821
9195
|
const queue = [];
|
|
8822
|
-
let
|
|
9196
|
+
let resolve7 = null;
|
|
8823
9197
|
let stopped = false;
|
|
8824
9198
|
const unsub = agentManager.agentSubscribe(
|
|
8825
9199
|
agentId,
|
|
8826
9200
|
"ai.stream",
|
|
8827
9201
|
{ spawnId: input.spawnId, fromOffset: input.fromOffset },
|
|
8828
9202
|
(data) => {
|
|
9203
|
+
if (firstChunk) {
|
|
9204
|
+
firstChunk = false;
|
|
9205
|
+
if (process.env.RUNEYA_DEBUG) console.log("[chat.pty.stream] first chunk +%dms", Date.now() - tSub);
|
|
9206
|
+
}
|
|
8829
9207
|
queue.push(data);
|
|
8830
|
-
if (
|
|
8831
|
-
|
|
8832
|
-
|
|
9208
|
+
if (resolve7) {
|
|
9209
|
+
resolve7();
|
|
9210
|
+
resolve7 = null;
|
|
8833
9211
|
}
|
|
8834
9212
|
},
|
|
8835
9213
|
() => {
|
|
8836
9214
|
stopped = true;
|
|
8837
|
-
if (
|
|
8838
|
-
|
|
8839
|
-
|
|
9215
|
+
if (resolve7) {
|
|
9216
|
+
resolve7();
|
|
9217
|
+
resolve7 = null;
|
|
8840
9218
|
}
|
|
8841
9219
|
}
|
|
8842
9220
|
);
|
|
@@ -8852,7 +9230,7 @@ Context:
|
|
|
8852
9230
|
}
|
|
8853
9231
|
if (stopped) break;
|
|
8854
9232
|
await new Promise((r) => {
|
|
8855
|
-
|
|
9233
|
+
resolve7 = r;
|
|
8856
9234
|
});
|
|
8857
9235
|
}
|
|
8858
9236
|
} finally {
|
|
@@ -9003,7 +9381,9 @@ Context:
|
|
|
9003
9381
|
{
|
|
9004
9382
|
cli: "node",
|
|
9005
9383
|
args: [scriptPath],
|
|
9006
|
-
|
|
9384
|
+
// Was `process.cwd()` — the SERVER's directory, handed to an agent
|
|
9385
|
+
// that may not even run on this machine.
|
|
9386
|
+
cwd: await resolveWorkspaceCwd({}),
|
|
9007
9387
|
cols: input.cols,
|
|
9008
9388
|
rows: input.rows,
|
|
9009
9389
|
sessionId: stressSessionId,
|
|
@@ -9050,11 +9430,15 @@ async function resolveServiceContext(serviceId) {
|
|
|
9050
9430
|
if (!agentId) {
|
|
9051
9431
|
throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent configured for this service" });
|
|
9052
9432
|
}
|
|
9053
|
-
return { agentId, cwd:
|
|
9433
|
+
return { agentId, cwd: await resolveWorkspaceCwd({ serviceId }) };
|
|
9054
9434
|
}
|
|
9055
9435
|
var gitTargetShape = {
|
|
9056
9436
|
serviceId: SERVICE_ID_SCHEMA.optional(),
|
|
9057
|
-
agentId: z16.string().max(128).optional()
|
|
9437
|
+
agentId: z16.string().max(128).optional(),
|
|
9438
|
+
/** Anchors on a project when no service is targeted (e.g. from the AI chat). */
|
|
9439
|
+
projectId: z16.string().max(128).optional(),
|
|
9440
|
+
/** Son organisation : un id de projet seul peut en nommer un par organisation. */
|
|
9441
|
+
orgId: z16.string().max(128).nullable().optional()
|
|
9058
9442
|
};
|
|
9059
9443
|
async function resolveGitContext(input) {
|
|
9060
9444
|
if (input.serviceId) return resolveServiceContext(input.serviceId);
|
|
@@ -9062,7 +9446,8 @@ async function resolveGitContext(input) {
|
|
|
9062
9446
|
if (!agentId) {
|
|
9063
9447
|
throw new TRPCError12({ code: "BAD_REQUEST", message: "No agent available" });
|
|
9064
9448
|
}
|
|
9065
|
-
|
|
9449
|
+
const cwd = await resolveAgentCwd({ projectId: input.projectId, orgId: input.orgId }) ?? ".";
|
|
9450
|
+
return { agentId, cwd };
|
|
9066
9451
|
}
|
|
9067
9452
|
var gitRouter = router({
|
|
9068
9453
|
/** Get full git status */
|
|
@@ -9223,7 +9608,7 @@ Rules:
|
|
|
9223
9608
|
onStatusChange: protectedProcedure.input(z16.object({ ...gitTargetShape })).subscription(async function* ({ input }) {
|
|
9224
9609
|
const { agentId, cwd } = await resolveGitContext(input);
|
|
9225
9610
|
const queue = [];
|
|
9226
|
-
let
|
|
9611
|
+
let resolve7 = null;
|
|
9227
9612
|
let done = false;
|
|
9228
9613
|
let unsubscribe;
|
|
9229
9614
|
try {
|
|
@@ -9233,16 +9618,16 @@ Rules:
|
|
|
9233
9618
|
{ cwd },
|
|
9234
9619
|
(data) => {
|
|
9235
9620
|
queue.push(data);
|
|
9236
|
-
if (
|
|
9237
|
-
|
|
9238
|
-
|
|
9621
|
+
if (resolve7) {
|
|
9622
|
+
resolve7();
|
|
9623
|
+
resolve7 = null;
|
|
9239
9624
|
}
|
|
9240
9625
|
},
|
|
9241
9626
|
() => {
|
|
9242
9627
|
done = true;
|
|
9243
|
-
if (
|
|
9244
|
-
|
|
9245
|
-
|
|
9628
|
+
if (resolve7) {
|
|
9629
|
+
resolve7();
|
|
9630
|
+
resolve7 = null;
|
|
9246
9631
|
}
|
|
9247
9632
|
}
|
|
9248
9633
|
);
|
|
@@ -9258,7 +9643,7 @@ Rules:
|
|
|
9258
9643
|
yield queue.shift();
|
|
9259
9644
|
} else {
|
|
9260
9645
|
await new Promise((r) => {
|
|
9261
|
-
|
|
9646
|
+
resolve7 = r;
|
|
9262
9647
|
});
|
|
9263
9648
|
}
|
|
9264
9649
|
}
|
|
@@ -9390,9 +9775,9 @@ async function* streamWithActiveProvider(opts) {
|
|
|
9390
9775
|
}
|
|
9391
9776
|
|
|
9392
9777
|
// ../server/src/services/scenario-runner.ts
|
|
9393
|
-
import { mkdir as
|
|
9778
|
+
import { mkdir as mkdir14 } from "fs/promises";
|
|
9394
9779
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
9395
|
-
import { join as
|
|
9780
|
+
import { join as join14 } from "path";
|
|
9396
9781
|
|
|
9397
9782
|
// ../server/src/services/scenario-presets.ts
|
|
9398
9783
|
var BUILT_IN_PRESETS = [
|
|
@@ -9606,21 +9991,21 @@ async function* tapBus(conversationId, signal) {
|
|
|
9606
9991
|
}
|
|
9607
9992
|
if (bus.done) return;
|
|
9608
9993
|
if (signal.aborted) return;
|
|
9609
|
-
await new Promise((
|
|
9994
|
+
await new Promise((resolve7) => {
|
|
9610
9995
|
const onAbort = () => {
|
|
9611
9996
|
bus.listeners.delete(notify);
|
|
9612
|
-
|
|
9997
|
+
resolve7();
|
|
9613
9998
|
};
|
|
9614
9999
|
const notify = () => {
|
|
9615
10000
|
signal.removeEventListener("abort", onAbort);
|
|
9616
|
-
|
|
10001
|
+
resolve7();
|
|
9617
10002
|
};
|
|
9618
10003
|
bus.listeners.add(notify);
|
|
9619
10004
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
9620
10005
|
if (cursor < bus.baseIndex + bus.buffer.length || bus.done || signal.aborted) {
|
|
9621
10006
|
bus.listeners.delete(notify);
|
|
9622
10007
|
signal.removeEventListener("abort", onAbort);
|
|
9623
|
-
|
|
10008
|
+
resolve7();
|
|
9624
10009
|
}
|
|
9625
10010
|
});
|
|
9626
10011
|
}
|
|
@@ -10088,6 +10473,10 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
|
|
|
10088
10473
|
const turnStart = Date.now();
|
|
10089
10474
|
const result = await state.runner.run({
|
|
10090
10475
|
agentId: runOverrides?.agentId ?? agentManager.getDefaultAgentId() ?? void 0,
|
|
10476
|
+
// Without this the CLI would run wherever the server was launched, whatever
|
|
10477
|
+
// project the scenario belongs to. No project: left unset, so the runner
|
|
10478
|
+
// keeps its own default rather than being sent to this machine's path.
|
|
10479
|
+
cwd: await resolveAgentCwd({ projectId: runOverrides?.projectId, orgId: runOverrides?.orgId }),
|
|
10091
10480
|
sessionId: state.sessionId,
|
|
10092
10481
|
prompt: currentPrompt,
|
|
10093
10482
|
isResume: state.isResume,
|
|
@@ -10124,9 +10513,9 @@ async function runNodeCall(conversationId, node, systemContext, userMessage, run
|
|
|
10124
10513
|
publish2({ type: "node_waiting_input", nodeId: node.id, question: result.tokenMessage });
|
|
10125
10514
|
await readme?.(`- \u2753 [${formatTs()}] **${node.label}** \u2014 waiting for user input
|
|
10126
10515
|
`);
|
|
10127
|
-
const answer = await new Promise((
|
|
10516
|
+
const answer = await new Promise((resolve7, reject) => {
|
|
10128
10517
|
const key = pendingNodeKey(conversationId, node.id);
|
|
10129
|
-
pendingNodeInputs.set(key,
|
|
10518
|
+
pendingNodeInputs.set(key, resolve7);
|
|
10130
10519
|
signal.addEventListener("abort", () => {
|
|
10131
10520
|
pendingNodeInputs.delete(key);
|
|
10132
10521
|
reject(new Error("Aborted"));
|
|
@@ -10166,7 +10555,7 @@ async function appendNodeOutput(conversationId, node, output) {
|
|
|
10166
10555
|
if (!dir) return;
|
|
10167
10556
|
const slug = node.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60) || "member";
|
|
10168
10557
|
const safeId = node.id.replace(/[^a-z0-9_-]/gi, "-").slice(0, 80);
|
|
10169
|
-
const filePath =
|
|
10558
|
+
const filePath = join14(dir, `${slug}_${safeId}.md`);
|
|
10170
10559
|
const content = `## Output
|
|
10171
10560
|
|
|
10172
10561
|
${output}
|
|
@@ -10179,9 +10568,9 @@ async function appendToReadme(conversationId, bus, text) {
|
|
|
10179
10568
|
const line = `${text}
|
|
10180
10569
|
`;
|
|
10181
10570
|
try {
|
|
10182
|
-
await
|
|
10571
|
+
await mkdir14(dir, { recursive: true });
|
|
10183
10572
|
const { appendFile: appendFile2 } = await import("fs/promises");
|
|
10184
|
-
await appendFile2(
|
|
10573
|
+
await appendFile2(join14(dir, "README.md"), line, "utf-8");
|
|
10185
10574
|
} catch {
|
|
10186
10575
|
}
|
|
10187
10576
|
publishToBus(bus, { type: "readme_append", text: line });
|
|
@@ -10201,7 +10590,7 @@ async function writeFinalMd(conversationId, bus, userRequest, content) {
|
|
|
10201
10590
|
|
|
10202
10591
|
${content}
|
|
10203
10592
|
`;
|
|
10204
|
-
const filePath =
|
|
10593
|
+
const filePath = join14(dir, "FINAL.md");
|
|
10205
10594
|
await atomicWriteSecure(filePath, fullContent);
|
|
10206
10595
|
await appendToReadme(
|
|
10207
10596
|
conversationId,
|
|
@@ -10531,9 +10920,9 @@ ${outputs.get(node.id) ?? ""}`.trim();
|
|
|
10531
10920
|
publish2({ type: "node_waiting_input", nodeId: node.id, question });
|
|
10532
10921
|
await readme(`- \u2753 [${ts()}] **${node.label || node.id}** \u2014 waiting for user input
|
|
10533
10922
|
`);
|
|
10534
|
-
const answer = await new Promise((
|
|
10923
|
+
const answer = await new Promise((resolve7, reject) => {
|
|
10535
10924
|
const key = pendingNodeKey(conversationId, node.id);
|
|
10536
|
-
pendingNodeInputs.set(key,
|
|
10925
|
+
pendingNodeInputs.set(key, resolve7);
|
|
10537
10926
|
signal.addEventListener("abort", () => {
|
|
10538
10927
|
pendingNodeInputs.delete(key);
|
|
10539
10928
|
reject(new Error("Aborted"));
|
|
@@ -10932,6 +11321,10 @@ var scenarioRouter = router({
|
|
|
10932
11321
|
runnerOverride: z17.enum(["claude-code", "codex"]).optional(),
|
|
10933
11322
|
modelOverride: Id128Schema.optional(),
|
|
10934
11323
|
agentId: Id128Schema.optional(),
|
|
11324
|
+
/** Anchors the run's working directory on the project it belongs to. */
|
|
11325
|
+
projectId: Id128Schema.optional(),
|
|
11326
|
+
/** Son organisation : sans elle, un id partagé désignerait un projet au hasard. */
|
|
11327
|
+
orgId: z17.string().max(128).nullable().optional(),
|
|
10935
11328
|
resume: z17.boolean().optional()
|
|
10936
11329
|
})).subscription(async function* ({ input, signal }) {
|
|
10937
11330
|
const scenario = await scenarioStore.get(input.scenarioId);
|
|
@@ -10945,7 +11338,9 @@ var scenarioRouter = router({
|
|
|
10945
11338
|
void runScenario(input.conversationId, input.scenarioId, userRequest, {
|
|
10946
11339
|
runnerOverride: input.runnerOverride,
|
|
10947
11340
|
modelOverride: input.modelOverride,
|
|
10948
|
-
agentId: input.agentId
|
|
11341
|
+
agentId: input.agentId,
|
|
11342
|
+
projectId: input.projectId,
|
|
11343
|
+
orgId: input.orgId
|
|
10949
11344
|
}, !!input.resume);
|
|
10950
11345
|
await new Promise((r) => setTimeout(r, 10));
|
|
10951
11346
|
}
|
|
@@ -11066,21 +11461,21 @@ async function* tapJob(job, signal) {
|
|
|
11066
11461
|
yield evt;
|
|
11067
11462
|
}
|
|
11068
11463
|
if (job.done || signal.aborted) return;
|
|
11069
|
-
await new Promise((
|
|
11464
|
+
await new Promise((resolve7) => {
|
|
11070
11465
|
const onAbort = () => {
|
|
11071
11466
|
job.listeners.delete(notify);
|
|
11072
|
-
|
|
11467
|
+
resolve7();
|
|
11073
11468
|
};
|
|
11074
11469
|
const notify = () => {
|
|
11075
11470
|
signal.removeEventListener("abort", onAbort);
|
|
11076
|
-
|
|
11471
|
+
resolve7();
|
|
11077
11472
|
};
|
|
11078
11473
|
job.listeners.add(notify);
|
|
11079
11474
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
11080
11475
|
if (cursor < job.baseIndex + job.buffer.length || job.done || signal.aborted) {
|
|
11081
11476
|
job.listeners.delete(notify);
|
|
11082
11477
|
signal.removeEventListener("abort", onAbort);
|
|
11083
|
-
|
|
11478
|
+
resolve7();
|
|
11084
11479
|
}
|
|
11085
11480
|
});
|
|
11086
11481
|
}
|
|
@@ -11352,13 +11747,13 @@ var kanbanQueueRouter = router({
|
|
|
11352
11747
|
}),
|
|
11353
11748
|
onChanged: protectedProcedure.subscription(async function* () {
|
|
11354
11749
|
const queue = [];
|
|
11355
|
-
let
|
|
11750
|
+
let resolve7 = null;
|
|
11356
11751
|
const handler = () => {
|
|
11357
11752
|
if (queue.length >= 50) queue.shift();
|
|
11358
11753
|
queue.push(kanbanQueue.getState());
|
|
11359
|
-
if (
|
|
11360
|
-
|
|
11361
|
-
|
|
11754
|
+
if (resolve7) {
|
|
11755
|
+
resolve7();
|
|
11756
|
+
resolve7 = null;
|
|
11362
11757
|
}
|
|
11363
11758
|
};
|
|
11364
11759
|
kanbanQueue.on("changed", handler);
|
|
@@ -11369,7 +11764,7 @@ var kanbanQueueRouter = router({
|
|
|
11369
11764
|
yield queue.shift();
|
|
11370
11765
|
} else {
|
|
11371
11766
|
await new Promise((r) => {
|
|
11372
|
-
|
|
11767
|
+
resolve7 = r;
|
|
11373
11768
|
});
|
|
11374
11769
|
}
|
|
11375
11770
|
}
|
|
@@ -11380,10 +11775,10 @@ var kanbanQueueRouter = router({
|
|
|
11380
11775
|
});
|
|
11381
11776
|
|
|
11382
11777
|
// ../server/src/trpc/routers/kanban.ts
|
|
11383
|
-
import { mkdir as
|
|
11778
|
+
import { mkdir as mkdir15, readdir as readdir5, rm as rm3, stat as stat4, writeFile as writeFile13 } from "fs/promises";
|
|
11384
11779
|
import { createHash } from "crypto";
|
|
11385
11780
|
import { tmpdir } from "os";
|
|
11386
|
-
import { join as
|
|
11781
|
+
import { join as join15 } from "path";
|
|
11387
11782
|
import { z as z20 } from "zod";
|
|
11388
11783
|
import { TRPCError as TRPCError17 } from "@trpc/server";
|
|
11389
11784
|
function summarizeCard(card, boardId, columnTitle) {
|
|
@@ -11434,30 +11829,30 @@ function safeDecode(value) {
|
|
|
11434
11829
|
}
|
|
11435
11830
|
var MAX_ATTACHMENT_BYTES = 55 * 1024 * 1024;
|
|
11436
11831
|
function attachmentCacheDir() {
|
|
11437
|
-
return
|
|
11832
|
+
return join15(tmpdir(), "runeya-kanban-attachments");
|
|
11438
11833
|
}
|
|
11439
11834
|
var ATTACHMENT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11440
11835
|
async function purgeStaleAttachments(dir, ttlMs = ATTACHMENT_CACHE_TTL_MS) {
|
|
11441
11836
|
try {
|
|
11442
11837
|
const cutoff = Date.now() - ttlMs;
|
|
11443
11838
|
for (const name of await readdir5(dir)) {
|
|
11444
|
-
const path =
|
|
11445
|
-
const info = await
|
|
11839
|
+
const path = join15(dir, name);
|
|
11840
|
+
const info = await stat4(path).catch(() => null);
|
|
11446
11841
|
if (info && info.mtimeMs < cutoff) await rm3(path, { force: true });
|
|
11447
11842
|
}
|
|
11448
11843
|
} catch {
|
|
11449
11844
|
}
|
|
11450
11845
|
}
|
|
11451
11846
|
function attachmentCacheDirs() {
|
|
11452
|
-
return [attachmentCacheDir(),
|
|
11847
|
+
return [attachmentCacheDir(), join15(machineRoot(), "kanban-attachments")];
|
|
11453
11848
|
}
|
|
11454
11849
|
async function storeAttachment(id, name, body, dirs = attachmentCacheDirs()) {
|
|
11455
11850
|
const refusals = [];
|
|
11456
11851
|
for (const dir of dirs) {
|
|
11457
11852
|
try {
|
|
11458
|
-
await
|
|
11459
|
-
const path =
|
|
11460
|
-
await
|
|
11853
|
+
await mkdir15(dir, { recursive: true });
|
|
11854
|
+
const path = join15(dir, safeLocalName(id, name));
|
|
11855
|
+
await writeFile13(path, body);
|
|
11461
11856
|
void purgeStaleAttachments(dir);
|
|
11462
11857
|
return path;
|
|
11463
11858
|
} catch (err) {
|
|
@@ -11644,13 +12039,28 @@ var terminalRouter = router({
|
|
|
11644
12039
|
create: protectedProcedure.input(z21.object({
|
|
11645
12040
|
agentId: z21.string().max(128),
|
|
11646
12041
|
cwd: z21.string().max(512).optional(),
|
|
12042
|
+
/** Anchors the terminal when no explicit cwd is given. */
|
|
12043
|
+
serviceId: z21.string().max(128).optional(),
|
|
12044
|
+
projectId: z21.string().max(128).optional(),
|
|
12045
|
+
/** L'organisation du projet : un id seul peut en nommer un par organisation. */
|
|
12046
|
+
orgId: z21.string().max(128).nullable().optional(),
|
|
11647
12047
|
cols: z21.number().int().min(1).max(512).optional(),
|
|
11648
12048
|
rows: z21.number().int().min(1).max(256).optional()
|
|
11649
12049
|
})).mutation(async ({ input }) => {
|
|
11650
12050
|
const result = await agentManager.agentMutation(
|
|
11651
12051
|
input.agentId,
|
|
11652
12052
|
"terminal.create",
|
|
11653
|
-
{
|
|
12053
|
+
{
|
|
12054
|
+
// An explicit cwd wins; otherwise the service's directory, then the
|
|
12055
|
+
// project's — never the directory the server was launched in.
|
|
12056
|
+
cwd: input.cwd ?? await resolveAgentCwd({
|
|
12057
|
+
serviceId: input.serviceId,
|
|
12058
|
+
projectId: input.projectId,
|
|
12059
|
+
orgId: input.orgId
|
|
12060
|
+
}),
|
|
12061
|
+
cols: input.cols,
|
|
12062
|
+
rows: input.rows
|
|
12063
|
+
}
|
|
11654
12064
|
);
|
|
11655
12065
|
return result;
|
|
11656
12066
|
}),
|
|
@@ -11699,7 +12109,7 @@ var terminalRouter = router({
|
|
|
11699
12109
|
sessionId: z21.string().max(128)
|
|
11700
12110
|
})).subscription(async function* ({ input }) {
|
|
11701
12111
|
const queue = [];
|
|
11702
|
-
let
|
|
12112
|
+
let resolve7 = null;
|
|
11703
12113
|
let done = false;
|
|
11704
12114
|
let unsubscribe;
|
|
11705
12115
|
try {
|
|
@@ -11709,16 +12119,16 @@ var terminalRouter = router({
|
|
|
11709
12119
|
{ sessionId: input.sessionId },
|
|
11710
12120
|
(data) => {
|
|
11711
12121
|
if (typeof data === "string") queue.push(data);
|
|
11712
|
-
if (
|
|
11713
|
-
|
|
11714
|
-
|
|
12122
|
+
if (resolve7) {
|
|
12123
|
+
resolve7();
|
|
12124
|
+
resolve7 = null;
|
|
11715
12125
|
}
|
|
11716
12126
|
},
|
|
11717
12127
|
() => {
|
|
11718
12128
|
done = true;
|
|
11719
|
-
if (
|
|
11720
|
-
|
|
11721
|
-
|
|
12129
|
+
if (resolve7) {
|
|
12130
|
+
resolve7();
|
|
12131
|
+
resolve7 = null;
|
|
11722
12132
|
}
|
|
11723
12133
|
}
|
|
11724
12134
|
);
|
|
@@ -11734,7 +12144,7 @@ var terminalRouter = router({
|
|
|
11734
12144
|
yield queue.shift();
|
|
11735
12145
|
} else {
|
|
11736
12146
|
await new Promise((r) => {
|
|
11737
|
-
|
|
12147
|
+
resolve7 = r;
|
|
11738
12148
|
});
|
|
11739
12149
|
}
|
|
11740
12150
|
}
|
|
@@ -11751,7 +12161,7 @@ var utilityRouter = router({
|
|
|
11751
12161
|
wait: protectedProcedure.input(z22.object({
|
|
11752
12162
|
ms: z22.number().int().min(0).max(MAX_WAIT_MS)
|
|
11753
12163
|
})).mutation(async ({ input }) => {
|
|
11754
|
-
await new Promise((
|
|
12164
|
+
await new Promise((resolve7) => setTimeout(resolve7, input.ms));
|
|
11755
12165
|
return { waited: input.ms };
|
|
11756
12166
|
})
|
|
11757
12167
|
});
|
|
@@ -11759,8 +12169,8 @@ var utilityRouter = router({
|
|
|
11759
12169
|
// ../server/src/trpc/routers/wiki.ts
|
|
11760
12170
|
import { z as z23 } from "zod";
|
|
11761
12171
|
import { TRPCError as TRPCError19 } from "@trpc/server";
|
|
11762
|
-
import { readFile as
|
|
11763
|
-
import { resolve as resolvePath, join as
|
|
12172
|
+
import { readFile as readFile14, readdir as readdir6, stat as stat5, realpath, writeFile as writeFile14, mkdir as mkdir16, rename as rename11, rm as rm4 } from "fs/promises";
|
|
12173
|
+
import { resolve as resolvePath, join as join16, relative, sep as sep3, dirname as dirname8 } from "path";
|
|
11764
12174
|
var SCOPE_SCHEMA = z23.union([
|
|
11765
12175
|
z23.literal("global"),
|
|
11766
12176
|
z23.string().regex(/^service:[A-Za-z0-9_-]{1,128}$/, "Invalid service scope")
|
|
@@ -11797,13 +12207,13 @@ async function resolveScope(scope) {
|
|
|
11797
12207
|
throw new TRPCError19({ code: "BAD_REQUEST", message: "Sous-chemin invalide" });
|
|
11798
12208
|
}
|
|
11799
12209
|
const dataDirAbs = resolvePath(env.DATA_DIR);
|
|
11800
|
-
await
|
|
11801
|
-
const base =
|
|
12210
|
+
await mkdir16(dataDirAbs, { recursive: true });
|
|
12211
|
+
const base = dirname8(await realpath(dataDirAbs));
|
|
11802
12212
|
const target = resolvePath(base, sub);
|
|
11803
12213
|
if (target !== base && !(target + sep3).startsWith(base + sep3)) {
|
|
11804
12214
|
throw new TRPCError19({ code: "FORBIDDEN", message: "Hors du dossier de donn\xE9es du serveur" });
|
|
11805
12215
|
}
|
|
11806
|
-
await
|
|
12216
|
+
await mkdir16(target, { recursive: true });
|
|
11807
12217
|
const root = await realpath(target);
|
|
11808
12218
|
if (root !== base && !(root + sep3).startsWith(base + sep3)) {
|
|
11809
12219
|
throw new TRPCError19({ code: "FORBIDDEN", message: "Le dossier wiki r\xE9sout hors du dossier de donn\xE9es" });
|
|
@@ -11815,7 +12225,7 @@ async function resolveScope(scope) {
|
|
|
11815
12225
|
if (!service) throw new TRPCError19({ code: "NOT_FOUND", message: "Service introuvable" });
|
|
11816
12226
|
const agentId = await resolveEffectiveAgentId(service);
|
|
11817
12227
|
if (!agentId) throw new TRPCError19({ code: "BAD_REQUEST", message: "Aucun agent configur\xE9 pour ce service" });
|
|
11818
|
-
const cwd = (service.docsPath?.trim() ||
|
|
12228
|
+
const cwd = await resolveWorkspaceCwd({ serviceId, override: service.docsPath?.trim() || void 0 });
|
|
11819
12229
|
return { mode: "agent", agentId, cwd };
|
|
11820
12230
|
}
|
|
11821
12231
|
async function walkLocal(root, dir, depth, counter) {
|
|
@@ -11830,7 +12240,7 @@ async function walkLocal(root, dir, depth, counter) {
|
|
|
11830
12240
|
for (const e of entries) {
|
|
11831
12241
|
if (counter.n >= MAX_FILES) break;
|
|
11832
12242
|
if (e.name.startsWith(".") && e.name !== ".") continue;
|
|
11833
|
-
const abs =
|
|
12243
|
+
const abs = join16(dir, e.name);
|
|
11834
12244
|
const rel = relative(root, abs).split(sep3).join("/");
|
|
11835
12245
|
if (e.isDirectory()) {
|
|
11836
12246
|
if (IGNORED_DIRS.has(e.name)) continue;
|
|
@@ -11864,7 +12274,7 @@ async function resolveSafeLocal(root, relPath, requireExist) {
|
|
|
11864
12274
|
}
|
|
11865
12275
|
return real;
|
|
11866
12276
|
}
|
|
11867
|
-
const parent =
|
|
12277
|
+
const parent = dirname8(target);
|
|
11868
12278
|
let realParent;
|
|
11869
12279
|
try {
|
|
11870
12280
|
realParent = await realpath(parent);
|
|
@@ -11895,10 +12305,10 @@ var wikiRouter = router({
|
|
|
11895
12305
|
return agentManager.agentQuery(target.agentId, "wiki.readRepoMarkdown", { cwd: target.cwd, path: input.path });
|
|
11896
12306
|
}
|
|
11897
12307
|
const file = await resolveSafeLocal(target.root, input.path, true);
|
|
11898
|
-
const s = await
|
|
12308
|
+
const s = await stat5(file);
|
|
11899
12309
|
if (!s.isFile()) throw new TRPCError19({ code: "BAD_REQUEST", message: "Pas un fichier" });
|
|
11900
12310
|
if (s.size > MAX_FILE_BYTES) throw new TRPCError19({ code: "PAYLOAD_TOO_LARGE", message: "Fichier trop volumineux" });
|
|
11901
|
-
const content = await
|
|
12311
|
+
const content = await readFile14(file, "utf-8");
|
|
11902
12312
|
return { path: input.path, content };
|
|
11903
12313
|
}),
|
|
11904
12314
|
write: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA, content: z23.string().max(5e6) })).mutation(async ({ input }) => {
|
|
@@ -11910,8 +12320,8 @@ var wikiRouter = router({
|
|
|
11910
12320
|
return agentManager.agentMutation(target.agentId, "wiki.writeMarkdown", { cwd: target.cwd, path: input.path, content: input.content });
|
|
11911
12321
|
}
|
|
11912
12322
|
const file = await resolveSafeLocal(target.root, input.path, false);
|
|
11913
|
-
await
|
|
11914
|
-
await
|
|
12323
|
+
await mkdir16(dirname8(file), { recursive: true });
|
|
12324
|
+
await writeFile14(file, input.content, "utf-8");
|
|
11915
12325
|
return { path: input.path, ok: true };
|
|
11916
12326
|
}),
|
|
11917
12327
|
createFile: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
@@ -11924,13 +12334,13 @@ var wikiRouter = router({
|
|
|
11924
12334
|
}
|
|
11925
12335
|
const file = await resolveSafeLocal(target.root, input.path, false);
|
|
11926
12336
|
try {
|
|
11927
|
-
await
|
|
12337
|
+
await stat5(file);
|
|
11928
12338
|
throw new TRPCError19({ code: "CONFLICT", message: "Fichier d\xE9j\xE0 existant" });
|
|
11929
12339
|
} catch (err) {
|
|
11930
12340
|
if (err instanceof TRPCError19) throw err;
|
|
11931
12341
|
}
|
|
11932
|
-
await
|
|
11933
|
-
await
|
|
12342
|
+
await mkdir16(dirname8(file), { recursive: true });
|
|
12343
|
+
await writeFile14(file, "", "utf-8");
|
|
11934
12344
|
return { path: input.path, ok: true };
|
|
11935
12345
|
}),
|
|
11936
12346
|
createDir: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
@@ -11939,7 +12349,7 @@ var wikiRouter = router({
|
|
|
11939
12349
|
return agentManager.agentMutation(target.agentId, "wiki.createDir", { cwd: target.cwd, path: input.path });
|
|
11940
12350
|
}
|
|
11941
12351
|
const dir = await resolveSafeLocal(target.root, input.path, false);
|
|
11942
|
-
await
|
|
12352
|
+
await mkdir16(dir, { recursive: true });
|
|
11943
12353
|
return { path: input.path, ok: true };
|
|
11944
12354
|
}),
|
|
11945
12355
|
rename: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, fromPath: REL_PATH_SCHEMA, toPath: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
@@ -11950,13 +12360,13 @@ var wikiRouter = router({
|
|
|
11950
12360
|
const from = await resolveSafeLocal(target.root, input.fromPath, true);
|
|
11951
12361
|
const to = await resolveSafeLocal(target.root, input.toPath, false);
|
|
11952
12362
|
try {
|
|
11953
|
-
await
|
|
12363
|
+
await stat5(to);
|
|
11954
12364
|
throw new TRPCError19({ code: "CONFLICT", message: "La cible existe d\xE9j\xE0" });
|
|
11955
12365
|
} catch (err) {
|
|
11956
12366
|
if (err instanceof TRPCError19) throw err;
|
|
11957
12367
|
}
|
|
11958
|
-
await
|
|
11959
|
-
await
|
|
12368
|
+
await mkdir16(dirname8(to), { recursive: true });
|
|
12369
|
+
await rename11(from, to);
|
|
11960
12370
|
return { fromPath: input.fromPath, toPath: input.toPath, ok: true };
|
|
11961
12371
|
}),
|
|
11962
12372
|
ensureRoot: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA })).mutation(async ({ input }) => {
|
|
@@ -11969,7 +12379,7 @@ var wikiRouter = router({
|
|
|
11969
12379
|
if (!service) throw new TRPCError19({ code: "NOT_FOUND", message: "Service introuvable" });
|
|
11970
12380
|
const agentId = await resolveEffectiveAgentId(service);
|
|
11971
12381
|
if (!agentId) throw new TRPCError19({ code: "BAD_REQUEST", message: "Aucun agent configur\xE9" });
|
|
11972
|
-
const cwd = (service.docsPath?.trim() ||
|
|
12382
|
+
const cwd = await resolveWorkspaceCwd({ serviceId, override: service.docsPath?.trim() || void 0 });
|
|
11973
12383
|
return agentManager.agentMutation(agentId, "wiki.ensureRoot", { cwd });
|
|
11974
12384
|
}),
|
|
11975
12385
|
delete: protectedProcedure.input(z23.object({ scope: SCOPE_SCHEMA, path: REL_PATH_SCHEMA })).mutation(async ({ input }) => {
|
|
@@ -11997,18 +12407,18 @@ import { z as z24 } from "zod";
|
|
|
11997
12407
|
import { TRPCError as TRPCError20 } from "@trpc/server";
|
|
11998
12408
|
|
|
11999
12409
|
// ../server/src/services/cloud-sync-store.ts
|
|
12000
|
-
import { readFile as
|
|
12001
|
-
var
|
|
12410
|
+
import { readFile as readFile15, writeFile as writeFile15, rename as rename12, mkdir as mkdir17, chmod as chmod10 } from "fs/promises";
|
|
12411
|
+
var FILENAME6 = "cloud-sync.json";
|
|
12002
12412
|
var CloudSyncStore = class {
|
|
12003
12413
|
links = /* @__PURE__ */ new Map();
|
|
12004
12414
|
loaded = false;
|
|
12005
12415
|
/** Machine-wide: this describes the machine, not any one project. */
|
|
12006
12416
|
getFilePath() {
|
|
12007
|
-
return machineFilePath(
|
|
12417
|
+
return machineFilePath(FILENAME6);
|
|
12008
12418
|
}
|
|
12009
12419
|
/** Where older versions kept it — read only, so an older Runeya keeps working. */
|
|
12010
12420
|
getLegacyFilePath() {
|
|
12011
|
-
return legacyMachineFilePath(
|
|
12421
|
+
return legacyMachineFilePath(FILENAME6);
|
|
12012
12422
|
}
|
|
12013
12423
|
/**
|
|
12014
12424
|
* Read the machine file, falling back to the launch directory of old.
|
|
@@ -12018,9 +12428,9 @@ var CloudSyncStore = class {
|
|
|
12018
12428
|
*/
|
|
12019
12429
|
async readFromDisk() {
|
|
12020
12430
|
try {
|
|
12021
|
-
return await
|
|
12431
|
+
return await readFile15(this.getFilePath(), "utf-8");
|
|
12022
12432
|
} catch {
|
|
12023
|
-
return
|
|
12433
|
+
return readFile15(this.getLegacyFilePath(), "utf-8");
|
|
12024
12434
|
}
|
|
12025
12435
|
}
|
|
12026
12436
|
async load() {
|
|
@@ -12030,24 +12440,26 @@ var CloudSyncStore = class {
|
|
|
12030
12440
|
const data = JSON.parse(raw);
|
|
12031
12441
|
for (const l of data) {
|
|
12032
12442
|
this.links.set(l.orgId, { orgId: l.orgId, cloudUrl: l.cloudUrl });
|
|
12443
|
+
void stampOrgCloud(l.orgId, l.cloudUrl);
|
|
12033
12444
|
}
|
|
12034
12445
|
} catch {
|
|
12035
12446
|
}
|
|
12036
12447
|
this.loaded = true;
|
|
12037
12448
|
}
|
|
12038
12449
|
async save() {
|
|
12039
|
-
await
|
|
12450
|
+
await mkdir17(machineRoot(), { recursive: true });
|
|
12040
12451
|
const filePath = this.getFilePath();
|
|
12041
12452
|
const tmpPath = filePath + ".tmp";
|
|
12042
12453
|
const data = JSON.stringify(Array.from(this.links.values()), null, 2);
|
|
12043
|
-
await
|
|
12044
|
-
await
|
|
12045
|
-
await
|
|
12454
|
+
await writeFile15(tmpPath, data, "utf-8");
|
|
12455
|
+
await rename12(tmpPath, filePath);
|
|
12456
|
+
await chmod10(filePath, 384);
|
|
12046
12457
|
}
|
|
12047
12458
|
/** Store (or replace) the sync link for an organization. */
|
|
12048
12459
|
async upsert(orgId, cloudUrl) {
|
|
12049
12460
|
await this.load();
|
|
12050
12461
|
this.links.set(orgId, { orgId, cloudUrl });
|
|
12462
|
+
await stampOrgCloud(orgId, cloudUrl);
|
|
12051
12463
|
await this.save();
|
|
12052
12464
|
}
|
|
12053
12465
|
async remove(orgId) {
|
|
@@ -12068,16 +12480,16 @@ var cloudSyncStore = new CloudSyncStore();
|
|
|
12068
12480
|
|
|
12069
12481
|
// ../server/src/services/cloud-sync-bridge.ts
|
|
12070
12482
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
12071
|
-
import { mkdir as
|
|
12483
|
+
import { mkdir as mkdir19, rm as rm5 } from "fs/promises";
|
|
12072
12484
|
import * as Y from "yjs";
|
|
12073
12485
|
|
|
12074
12486
|
// ../server/src/services/org-lock.ts
|
|
12075
|
-
import { open as open2, readFile as
|
|
12487
|
+
import { open as open2, readFile as readFile16, unlink as unlink4, mkdir as mkdir18 } from "fs/promises";
|
|
12076
12488
|
import { readFileSync, unlinkSync } from "fs";
|
|
12077
|
-
import { join as
|
|
12489
|
+
import { join as join17 } from "path";
|
|
12078
12490
|
var held = /* @__PURE__ */ new Set();
|
|
12079
12491
|
function lockPath(orgId) {
|
|
12080
|
-
return
|
|
12492
|
+
return join17(orgVaultRoot(orgId), "org.lock");
|
|
12081
12493
|
}
|
|
12082
12494
|
function isRunning(pid) {
|
|
12083
12495
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -12090,7 +12502,7 @@ function isRunning(pid) {
|
|
|
12090
12502
|
}
|
|
12091
12503
|
async function readLock(path) {
|
|
12092
12504
|
try {
|
|
12093
|
-
const parsed = JSON.parse(await
|
|
12505
|
+
const parsed = JSON.parse(await readFile16(path, "utf-8"));
|
|
12094
12506
|
return typeof parsed?.pid === "number" ? parsed : null;
|
|
12095
12507
|
} catch {
|
|
12096
12508
|
return null;
|
|
@@ -12098,7 +12510,7 @@ async function readLock(path) {
|
|
|
12098
12510
|
}
|
|
12099
12511
|
async function acquireOrgLock(orgId) {
|
|
12100
12512
|
const path = lockPath(orgId);
|
|
12101
|
-
await
|
|
12513
|
+
await mkdir18(orgVaultRoot(orgId), { recursive: true });
|
|
12102
12514
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
12103
12515
|
try {
|
|
12104
12516
|
const handle = await open2(path, "wx");
|
|
@@ -12165,6 +12577,17 @@ var SyncOutdatedError = class extends Error {
|
|
|
12165
12577
|
this.lastIncompatibleVersion = lastIncompatibleVersion;
|
|
12166
12578
|
}
|
|
12167
12579
|
};
|
|
12580
|
+
var ProjectHeldElsewhereError = class extends Error {
|
|
12581
|
+
constructor(projectId, heldByOrgId) {
|
|
12582
|
+
super(
|
|
12583
|
+
`Project ${projectId} is already held here for ${heldByOrgId ? `organization ${heldByOrgId}` : "this directory"}. Two copies of one project share every entity id \u2014 stop following the other one first.`
|
|
12584
|
+
);
|
|
12585
|
+
this.projectId = projectId;
|
|
12586
|
+
this.heldByOrgId = heldByOrgId;
|
|
12587
|
+
this.name = "ProjectHeldElsewhereError";
|
|
12588
|
+
}
|
|
12589
|
+
code = "PROJECT_HELD_ELSEWHERE";
|
|
12590
|
+
};
|
|
12168
12591
|
var OrgSyncConnection = class {
|
|
12169
12592
|
doc = new Y.Doc();
|
|
12170
12593
|
projects = getCollection(this.doc, "projects");
|
|
@@ -12481,18 +12904,24 @@ var OrgSyncConnection = class {
|
|
|
12481
12904
|
*/
|
|
12482
12905
|
async followProject(projectId) {
|
|
12483
12906
|
const inDoc = readEntity(this.collection("projects"), projectId) !== null;
|
|
12484
|
-
const existing = await projectStore.get(projectId);
|
|
12907
|
+
const existing = await projectStore.get({ id: projectId, orgId: this.link.orgId }) ?? await projectStore.get({ id: projectId, orgId: null });
|
|
12485
12908
|
if (existing && !existing.orgId) {
|
|
12486
12909
|
console.error(
|
|
12487
12910
|
"[cloud-sync] project %s is already a local project here \u2014 not following it for org %s",
|
|
12488
12911
|
projectId,
|
|
12489
12912
|
this.link.orgId
|
|
12490
12913
|
);
|
|
12491
|
-
|
|
12914
|
+
throw new ProjectHeldElsewhereError(projectId, null);
|
|
12915
|
+
}
|
|
12916
|
+
const heldBy = await projectStore.list().then((all) => all.find(
|
|
12917
|
+
(p) => p.id === projectId && p.orgId && p.orgId !== this.link.orgId
|
|
12918
|
+
));
|
|
12919
|
+
if (heldBy?.orgId) {
|
|
12920
|
+
throw new ProjectHeldElsewhereError(projectId, heldBy.orgId);
|
|
12492
12921
|
}
|
|
12493
12922
|
const isLocal = existing?.orgId === this.link.orgId;
|
|
12494
12923
|
if (!inDoc && !isLocal) return false;
|
|
12495
|
-
await
|
|
12924
|
+
await mkdir19(projectVaultRoot(this.link.orgId, projectId), { recursive: true });
|
|
12496
12925
|
this.tracked.add(projectId);
|
|
12497
12926
|
if (inDoc) await this.pullFromDoc([projectId]);
|
|
12498
12927
|
else await this.reconcileLocalToDoc();
|
|
@@ -12507,7 +12936,7 @@ var OrgSyncConnection = class {
|
|
|
12507
12936
|
*/
|
|
12508
12937
|
async unfollowProject(projectId) {
|
|
12509
12938
|
this.tracked.delete(projectId);
|
|
12510
|
-
await projectStore.forgetLocally(projectId);
|
|
12939
|
+
await projectStore.forgetLocally({ id: projectId, orgId: this.link.orgId });
|
|
12511
12940
|
await rm5(projectVaultRoot(this.link.orgId, projectId), { recursive: true, force: true }).catch(() => {
|
|
12512
12941
|
});
|
|
12513
12942
|
}
|
|
@@ -12589,9 +13018,33 @@ var OrgSyncConnection = class {
|
|
|
12589
13018
|
if (result?.created) await this.bootstrapEnvironment(id);
|
|
12590
13019
|
}
|
|
12591
13020
|
async deleteLocally(name, id) {
|
|
12592
|
-
if (name === "services")
|
|
12593
|
-
|
|
12594
|
-
|
|
13021
|
+
if (name === "services") {
|
|
13022
|
+
await serviceStore.applyRemoteDelete(id);
|
|
13023
|
+
return;
|
|
13024
|
+
}
|
|
13025
|
+
if (name === "environments") {
|
|
13026
|
+
await environmentStore.applyRemoteDelete(id);
|
|
13027
|
+
return;
|
|
13028
|
+
}
|
|
13029
|
+
const refusal = this.refusalFor(id);
|
|
13030
|
+
if (refusal) {
|
|
13031
|
+
console.error(
|
|
13032
|
+
"[cloud-sync] project %s refused by the cloud for org %s (%s) \u2014 kept on disk, no longer pushed",
|
|
13033
|
+
id,
|
|
13034
|
+
this.link.orgId,
|
|
13035
|
+
refusal
|
|
13036
|
+
);
|
|
13037
|
+
this.tracked.delete(id);
|
|
13038
|
+
return;
|
|
13039
|
+
}
|
|
13040
|
+
await projectStore.applyRemoteDelete({ id, orgId: this.link.orgId });
|
|
13041
|
+
}
|
|
13042
|
+
/** Le motif du refus du cloud pour ce projet, s'il en a écrit un. */
|
|
13043
|
+
refusalFor(projectId) {
|
|
13044
|
+
const entry = this.doc.getMap("projectRefusals").get(projectId);
|
|
13045
|
+
if (!entry || typeof entry !== "object") return null;
|
|
13046
|
+
const reason = entry.reason;
|
|
13047
|
+
return typeof reason === "string" ? reason : "refused";
|
|
12595
13048
|
}
|
|
12596
13049
|
async bootstrapEnvironment(projectId) {
|
|
12597
13050
|
try {
|
|
@@ -12603,8 +13056,8 @@ var OrgSyncConnection = class {
|
|
|
12603
13056
|
description: "Default local environment (immutable)",
|
|
12604
13057
|
parentIds: [],
|
|
12605
13058
|
immutable: true
|
|
12606
|
-
});
|
|
12607
|
-
await projectStore.addEnvironment(projectId, localEnv.id);
|
|
13059
|
+
}, this.link.orgId);
|
|
13060
|
+
await projectStore.addEnvironment({ id: projectId, orgId: this.link.orgId }, localEnv.id);
|
|
12608
13061
|
} catch (err) {
|
|
12609
13062
|
console.error("[cloud-sync] env bootstrap failed for project %s:", projectId, err.message);
|
|
12610
13063
|
}
|
|
@@ -12627,7 +13080,7 @@ var OrgSyncConnection = class {
|
|
|
12627
13080
|
*/
|
|
12628
13081
|
async reconcileLocalToDoc() {
|
|
12629
13082
|
if (this.stopped || this.pulling) return;
|
|
12630
|
-
const projects = await projectStore.listDiskFormByOrg(this.link.orgId);
|
|
13083
|
+
const projects = (await projectStore.listDiskFormByOrg(this.link.orgId)).filter((p) => !this.refusalFor(p.id));
|
|
12631
13084
|
const projectIds = projects.map((p) => p.id);
|
|
12632
13085
|
const serviceIds = [...new Set(projects.flatMap((p) => p.serviceIds ?? []))];
|
|
12633
13086
|
const [services, environments] = await Promise.all([
|
|
@@ -12777,8 +13230,8 @@ var cloudSyncBridge = new CloudSyncBridge();
|
|
|
12777
13230
|
|
|
12778
13231
|
// ../server/src/services/master-password.ts
|
|
12779
13232
|
import { randomBytes as randomBytes4, createCipheriv, createDecipheriv } from "crypto";
|
|
12780
|
-
import { readFile as
|
|
12781
|
-
import { join as
|
|
13233
|
+
import { readFile as readFile17, writeFile as writeFile16, chmod as chmod11, stat as stat6, rename as rename13 } from "fs/promises";
|
|
13234
|
+
import { join as join18 } from "path";
|
|
12782
13235
|
import { Entry } from "@napi-rs/keyring";
|
|
12783
13236
|
import { hashRaw as argon2HashRaw } from "@node-rs/argon2";
|
|
12784
13237
|
var KEYRING_SERVICE = "runeya";
|
|
@@ -12790,9 +13243,9 @@ var CANARY_PLAINTEXT = "runeya:v1:key-verification";
|
|
|
12790
13243
|
var MAX_KEY_FILE_SIZE = 1024;
|
|
12791
13244
|
var ARGON2_OPTIONS = { memoryCost: 65536, timeCost: 3, parallelism: 4, outputLen: 32 };
|
|
12792
13245
|
async function readKeyFile(filePath) {
|
|
12793
|
-
const s = await
|
|
13246
|
+
const s = await stat6(filePath);
|
|
12794
13247
|
if (s.size > MAX_KEY_FILE_SIZE) throw new Error(`Fichier suspect : ${filePath} (${s.size} octets)`);
|
|
12795
|
-
return
|
|
13248
|
+
return readFile17(filePath);
|
|
12796
13249
|
}
|
|
12797
13250
|
function aesEncrypt(key, plaintext) {
|
|
12798
13251
|
const iv = randomBytes4(12);
|
|
@@ -12811,7 +13264,7 @@ function aesDecrypt(key, data) {
|
|
|
12811
13264
|
}
|
|
12812
13265
|
async function getPepperFromFile(dataDir) {
|
|
12813
13266
|
try {
|
|
12814
|
-
const raw = await readKeyFile(
|
|
13267
|
+
const raw = await readKeyFile(join18(dataDir, PEPPER_FILENAME));
|
|
12815
13268
|
const hex = raw.toString("utf-8").trim();
|
|
12816
13269
|
if (/^[0-9a-f]{64}$/i.test(hex)) return Buffer.from(hex, "hex");
|
|
12817
13270
|
} catch {
|
|
@@ -12819,11 +13272,11 @@ async function getPepperFromFile(dataDir) {
|
|
|
12819
13272
|
return null;
|
|
12820
13273
|
}
|
|
12821
13274
|
async function writePepperToFile(dataDir, pepper) {
|
|
12822
|
-
const filePath =
|
|
13275
|
+
const filePath = join18(dataDir, PEPPER_FILENAME);
|
|
12823
13276
|
const tmpPath = filePath + ".tmp";
|
|
12824
|
-
await
|
|
12825
|
-
await
|
|
12826
|
-
await
|
|
13277
|
+
await writeFile16(tmpPath, pepper.toString("hex"), "utf-8");
|
|
13278
|
+
await chmod11(tmpPath, 384);
|
|
13279
|
+
await rename13(tmpPath, filePath);
|
|
12827
13280
|
}
|
|
12828
13281
|
function tryKeyringGet() {
|
|
12829
13282
|
try {
|
|
@@ -12870,14 +13323,14 @@ async function writeEncryptedKey(dataDir, masterPassword, encryptionKey) {
|
|
|
12870
13323
|
const payload = aesEncrypt(derivedKey, encryptionKey);
|
|
12871
13324
|
const fileContent = Buffer.concat([salt, payload]);
|
|
12872
13325
|
const encoded = fileContent.toString("base64");
|
|
12873
|
-
const filePath =
|
|
13326
|
+
const filePath = join18(dataDir, ENCRYPTED_KEY_FILENAME);
|
|
12874
13327
|
const tmpPath = filePath + ".tmp";
|
|
12875
|
-
await
|
|
12876
|
-
await
|
|
12877
|
-
await
|
|
13328
|
+
await writeFile16(tmpPath, encoded, "utf-8");
|
|
13329
|
+
await chmod11(tmpPath, 384);
|
|
13330
|
+
await rename13(tmpPath, filePath);
|
|
12878
13331
|
}
|
|
12879
13332
|
async function readEncryptedKey(dataDir, masterPassword) {
|
|
12880
|
-
const filePath =
|
|
13333
|
+
const filePath = join18(dataDir, ENCRYPTED_KEY_FILENAME);
|
|
12881
13334
|
const raw = await readKeyFile(filePath);
|
|
12882
13335
|
const fileContent = Buffer.from(raw.toString("utf-8").trim(), "base64");
|
|
12883
13336
|
const salt = fileContent.subarray(0, 16);
|
|
@@ -12897,13 +13350,13 @@ async function getPepperFromKeychain(dataDir) {
|
|
|
12897
13350
|
}
|
|
12898
13351
|
async function writeVerifyFile(dataDir, encryptionKey) {
|
|
12899
13352
|
const payload = aesEncrypt(encryptionKey, Buffer.from(CANARY_PLAINTEXT, "utf-8"));
|
|
12900
|
-
const filePath =
|
|
13353
|
+
const filePath = join18(dataDir, VERIFY_FILENAME);
|
|
12901
13354
|
const tmpPath = filePath + ".tmp";
|
|
12902
|
-
await
|
|
12903
|
-
await
|
|
13355
|
+
await writeFile16(tmpPath, payload.toString("base64"), "utf-8");
|
|
13356
|
+
await rename13(tmpPath, filePath);
|
|
12904
13357
|
}
|
|
12905
13358
|
async function verifyEncryptionKey(dataDir, encryptionKey) {
|
|
12906
|
-
const filePath =
|
|
13359
|
+
const filePath = join18(dataDir, VERIFY_FILENAME);
|
|
12907
13360
|
const raw = await readKeyFile(filePath);
|
|
12908
13361
|
if (!matchesCanary(raw.toString("utf-8"), encryptionKey)) {
|
|
12909
13362
|
throw new Error("Cl\xE9 d'encryption incorrecte");
|
|
@@ -12919,14 +13372,14 @@ function matchesCanary(canaryBase64, encryptionKey) {
|
|
|
12919
13372
|
}
|
|
12920
13373
|
async function readVerifyFile(dataDir) {
|
|
12921
13374
|
try {
|
|
12922
|
-
return (await readKeyFile(
|
|
13375
|
+
return (await readKeyFile(join18(dataDir, VERIFY_FILENAME))).toString("utf-8").trim();
|
|
12923
13376
|
} catch {
|
|
12924
13377
|
return null;
|
|
12925
13378
|
}
|
|
12926
13379
|
}
|
|
12927
13380
|
async function hasVerifyFile(dataDir) {
|
|
12928
13381
|
try {
|
|
12929
|
-
await
|
|
13382
|
+
await stat6(join18(dataDir, VERIFY_FILENAME));
|
|
12930
13383
|
return true;
|
|
12931
13384
|
} catch {
|
|
12932
13385
|
return false;
|
|
@@ -12934,7 +13387,7 @@ async function hasVerifyFile(dataDir) {
|
|
|
12934
13387
|
}
|
|
12935
13388
|
async function hasEncryptedKeyFile(dataDir) {
|
|
12936
13389
|
try {
|
|
12937
|
-
await
|
|
13390
|
+
await stat6(join18(dataDir, ENCRYPTED_KEY_FILENAME));
|
|
12938
13391
|
return true;
|
|
12939
13392
|
} catch {
|
|
12940
13393
|
return false;
|
|
@@ -12942,6 +13395,20 @@ async function hasEncryptedKeyFile(dataDir) {
|
|
|
12942
13395
|
}
|
|
12943
13396
|
|
|
12944
13397
|
// ../server/src/trpc/routers/cloud-sync.ts
|
|
13398
|
+
async function cloudMessage(res) {
|
|
13399
|
+
try {
|
|
13400
|
+
const body = await res.text();
|
|
13401
|
+
if (!body) return "";
|
|
13402
|
+
try {
|
|
13403
|
+
const parsed = JSON.parse(body);
|
|
13404
|
+
if (typeof parsed.message === "string") return parsed.message;
|
|
13405
|
+
} catch {
|
|
13406
|
+
}
|
|
13407
|
+
return body.slice(0, 300);
|
|
13408
|
+
} catch {
|
|
13409
|
+
return "";
|
|
13410
|
+
}
|
|
13411
|
+
}
|
|
12945
13412
|
async function reconcileOrgCanary(orgId) {
|
|
12946
13413
|
const key = getEncryptionKey();
|
|
12947
13414
|
if (!key) {
|
|
@@ -13016,8 +13483,15 @@ var cloudSyncRouter = router({
|
|
|
13016
13483
|
const link = await cloudSyncStore.get(input.orgId);
|
|
13017
13484
|
if (link) await cloudSyncBridge.connect(link);
|
|
13018
13485
|
}
|
|
13019
|
-
|
|
13020
|
-
|
|
13486
|
+
try {
|
|
13487
|
+
const pulled = await cloudSyncBridge.followProject(input.orgId, input.projectId);
|
|
13488
|
+
return { pulled, state: syncState(input.orgId) };
|
|
13489
|
+
} catch (err) {
|
|
13490
|
+
if (err instanceof ProjectHeldElsewhereError) {
|
|
13491
|
+
throw new TRPCError20({ code: "CONFLICT", message: err.code, cause: err });
|
|
13492
|
+
}
|
|
13493
|
+
throw err;
|
|
13494
|
+
}
|
|
13021
13495
|
}),
|
|
13022
13496
|
/**
|
|
13023
13497
|
* Stop materializing one project of an org on this machine.
|
|
@@ -13034,6 +13508,47 @@ var cloudSyncRouter = router({
|
|
|
13034
13508
|
await cloudSyncBridge.unfollowProject(input.orgId, input.projectId);
|
|
13035
13509
|
return { success: true };
|
|
13036
13510
|
}),
|
|
13511
|
+
/**
|
|
13512
|
+
* Retirer un projet de l'organisation — pour tous ses membres.
|
|
13513
|
+
*
|
|
13514
|
+
* À ne pas confondre avec `untrackProject`, qui ne concerne que cette
|
|
13515
|
+
* machine. Ici le projet quitte l'organisation : il disparaît pour chaque
|
|
13516
|
+
* membre, et le cloud rend son identifiant. La suppression locale n'est pas
|
|
13517
|
+
* faite ici : elle arrive par la synchronisation, comme pour tout autre
|
|
13518
|
+
* membre, ce qui évite d'effacer les fichiers si le cloud a refusé l'ordre.
|
|
13519
|
+
*/
|
|
13520
|
+
deleteProjectFromOrg: protectedProcedure.input(
|
|
13521
|
+
z24.object({
|
|
13522
|
+
orgId: z24.string().min(1).max(128),
|
|
13523
|
+
projectId: z24.string().min(1).max(128)
|
|
13524
|
+
})
|
|
13525
|
+
).mutation(async ({ input }) => {
|
|
13526
|
+
let res;
|
|
13527
|
+
try {
|
|
13528
|
+
res = await cloudFetch("/api/auth/project/delete", {
|
|
13529
|
+
method: "POST",
|
|
13530
|
+
body: { organizationId: input.orgId, projectId: input.projectId }
|
|
13531
|
+
});
|
|
13532
|
+
} catch (err) {
|
|
13533
|
+
if (err instanceof TRPCError20 && err.code === "PRECONDITION_FAILED") {
|
|
13534
|
+
throw new TRPCError20({ code: "PRECONDITION_FAILED", message: "CLOUD_NOT_LINKED" });
|
|
13535
|
+
}
|
|
13536
|
+
throw err;
|
|
13537
|
+
}
|
|
13538
|
+
if (res.ok) return { success: true };
|
|
13539
|
+
const detail = await cloudMessage(res);
|
|
13540
|
+
if (res.status === 403) {
|
|
13541
|
+
const code = /not a member/i.test(detail) ? "PROJECT_NOT_MEMBER" : "PROJECT_DELETE_FORBIDDEN";
|
|
13542
|
+
throw new TRPCError20({ code: "FORBIDDEN", message: code, cause: new Error(detail) });
|
|
13543
|
+
}
|
|
13544
|
+
if (res.status === 404) {
|
|
13545
|
+
throw new TRPCError20({ code: "NOT_FOUND", message: "PROJECT_NOT_IN_ORG", cause: new Error(detail) });
|
|
13546
|
+
}
|
|
13547
|
+
throw new TRPCError20({
|
|
13548
|
+
code: "BAD_GATEWAY",
|
|
13549
|
+
message: detail || `PROJECT_DELETE_FAILED (${res.status})`
|
|
13550
|
+
});
|
|
13551
|
+
}),
|
|
13037
13552
|
/** Stop syncing an org and forget its token. */
|
|
13038
13553
|
unlink: protectedProcedure.input(z24.object({ orgId: z24.string().min(1).max(128) })).mutation(async ({ input }) => {
|
|
13039
13554
|
cloudSyncBridge.disconnect(input.orgId);
|
|
@@ -13056,6 +13571,104 @@ var cloudSyncRouter = router({
|
|
|
13056
13571
|
// borne est exclusive — il faut strictement plus récent que ce numéro.
|
|
13057
13572
|
lastIncompatibleVersion: states.get(l.orgId)?.lastIncompatibleVersion ?? null
|
|
13058
13573
|
}));
|
|
13574
|
+
}),
|
|
13575
|
+
/**
|
|
13576
|
+
* The cloud instance behind every org vault on this machine.
|
|
13577
|
+
*
|
|
13578
|
+
* Wider than `list`, which only sees the links of the directory this server
|
|
13579
|
+
* was launched in: org vaults are shared machine-wide, so a project can very
|
|
13580
|
+
* well belong to an org linked from another directory — or from another cloud
|
|
13581
|
+
* entirely.
|
|
13582
|
+
*/
|
|
13583
|
+
orgClouds: protectedProcedure.query(async () => {
|
|
13584
|
+
const clouds = await readOrgClouds();
|
|
13585
|
+
for (const l of await cloudSyncStore.list()) clouds[l.orgId] = l.cloudUrl;
|
|
13586
|
+
return clouds;
|
|
13587
|
+
})
|
|
13588
|
+
});
|
|
13589
|
+
|
|
13590
|
+
// ../server/src/trpc/routers/filesystem.ts
|
|
13591
|
+
import { readdir as readdir7, stat as stat7 } from "fs/promises";
|
|
13592
|
+
import { homedir as homedir5 } from "os";
|
|
13593
|
+
import { isAbsolute as isAbsolute4, join as join19, resolve as resolve5, dirname as dirname9, basename as basename2 } from "path";
|
|
13594
|
+
import { z as z25 } from "zod";
|
|
13595
|
+
var MAX_ENTRIES = 500;
|
|
13596
|
+
var PATH_SCHEMA = z25.string().min(1).max(4096);
|
|
13597
|
+
function safeResolve(input) {
|
|
13598
|
+
if (input.includes("\0")) throw new Error("Chemin invalide");
|
|
13599
|
+
const expanded = input.startsWith("~") ? join19(homedir5(), input.slice(1)) : input;
|
|
13600
|
+
if (!isAbsolute4(expanded)) throw new Error("Le chemin doit \xEAtre absolu");
|
|
13601
|
+
return resolve5(expanded);
|
|
13602
|
+
}
|
|
13603
|
+
var filesystemRouter = router({
|
|
13604
|
+
/** Where a picker should open when the user has chosen nothing yet. */
|
|
13605
|
+
suggestedRoots: protectedProcedure.query(async () => {
|
|
13606
|
+
const roots = [
|
|
13607
|
+
{ label: "home", path: homedir5() },
|
|
13608
|
+
// The launch directory, taken from the vault rather than the process.
|
|
13609
|
+
{ label: "cwd", path: await resolveWorkspaceCwd({}) }
|
|
13610
|
+
];
|
|
13611
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13612
|
+
return roots.filter((r) => !seen.has(r.path) && seen.add(r.path));
|
|
13613
|
+
}),
|
|
13614
|
+
/**
|
|
13615
|
+
* The sub-directories of one directory.
|
|
13616
|
+
*
|
|
13617
|
+
* Hidden entries are skipped: they are noise for choosing a workspace, and
|
|
13618
|
+
* `.runeya` itself would invite placing a project inside another's data.
|
|
13619
|
+
*/
|
|
13620
|
+
listDirectories: protectedProcedure.input(z25.object({ path: PATH_SCHEMA })).query(async ({ input }) => {
|
|
13621
|
+
const dir = safeResolve(input.path);
|
|
13622
|
+
const info = await stat7(dir).catch(() => null);
|
|
13623
|
+
if (!info?.isDirectory()) {
|
|
13624
|
+
throw new Error(`Ce n'est pas un r\xE9pertoire : ${dir}`);
|
|
13625
|
+
}
|
|
13626
|
+
let names;
|
|
13627
|
+
try {
|
|
13628
|
+
names = (await readdir7(dir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
13629
|
+
} catch {
|
|
13630
|
+
throw new Error(`R\xE9pertoire illisible : ${dir}`);
|
|
13631
|
+
}
|
|
13632
|
+
const truncated = names.length > MAX_ENTRIES;
|
|
13633
|
+
const entries = [];
|
|
13634
|
+
for (const name of names.slice(0, MAX_ENTRIES)) {
|
|
13635
|
+
const path = join19(dir, name);
|
|
13636
|
+
entries.push({ name, path, hasRuneyaProject: await holdsRuneyaProject(path) });
|
|
13637
|
+
}
|
|
13638
|
+
return {
|
|
13639
|
+
path: dir,
|
|
13640
|
+
// Null at the filesystem root, which is what stops "up" from looping.
|
|
13641
|
+
parent: dirname9(dir) === dir ? null : dirname9(dir),
|
|
13642
|
+
name: basename2(dir) || dir,
|
|
13643
|
+
entries,
|
|
13644
|
+
truncated,
|
|
13645
|
+
hasRuneyaProject: await holdsRuneyaProject(dir)
|
|
13646
|
+
};
|
|
13647
|
+
}),
|
|
13648
|
+
/**
|
|
13649
|
+
* Whether a directory can receive this project.
|
|
13650
|
+
*
|
|
13651
|
+
* Two projects in one directory is what makes services run against the wrong
|
|
13652
|
+
* code, so a directory that already holds one is refused outright.
|
|
13653
|
+
*/
|
|
13654
|
+
checkProjectTarget: protectedProcedure.input(z25.object({
|
|
13655
|
+
path: PATH_SCHEMA,
|
|
13656
|
+
projectId: z25.string().min(1).max(128),
|
|
13657
|
+
orgId: z25.string().max(128).nullable().optional()
|
|
13658
|
+
})).query(async ({ input }) => {
|
|
13659
|
+
const dir = safeResolve(input.path);
|
|
13660
|
+
const info = await stat7(dir).catch(() => null);
|
|
13661
|
+
if (!info?.isDirectory()) {
|
|
13662
|
+
return { ok: false, reason: "missing", path: dir, otherProjectId: null };
|
|
13663
|
+
}
|
|
13664
|
+
if (await holdsRuneyaProject(dir)) {
|
|
13665
|
+
return { ok: false, reason: "has-runeya-project", path: dir, otherProjectId: null };
|
|
13666
|
+
}
|
|
13667
|
+
const otherProjectId = await projectPathStore.projectAt(dir, { id: input.projectId, orgId: input.orgId });
|
|
13668
|
+
if (otherProjectId) {
|
|
13669
|
+
return { ok: false, reason: "taken", path: dir, otherProjectId };
|
|
13670
|
+
}
|
|
13671
|
+
return { ok: true, reason: null, path: dir, otherProjectId: null };
|
|
13059
13672
|
})
|
|
13060
13673
|
});
|
|
13061
13674
|
|
|
@@ -13083,7 +13696,8 @@ var appRouter = router({
|
|
|
13083
13696
|
wiki: wikiRouter,
|
|
13084
13697
|
config: configRouter,
|
|
13085
13698
|
cloudSync: cloudSyncRouter,
|
|
13086
|
-
cloudAuth: cloudAuthRouter
|
|
13699
|
+
cloudAuth: cloudAuthRouter,
|
|
13700
|
+
filesystem: filesystemRouter
|
|
13087
13701
|
});
|
|
13088
13702
|
|
|
13089
13703
|
// ../server/src/middleware/error-handler.ts
|
|
@@ -13118,7 +13732,7 @@ var MigrationEngine = class {
|
|
|
13118
13732
|
var migrationEngine = new MigrationEngine();
|
|
13119
13733
|
|
|
13120
13734
|
// ../server/src/services/file-security.ts
|
|
13121
|
-
import { chmod as
|
|
13735
|
+
import { chmod as chmod12, stat as stat8 } from "fs/promises";
|
|
13122
13736
|
var SENSITIVE_FILES = [
|
|
13123
13737
|
"agents.json",
|
|
13124
13738
|
"settings.json",
|
|
@@ -13132,11 +13746,11 @@ async function ensureFilePermissions() {
|
|
|
13132
13746
|
for (const filename of SENSITIVE_FILES) {
|
|
13133
13747
|
for (const filepath of [machineFilePath(filename), legacyMachineFilePath(filename)]) {
|
|
13134
13748
|
try {
|
|
13135
|
-
const stats = await
|
|
13749
|
+
const stats = await stat8(filepath);
|
|
13136
13750
|
const mode = stats.mode & 511;
|
|
13137
13751
|
if (mode !== SECURE_MODE) {
|
|
13138
13752
|
console.warn("[security] File", filepath, "has mode", mode.toString(8) + ", fixing to 600");
|
|
13139
|
-
await
|
|
13753
|
+
await chmod12(filepath, SECURE_MODE);
|
|
13140
13754
|
}
|
|
13141
13755
|
} catch (err) {
|
|
13142
13756
|
if (err.code !== "ENOENT") {
|
|
@@ -13147,19 +13761,129 @@ async function ensureFilePermissions() {
|
|
|
13147
13761
|
}
|
|
13148
13762
|
}
|
|
13149
13763
|
|
|
13764
|
+
// ../server/src/services/vault-migration.ts
|
|
13765
|
+
import { readdir as readdir8, readFile as readFile18, writeFile as writeFile17, mkdir as mkdir20, copyFile, unlink as unlink5, rename as rename14, stat as stat9 } from "fs/promises";
|
|
13766
|
+
import { join as join20 } from "path";
|
|
13767
|
+
var KEY_FILES = [".encryption.key.enc", ".runeya-verify"];
|
|
13768
|
+
var MACHINE_FILES = [
|
|
13769
|
+
"agents.json",
|
|
13770
|
+
"cloud-session.json",
|
|
13771
|
+
"conversations.private.json",
|
|
13772
|
+
"api-keys.json",
|
|
13773
|
+
"api-keys.private.json",
|
|
13774
|
+
"settings.private.json"
|
|
13775
|
+
];
|
|
13776
|
+
function rekeyValue(value, from, to, counter) {
|
|
13777
|
+
if (typeof value === "string") {
|
|
13778
|
+
if (!isEncryptedValue(value)) return value;
|
|
13779
|
+
let plaintext;
|
|
13780
|
+
try {
|
|
13781
|
+
plaintext = decryptValueWith(from, value);
|
|
13782
|
+
} catch {
|
|
13783
|
+
return value;
|
|
13784
|
+
}
|
|
13785
|
+
counter.n += 1;
|
|
13786
|
+
return encryptValueWith(to, plaintext);
|
|
13787
|
+
}
|
|
13788
|
+
if (Array.isArray(value)) return value.map((v) => rekeyValue(v, from, to, counter));
|
|
13789
|
+
if (value && typeof value === "object") {
|
|
13790
|
+
const out = {};
|
|
13791
|
+
for (const [k, v] of Object.entries(value)) {
|
|
13792
|
+
out[k] = rekeyValue(v, from, to, counter);
|
|
13793
|
+
}
|
|
13794
|
+
return out;
|
|
13795
|
+
}
|
|
13796
|
+
return value;
|
|
13797
|
+
}
|
|
13798
|
+
async function vaultJsonFiles(root) {
|
|
13799
|
+
const entries = await readdir8(root, { withFileTypes: true });
|
|
13800
|
+
return entries.filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => e.name);
|
|
13801
|
+
}
|
|
13802
|
+
async function exists(path) {
|
|
13803
|
+
try {
|
|
13804
|
+
await stat9(path);
|
|
13805
|
+
return true;
|
|
13806
|
+
} catch {
|
|
13807
|
+
return false;
|
|
13808
|
+
}
|
|
13809
|
+
}
|
|
13810
|
+
async function openLegacyVault(legacyRoot, masterPassword) {
|
|
13811
|
+
const key = await readEncryptedKey(legacyRoot, masterPassword);
|
|
13812
|
+
await verifyEncryptionKey(legacyRoot, key);
|
|
13813
|
+
return key;
|
|
13814
|
+
}
|
|
13815
|
+
async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machineKey, now) {
|
|
13816
|
+
const stamp = now.toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
13817
|
+
const backupDir = join20(legacyRoot, `backup-${stamp}`);
|
|
13818
|
+
await mkdir20(backupDir, { recursive: true });
|
|
13819
|
+
const jsonFiles = await vaultJsonFiles(legacyRoot);
|
|
13820
|
+
for (const name of [...jsonFiles, ...KEY_FILES]) {
|
|
13821
|
+
if (await exists(join20(legacyRoot, name))) {
|
|
13822
|
+
await copyFile(join20(legacyRoot, name), join20(backupDir, name));
|
|
13823
|
+
}
|
|
13824
|
+
}
|
|
13825
|
+
const rekeyed = [];
|
|
13826
|
+
for (const name of jsonFiles) {
|
|
13827
|
+
const raw = await readFile18(join20(legacyRoot, name), "utf-8");
|
|
13828
|
+
let parsed;
|
|
13829
|
+
try {
|
|
13830
|
+
parsed = JSON.parse(raw);
|
|
13831
|
+
} catch {
|
|
13832
|
+
continue;
|
|
13833
|
+
}
|
|
13834
|
+
const counter = { n: 0 };
|
|
13835
|
+
const converted = rekeyValue(parsed, legacyKey, machineKey, counter);
|
|
13836
|
+
if (counter.n === 0) continue;
|
|
13837
|
+
const tmp = join20(legacyRoot, `${name}.tmp`);
|
|
13838
|
+
await writeFile17(tmp, JSON.stringify(converted, null, 2), "utf-8");
|
|
13839
|
+
await rename14(tmp, join20(legacyRoot, name));
|
|
13840
|
+
rekeyed.push({ file: name, values: counter.n });
|
|
13841
|
+
}
|
|
13842
|
+
for (const { file, values } of rekeyed) {
|
|
13843
|
+
const parsed = JSON.parse(await readFile18(join20(legacyRoot, file), "utf-8"));
|
|
13844
|
+
const check = { n: 0 };
|
|
13845
|
+
rekeyValue(parsed, machineKey, machineKey, check);
|
|
13846
|
+
if (check.n !== values) {
|
|
13847
|
+
throw new Error(
|
|
13848
|
+
`V\xE9rification \xE9chou\xE9e pour ${file} : ${check.n} valeur(s) relisible(s) avec la cl\xE9 machine, ${values} attendue(s)`
|
|
13849
|
+
);
|
|
13850
|
+
}
|
|
13851
|
+
}
|
|
13852
|
+
await mkdir20(machineRootDir, { recursive: true });
|
|
13853
|
+
const moved = [];
|
|
13854
|
+
const skipped = [];
|
|
13855
|
+
for (const name of MACHINE_FILES) {
|
|
13856
|
+
const from = join20(legacyRoot, name);
|
|
13857
|
+
if (!await exists(from)) continue;
|
|
13858
|
+
const to = join20(machineRootDir, name);
|
|
13859
|
+
if (await exists(to)) {
|
|
13860
|
+
skipped.push(name);
|
|
13861
|
+
continue;
|
|
13862
|
+
}
|
|
13863
|
+
await copyFile(from, to);
|
|
13864
|
+
await unlink5(from);
|
|
13865
|
+
moved.push(name);
|
|
13866
|
+
}
|
|
13867
|
+
for (const name of KEY_FILES) {
|
|
13868
|
+
const path = join20(legacyRoot, name);
|
|
13869
|
+
if (await exists(path)) await unlink5(path);
|
|
13870
|
+
}
|
|
13871
|
+
return { backupDir, rekeyed, moved, skipped };
|
|
13872
|
+
}
|
|
13873
|
+
|
|
13150
13874
|
// ../server/src/migrations/migrate-from-legacy.ts
|
|
13151
13875
|
import { createRequire } from "module";
|
|
13152
13876
|
import { randomBytes as randomBytes5, createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2 } from "crypto";
|
|
13153
13877
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
13154
13878
|
import {
|
|
13155
|
-
readFile as
|
|
13156
|
-
writeFile as
|
|
13157
|
-
rename as
|
|
13158
|
-
mkdir as
|
|
13879
|
+
readFile as readFile19,
|
|
13880
|
+
writeFile as writeFile18,
|
|
13881
|
+
rename as rename15,
|
|
13882
|
+
mkdir as mkdir21,
|
|
13159
13883
|
access as access2,
|
|
13160
|
-
readdir as
|
|
13884
|
+
readdir as readdir9
|
|
13161
13885
|
} from "fs/promises";
|
|
13162
|
-
import { join as
|
|
13886
|
+
import { join as join21, dirname as dirname10 } from "path";
|
|
13163
13887
|
var _require = createRequire(import.meta.url);
|
|
13164
13888
|
var PARSER_ID_MAP = {
|
|
13165
13889
|
"stack-monitor-parser-jsons": "native:json",
|
|
@@ -13268,7 +13992,7 @@ async function getSodium() {
|
|
|
13268
13992
|
return _sodium;
|
|
13269
13993
|
}
|
|
13270
13994
|
async function decryptLegacyFile(filePath, key, sodium) {
|
|
13271
|
-
const raw = (await
|
|
13995
|
+
const raw = (await readFile19(filePath, "utf-8")).trim();
|
|
13272
13996
|
if (!raw || raw === "[]" || raw === "{}") {
|
|
13273
13997
|
try {
|
|
13274
13998
|
return JSON.parse(raw);
|
|
@@ -13288,14 +14012,12 @@ async function decryptLegacyFile(filePath, key, sodium) {
|
|
|
13288
14012
|
}
|
|
13289
14013
|
async function atomicWrite(path, data) {
|
|
13290
14014
|
const tmp = path + ".tmp";
|
|
13291
|
-
await
|
|
13292
|
-
await
|
|
14015
|
+
await writeFile18(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
14016
|
+
await rename15(tmp, path);
|
|
13293
14017
|
}
|
|
13294
14018
|
async function runLegacyMigration(dataDir) {
|
|
13295
|
-
const
|
|
13296
|
-
const
|
|
13297
|
-
const legacyRoot = join17(abCwd, env.DATA_DIR);
|
|
13298
|
-
const legacyKeyPath = join17(legacyRoot, "dbs", "encryption-key.json");
|
|
14019
|
+
const legacyRoot = env.DATA_DIR;
|
|
14020
|
+
const legacyKeyPath = join21(legacyRoot, "dbs", "encryption-key.json");
|
|
13299
14021
|
try {
|
|
13300
14022
|
await access2(legacyKeyPath);
|
|
13301
14023
|
} catch {
|
|
@@ -13303,20 +14025,20 @@ async function runLegacyMigration(dataDir) {
|
|
|
13303
14025
|
}
|
|
13304
14026
|
console.log("[legacy-migration] Legacy Runeya data detected, starting migration\u2026");
|
|
13305
14027
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
13306
|
-
const legacyTmp =
|
|
13307
|
-
await
|
|
13308
|
-
await
|
|
13309
|
-
const legacyOld =
|
|
13310
|
-
await
|
|
14028
|
+
const legacyTmp = join21(abCwd, ".runeya-tmp");
|
|
14029
|
+
await rename15(legacyRoot, legacyTmp);
|
|
14030
|
+
await mkdir21(dataDir, { recursive: true });
|
|
14031
|
+
const legacyOld = join21(dataDir, `backup-${timestamp}`);
|
|
14032
|
+
await rename15(legacyTmp, legacyOld);
|
|
13311
14033
|
console.log(`[legacy-migration] Legacy data backed up to ${legacyOld}`);
|
|
13312
|
-
const keyJson = JSON.parse(await
|
|
14034
|
+
const keyJson = JSON.parse(await readFile19(join21(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
|
|
13313
14035
|
const sodium = await getSodium();
|
|
13314
14036
|
const sodiumKey = sodium.from_base64(keyJson.encryptionKey);
|
|
13315
14037
|
const keyBuffer = Buffer.from(sodiumKey);
|
|
13316
14038
|
const runyeaKeyHex = keyBuffer.toString("hex");
|
|
13317
14039
|
await setEncryptionKey(runyeaKeyHex);
|
|
13318
14040
|
console.log("[legacy-migration] Encryption key loaded in memory (temporary)");
|
|
13319
|
-
const dbsDir =
|
|
14041
|
+
const dbsDir = join21(legacyOld, "dbs");
|
|
13320
14042
|
async function readEncrypted(filePath) {
|
|
13321
14043
|
try {
|
|
13322
14044
|
return await decryptLegacyFile(filePath, sodiumKey, sodium);
|
|
@@ -13325,29 +14047,29 @@ async function runLegacyMigration(dataDir) {
|
|
|
13325
14047
|
return null;
|
|
13326
14048
|
}
|
|
13327
14049
|
}
|
|
13328
|
-
const envsDir =
|
|
13329
|
-
const envFiles = (await
|
|
14050
|
+
const envsDir = join21(dbsDir, "envs");
|
|
14051
|
+
const envFiles = (await readdir9(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
|
|
13330
14052
|
const legacyEnvs = {};
|
|
13331
14053
|
for (const file of envFiles) {
|
|
13332
14054
|
const name = file.replace(".encrypted.json", "");
|
|
13333
|
-
legacyEnvs[name] = await readEncrypted(
|
|
14055
|
+
legacyEnvs[name] = await readEncrypted(join21(envsDir, file));
|
|
13334
14056
|
}
|
|
13335
|
-
const servicesDir =
|
|
13336
|
-
const serviceFiles = (await
|
|
14057
|
+
const servicesDir = join21(dbsDir, "services");
|
|
14058
|
+
const serviceFiles = (await readdir9(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
|
|
13337
14059
|
const legacyServices = {};
|
|
13338
14060
|
for (const file of serviceFiles) {
|
|
13339
14061
|
const name = file.replace(".encrypted.json", "");
|
|
13340
14062
|
if (!name) continue;
|
|
13341
|
-
const data = await readEncrypted(
|
|
14063
|
+
const data = await readEncrypted(join21(servicesDir, file));
|
|
13342
14064
|
if (data?.label) legacyServices[name] = data;
|
|
13343
14065
|
}
|
|
13344
|
-
const overridesDir =
|
|
13345
|
-
const overrideFiles = (await
|
|
14066
|
+
const overridesDir = join21(dbsDir, "overrides");
|
|
14067
|
+
const overrideFiles = (await readdir9(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
|
|
13346
14068
|
const legacyServiceOverrides = {};
|
|
13347
14069
|
const legacyEnvOverrides = {};
|
|
13348
14070
|
for (const file of overrideFiles) {
|
|
13349
14071
|
const baseName = file.replace(".encrypted.json", "");
|
|
13350
|
-
const data = await readEncrypted(
|
|
14072
|
+
const data = await readEncrypted(join21(overridesDir, file));
|
|
13351
14073
|
if (!data) continue;
|
|
13352
14074
|
if (baseName.endsWith("-envs")) {
|
|
13353
14075
|
legacyServiceOverrides[baseName.slice(0, -5)] = data;
|
|
@@ -13355,11 +14077,11 @@ async function runLegacyMigration(dataDir) {
|
|
|
13355
14077
|
legacyEnvOverrides[baseName.slice(0, -12)] = data;
|
|
13356
14078
|
}
|
|
13357
14079
|
}
|
|
13358
|
-
const parsersDir =
|
|
13359
|
-
const parserFiles = (await
|
|
14080
|
+
const parsersDir = join21(dbsDir, "parsers");
|
|
14081
|
+
const parserFiles = (await readdir9(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
|
|
13360
14082
|
const legacyParsers = [];
|
|
13361
14083
|
for (const file of parserFiles) {
|
|
13362
|
-
const data = await readEncrypted(
|
|
14084
|
+
const data = await readEncrypted(join21(parsersDir, file));
|
|
13363
14085
|
if (data?.id) legacyParsers.push(data);
|
|
13364
14086
|
}
|
|
13365
14087
|
const projectMonoId = randomUUID12();
|
|
@@ -13700,7 +14422,7 @@ async function runLegacyMigration(dataDir) {
|
|
|
13700
14422
|
}
|
|
13701
14423
|
let existingSettings = {};
|
|
13702
14424
|
try {
|
|
13703
|
-
existingSettings = JSON.parse(await
|
|
14425
|
+
existingSettings = JSON.parse(await readFile19(join21(dataDir, "settings.json"), "utf-8"));
|
|
13704
14426
|
} catch {
|
|
13705
14427
|
}
|
|
13706
14428
|
const migratedParsers = legacyParsers.map((p) => ({
|
|
@@ -13721,11 +14443,11 @@ ${p.transform ?? ""}`,
|
|
|
13721
14443
|
...migratedParsers
|
|
13722
14444
|
]
|
|
13723
14445
|
};
|
|
13724
|
-
await atomicWrite(
|
|
13725
|
-
await atomicWrite(
|
|
13726
|
-
await atomicWrite(
|
|
13727
|
-
await atomicWrite(
|
|
13728
|
-
await atomicWrite(
|
|
14446
|
+
await atomicWrite(join21(dataDir, "projects.json"), runeProjects);
|
|
14447
|
+
await atomicWrite(join21(dataDir, "services.json"), runeServices);
|
|
14448
|
+
await atomicWrite(join21(dataDir, "environments.json"), runeEnvs);
|
|
14449
|
+
await atomicWrite(join21(dataDir, "environment-overrides.json"), flatOverrides);
|
|
14450
|
+
await atomicWrite(join21(dataDir, "settings.json"), newSettings);
|
|
13729
14451
|
const svcEnvCount = runeEnvs.filter((e) => e.scope === "service").length;
|
|
13730
14452
|
console.log(`[legacy-migration] Written: ${runeProjects.length} projects, ${runeServices.length} services, ${runeEnvs.length} environments (${svcEnvCount} service-scoped)`);
|
|
13731
14453
|
console.log(`[legacy-migration] Overrides: ${Object.keys(flatOverrides).length}, custom parsers: ${migratedParsers.length} (all disabled \u2014 rewrite required)`);
|
|
@@ -13736,12 +14458,12 @@ ${p.transform ?? ""}`,
|
|
|
13736
14458
|
}
|
|
13737
14459
|
async function findLegacyKeyInBackup(dataDir) {
|
|
13738
14460
|
try {
|
|
13739
|
-
const entries = await
|
|
14461
|
+
const entries = await readdir9(dataDir);
|
|
13740
14462
|
const backupDirs = entries.filter((e) => e.startsWith("backup-")).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).reverse();
|
|
13741
14463
|
for (const dir of backupDirs) {
|
|
13742
|
-
const keyJsonPath =
|
|
14464
|
+
const keyJsonPath = join21(dataDir, dir, "dbs", "encryption-key.json");
|
|
13743
14465
|
try {
|
|
13744
|
-
const raw = await
|
|
14466
|
+
const raw = await readFile19(keyJsonPath, "utf-8");
|
|
13745
14467
|
const parsed = JSON.parse(raw);
|
|
13746
14468
|
if (!parsed.encryptionKey) continue;
|
|
13747
14469
|
const sodium = await getSodium();
|
|
@@ -13759,18 +14481,17 @@ async function findLegacyKeyInBackup(dataDir) {
|
|
|
13759
14481
|
return null;
|
|
13760
14482
|
}
|
|
13761
14483
|
async function runOverrideOnlyMigration(dataDir) {
|
|
13762
|
-
const { resolve:
|
|
13763
|
-
const abDataDir =
|
|
13764
|
-
const abCwd = resolve6(process.cwd());
|
|
14484
|
+
const { resolve: resolve7 } = await import("path");
|
|
14485
|
+
const abDataDir = resolve7(dataDir);
|
|
13765
14486
|
const candidateDirs = [
|
|
13766
|
-
|
|
13767
|
-
|
|
14487
|
+
join21(dirname10(env.DATA_DIR), ".runeya", "dbs", "overrides"),
|
|
14488
|
+
join21(abDataDir, "dbs", "overrides")
|
|
13768
14489
|
];
|
|
13769
14490
|
let overridesDir = null;
|
|
13770
14491
|
let overrideFiles = [];
|
|
13771
14492
|
for (const dir of candidateDirs) {
|
|
13772
14493
|
try {
|
|
13773
|
-
const files = (await
|
|
14494
|
+
const files = (await readdir9(dir)).filter((f) => f.endsWith(".encrypted.json"));
|
|
13774
14495
|
if (files.length > 0) {
|
|
13775
14496
|
overridesDir = dir;
|
|
13776
14497
|
overrideFiles = files;
|
|
@@ -13780,7 +14501,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13780
14501
|
}
|
|
13781
14502
|
}
|
|
13782
14503
|
if (!overridesDir || overrideFiles.length === 0) return false;
|
|
13783
|
-
const legacyKeyPath =
|
|
14504
|
+
const legacyKeyPath = join21(overridesDir, "..", "encryption-key.json");
|
|
13784
14505
|
try {
|
|
13785
14506
|
await access2(legacyKeyPath);
|
|
13786
14507
|
return false;
|
|
@@ -13798,7 +14519,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13798
14519
|
const envNameToId = {};
|
|
13799
14520
|
let envsRaw;
|
|
13800
14521
|
try {
|
|
13801
|
-
envsRaw = JSON.parse(await
|
|
14522
|
+
envsRaw = JSON.parse(await readFile19(join21(abDataDir, "environments.json"), "utf-8"));
|
|
13802
14523
|
for (const _env of envsRaw) {
|
|
13803
14524
|
const env2 = _env;
|
|
13804
14525
|
if (env2["name"] && env2["id"]) {
|
|
@@ -13817,7 +14538,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13817
14538
|
let servicesRaw = [];
|
|
13818
14539
|
const serviceNameToId = {};
|
|
13819
14540
|
try {
|
|
13820
|
-
servicesRaw = JSON.parse(await
|
|
14541
|
+
servicesRaw = JSON.parse(await readFile19(join21(abDataDir, "services.json"), "utf-8"));
|
|
13821
14542
|
for (const _svc of servicesRaw) {
|
|
13822
14543
|
const svc = _svc;
|
|
13823
14544
|
if (svc["name"] && svc["id"]) serviceNameToId[svc["name"]] = svc["id"];
|
|
@@ -13834,7 +14555,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13834
14555
|
const envName = baseName.slice(0, -"-environment".length);
|
|
13835
14556
|
let data;
|
|
13836
14557
|
try {
|
|
13837
|
-
data = await decryptLegacyFile(
|
|
14558
|
+
data = await decryptLegacyFile(join21(overridesDir, file), sodiumKey, sodium);
|
|
13838
14559
|
} catch (err) {
|
|
13839
14560
|
console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
|
|
13840
14561
|
continue;
|
|
@@ -13861,7 +14582,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13861
14582
|
}
|
|
13862
14583
|
}
|
|
13863
14584
|
if (Object.keys(flatOverrides).length > 0) {
|
|
13864
|
-
await atomicWrite(
|
|
14585
|
+
await atomicWrite(join21(abDataDir, "environment-overrides.json"), flatOverrides);
|
|
13865
14586
|
console.log(`[override-migration] Written ${Object.keys(flatOverrides).length} override(s) to environment-overrides.json`);
|
|
13866
14587
|
}
|
|
13867
14588
|
let envsModified = false;
|
|
@@ -13871,7 +14592,7 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13871
14592
|
const serviceName = baseName.slice(0, -"-envs".length);
|
|
13872
14593
|
let data;
|
|
13873
14594
|
try {
|
|
13874
|
-
data = await decryptLegacyFile(
|
|
14595
|
+
data = await decryptLegacyFile(join21(overridesDir, file), sodiumKey, sodium);
|
|
13875
14596
|
} catch (err) {
|
|
13876
14597
|
console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
|
|
13877
14598
|
continue;
|
|
@@ -13930,12 +14651,12 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13930
14651
|
}
|
|
13931
14652
|
}
|
|
13932
14653
|
if (envsModified) {
|
|
13933
|
-
await atomicWrite(
|
|
14654
|
+
await atomicWrite(join21(abDataDir, "environments.json"), envsRaw);
|
|
13934
14655
|
console.log("[override-migration] Merged service overrides into environments.json");
|
|
13935
14656
|
}
|
|
13936
14657
|
const migratedDir = overridesDir.replace(/overrides$/, "overrides-migrated");
|
|
13937
14658
|
try {
|
|
13938
|
-
await
|
|
14659
|
+
await rename15(overridesDir, migratedDir);
|
|
13939
14660
|
console.log(`[override-migration] Moved overrides/ \u2192 overrides-migrated/`);
|
|
13940
14661
|
} catch (err) {
|
|
13941
14662
|
console.warn(`[override-migration] Could not rename overrides dir: ${err.message}`);
|
|
@@ -13945,8 +14666,8 @@ async function runOverrideOnlyMigration(dataDir) {
|
|
|
13945
14666
|
}
|
|
13946
14667
|
|
|
13947
14668
|
// ../server/src/services/server-state.ts
|
|
13948
|
-
import { join as
|
|
13949
|
-
import { stat as
|
|
14669
|
+
import { join as join22 } from "path";
|
|
14670
|
+
import { stat as stat10 } from "fs/promises";
|
|
13950
14671
|
var ENCRYPTED_KEY_FILENAME2 = ".encryption.key.enc";
|
|
13951
14672
|
var VERIFY_FILENAME2 = ".runeya-verify";
|
|
13952
14673
|
async function getServerState() {
|
|
@@ -13955,36 +14676,43 @@ async function getServerState() {
|
|
|
13955
14676
|
let hasVerify = false;
|
|
13956
14677
|
let hasData = false;
|
|
13957
14678
|
try {
|
|
13958
|
-
await
|
|
14679
|
+
await stat10(join22(dataDir, ENCRYPTED_KEY_FILENAME2));
|
|
13959
14680
|
hasEncFile = true;
|
|
13960
14681
|
} catch {
|
|
13961
14682
|
}
|
|
13962
14683
|
try {
|
|
13963
|
-
await
|
|
14684
|
+
await stat10(join22(dataDir, VERIFY_FILENAME2));
|
|
13964
14685
|
hasVerify = true;
|
|
13965
14686
|
} catch {
|
|
13966
14687
|
}
|
|
13967
14688
|
try {
|
|
13968
|
-
await
|
|
14689
|
+
await stat10(join22(dataDir, "projects.json"));
|
|
13969
14690
|
hasData = true;
|
|
13970
14691
|
} catch {
|
|
13971
14692
|
}
|
|
13972
14693
|
try {
|
|
13973
|
-
await
|
|
14694
|
+
await stat10(join22(dataDir, "project.json"));
|
|
13974
14695
|
hasData = true;
|
|
13975
14696
|
} catch {
|
|
13976
14697
|
}
|
|
13977
14698
|
if (hasEncryptionKey()) {
|
|
13978
|
-
return {
|
|
14699
|
+
return {
|
|
14700
|
+
state: "unlocked",
|
|
14701
|
+
hasVerifyFile: false,
|
|
14702
|
+
isLegacyMigration: false,
|
|
14703
|
+
hasExistingData: hasData,
|
|
14704
|
+
legacyVault: await legacyVaultRoot()
|
|
14705
|
+
};
|
|
13979
14706
|
}
|
|
13980
14707
|
if (hasEncFile) {
|
|
13981
|
-
return { state: "locked", hasVerifyFile: hasVerify, isLegacyMigration: false, hasExistingData: hasData };
|
|
14708
|
+
return { state: "locked", hasVerifyFile: hasVerify, isLegacyMigration: false, hasExistingData: hasData, legacyVault: null };
|
|
13982
14709
|
}
|
|
13983
14710
|
return {
|
|
13984
14711
|
state: "setup",
|
|
13985
14712
|
hasVerifyFile: hasVerify,
|
|
13986
14713
|
isLegacyMigration: hasPendingLegacyKey(),
|
|
13987
|
-
hasExistingData: hasData
|
|
14714
|
+
hasExistingData: hasData,
|
|
14715
|
+
legacyVault: null
|
|
13988
14716
|
};
|
|
13989
14717
|
}
|
|
13990
14718
|
|
|
@@ -14368,7 +15096,7 @@ async function createLocalServer() {
|
|
|
14368
15096
|
console.log("[server] Premier lancement \u2014 setup requis");
|
|
14369
15097
|
}
|
|
14370
15098
|
}
|
|
14371
|
-
const absoluteDataDir =
|
|
15099
|
+
const absoluteDataDir = resolve6(env.DATA_DIR);
|
|
14372
15100
|
console.log(`[server] Local project directory: ${absoluteDataDir}`);
|
|
14373
15101
|
console.log(`[server] Machine directory: ${machineRoot()}`);
|
|
14374
15102
|
if (existsSync2(env.DATA_DIR)) {
|
|
@@ -14416,9 +15144,10 @@ async function createLocalServer() {
|
|
|
14416
15144
|
parentIds: [],
|
|
14417
15145
|
color: "#22c55e",
|
|
14418
15146
|
immutable: true
|
|
14419
|
-
});
|
|
15147
|
+
}, project.orgId);
|
|
14420
15148
|
await projectStore.update({
|
|
14421
15149
|
id: project.id,
|
|
15150
|
+
orgId: project.orgId,
|
|
14422
15151
|
environmentIds: [localEnv.id],
|
|
14423
15152
|
activeEnvironmentId: localEnv.id
|
|
14424
15153
|
});
|
|
@@ -14730,6 +15459,48 @@ async function createLocalServer() {
|
|
|
14730
15459
|
}
|
|
14731
15460
|
}
|
|
14732
15461
|
});
|
|
15462
|
+
app.post("/api/migrate-vault", authLimiter, csrfMiddleware, express.json({ limit: "10kb" }), async (req, res) => {
|
|
15463
|
+
try {
|
|
15464
|
+
const jwt = typeof req.headers.authorization === "string" && req.headers.authorization.startsWith("Bearer ") ? req.headers.authorization.slice(7) : "";
|
|
15465
|
+
if (!isCiMode()) {
|
|
15466
|
+
try {
|
|
15467
|
+
await verifyJwt(jwt);
|
|
15468
|
+
} catch {
|
|
15469
|
+
res.status(401).json({ error: "Session invalide" });
|
|
15470
|
+
return;
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
const machineKey = getEncryptionKey();
|
|
15474
|
+
if (!machineKey) {
|
|
15475
|
+
res.status(409).json({ error: "Serveur verrouill\xE9" });
|
|
15476
|
+
return;
|
|
15477
|
+
}
|
|
15478
|
+
const legacyRoot = await legacyVaultRoot();
|
|
15479
|
+
if (!legacyRoot) {
|
|
15480
|
+
res.status(409).json({ error: "Aucun coffre \xE0 migrer dans ce r\xE9pertoire" });
|
|
15481
|
+
return;
|
|
15482
|
+
}
|
|
15483
|
+
const body = req.body;
|
|
15484
|
+
const masterPassword = body.masterPassword;
|
|
15485
|
+
if (typeof masterPassword !== "string" || masterPassword.length < 1 || masterPassword.length > 1024) {
|
|
15486
|
+
res.status(400).json({ error: "masterPassword invalide" });
|
|
15487
|
+
return;
|
|
15488
|
+
}
|
|
15489
|
+
let legacyKey;
|
|
15490
|
+
try {
|
|
15491
|
+
legacyKey = await openLegacyVault(legacyRoot, masterPassword);
|
|
15492
|
+
} catch {
|
|
15493
|
+
res.status(401).json({ error: "Ancien master password incorrect" });
|
|
15494
|
+
return;
|
|
15495
|
+
}
|
|
15496
|
+
const report = await migrateLegacyVault(legacyRoot, machineRoot(), legacyKey, machineKey, /* @__PURE__ */ new Date());
|
|
15497
|
+
console.log(`[vault-migration] ${legacyRoot} migr\xE9 \u2014 sauvegarde : ${report.backupDir}`);
|
|
15498
|
+
res.json(report);
|
|
15499
|
+
} catch (err) {
|
|
15500
|
+
console.error("[server] Vault migration error:", err);
|
|
15501
|
+
res.status(500).json({ error: err instanceof Error ? err.message : "Erreur interne" });
|
|
15502
|
+
}
|
|
15503
|
+
});
|
|
14733
15504
|
app.get("/:apikey/llms.txt", llmsLimiter, async (req, res) => {
|
|
14734
15505
|
const apikey = Array.isArray(req.params["apikey"]) ? req.params["apikey"][0] : req.params["apikey"];
|
|
14735
15506
|
if (!apikey || !/^[0-9a-f]{64}$/i.test(apikey)) {
|
|
@@ -14964,12 +15735,12 @@ async function createLocalServer() {
|
|
|
14964
15735
|
]);
|
|
14965
15736
|
server.closeAllConnections();
|
|
14966
15737
|
await Promise.race([
|
|
14967
|
-
new Promise((
|
|
14968
|
-
new Promise((
|
|
15738
|
+
new Promise((resolve7) => wss.close(() => resolve7())),
|
|
15739
|
+
new Promise((resolve7) => setTimeout(resolve7, 2e3))
|
|
14969
15740
|
]);
|
|
14970
15741
|
await Promise.race([
|
|
14971
|
-
new Promise((
|
|
14972
|
-
new Promise((
|
|
15742
|
+
new Promise((resolve7) => server.close(() => resolve7())),
|
|
15743
|
+
new Promise((resolve7) => setTimeout(resolve7, 2e3))
|
|
14973
15744
|
]);
|
|
14974
15745
|
console.log("[server] Shutdown complete");
|
|
14975
15746
|
};
|
|
@@ -14990,7 +15761,7 @@ var PullEnvError = class extends Error {
|
|
|
14990
15761
|
};
|
|
14991
15762
|
async function promptPassword() {
|
|
14992
15763
|
process.stderr.write("Master password: ");
|
|
14993
|
-
return new Promise((
|
|
15764
|
+
return new Promise((resolve7) => {
|
|
14994
15765
|
const stdin = process.stdin;
|
|
14995
15766
|
const wasRaw = stdin.isRaw;
|
|
14996
15767
|
if (stdin.isTTY) stdin.setRawMode(true);
|
|
@@ -15003,7 +15774,7 @@ async function promptPassword() {
|
|
|
15003
15774
|
stdin.pause();
|
|
15004
15775
|
stdin.removeListener("data", onData);
|
|
15005
15776
|
process.stderr.write("\n");
|
|
15006
|
-
|
|
15777
|
+
resolve7(input);
|
|
15007
15778
|
} else if (ch === "") {
|
|
15008
15779
|
process.exit(1);
|
|
15009
15780
|
} else if (ch === "\x7F" || ch === "\b") {
|
|
@@ -15066,7 +15837,7 @@ if (process.env["RUNEYA_DATA_DIR"]) {
|
|
|
15066
15837
|
console.log(`[server] Working directory: ${resolved} (from RUNEYA_DATA_DIR)`);
|
|
15067
15838
|
}
|
|
15068
15839
|
function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500) {
|
|
15069
|
-
return new Promise((
|
|
15840
|
+
return new Promise((resolve7, reject) => {
|
|
15070
15841
|
let attempts = 0;
|
|
15071
15842
|
const wssErrorHandler = () => {
|
|
15072
15843
|
};
|
|
@@ -15087,7 +15858,7 @@ function listenWithRetry(server, port, host, wss, maxRetries = 20, delay = 500)
|
|
|
15087
15858
|
server.listen(port, host, () => {
|
|
15088
15859
|
server.removeListener("error", onError);
|
|
15089
15860
|
wss?.removeListener("error", wssErrorHandler);
|
|
15090
|
-
|
|
15861
|
+
resolve7();
|
|
15091
15862
|
});
|
|
15092
15863
|
};
|
|
15093
15864
|
tryListen();
|
|
@@ -15134,4 +15905,4 @@ export {
|
|
|
15134
15905
|
createLocalServer,
|
|
15135
15906
|
pullEnv
|
|
15136
15907
|
};
|
|
15137
|
-
//# sourceMappingURL=src-
|
|
15908
|
+
//# sourceMappingURL=src-5QPLS5TQ.js.map
|