@runeya/runeya 2.0.57 → 2.0.58
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 +138 -103
- package/agent/index.js.map +1 -1
- package/agent-manager-EDFP4VXP.js +7 -0
- package/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/assets/icon.svg +5 -0
- package/{chunk-4665DVGB.js → chunk-NLVDO4EC.js} +164 -10
- package/chunk-NLVDO4EC.js.map +1 -0
- package/chunk-O2KOX4LU.js +66 -0
- package/chunk-O2KOX4LU.js.map +1 -0
- package/desktop-launcher-TM7AABMC.js +256 -0
- package/desktop-launcher-TM7AABMC.js.map +1 -0
- package/desktop-wizard-4CYANX2O.js +209 -0
- package/desktop-wizard-4CYANX2O.js.map +1 -0
- package/free-port-565NMFIZ.js +13 -0
- package/free-port-565NMFIZ.js.map +1 -0
- package/index.js +101 -2
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/{src-6MVU3BDZ.js → src-V3J62F6F.js} +2976 -2474
- package/src-V3J62F6F.js.map +1 -0
- package/startup-failure-FOO7BLHD.js +180 -0
- package/startup-failure-FOO7BLHD.js.map +1 -0
- package/web/assets/AgentScalarChatInterface.vue-EIqnE2D0.js +13 -0
- package/web/assets/AgentTerminalPage-5kH1RTj6.js +1 -0
- package/web/assets/AiChatPage-DvPM0b-g.js +1 -0
- package/web/assets/AppLayout-CxU9x6en.js +1 -0
- package/web/assets/{AppLayout-CDh-jiJ3.css → AppLayout-Dj8YOvMQ.css} +1 -1
- package/web/assets/AuditPage-BrqreO8a.js +1 -0
- package/web/assets/CloudAccountPage-CzUpIjdc.js +1 -0
- package/web/assets/CloudAccountPage-DRcN1jNp.css +1 -0
- package/web/assets/CloudAccountPage-DS3ZfENB.js +1 -0
- package/web/assets/CloudMessagingPage-D8gBnkFY.js +2 -0
- package/web/assets/DashboardPage-BiskmF6R.js +1 -0
- package/web/assets/DirectoryPickerDialog-Bny02api.css +1 -0
- package/web/assets/DirectoryPickerDialog-CtKWj2hx.js +1 -0
- package/web/assets/{EditorIcon-Bdl2Mco_.js → EditorIcon-B58cXgvK.js} +2 -2
- package/web/assets/EnvironmentForm-DZXx9r78.js +108 -0
- package/web/assets/EnvironmentFormPage-Dl7pdkEM.js +1 -0
- package/web/assets/EnvironmentListPage-B0WX_Hq7.js +1 -0
- package/web/assets/KanbanBoardPage-CBJs26gZ.js +4 -0
- package/web/assets/KanbanListPage-Cz-tlLWA.js +1 -0
- package/web/assets/LaunchProjectPage-8SDkFiLE.js +1 -0
- package/web/assets/LaunchServicesPage-B-ncX3rd.js +1 -0
- package/web/assets/LogViewer-BJNqKt3I.js +3 -0
- package/web/assets/{ProcessControls-DJtuLQwj.js → ProcessControls-NA6_my0q.js} +1 -1
- package/web/assets/ProcessDetailPage-6galJRdd.js +1 -0
- package/web/assets/ProcessDetailPage-CFJfjneL.css +1 -0
- package/web/assets/{ProcessMetrics-BZ6CoGPS.js → ProcessMetrics-CUDjJ4s_.js} +2 -2
- package/web/assets/ProjectDetailPage-CLbj9ZT4.css +1 -0
- package/web/assets/ProjectDetailPage-DKHoFXjW.js +1 -0
- package/web/assets/ProjectListPage-C1xSP6Bq.js +1 -0
- package/web/assets/RandomJobTestPage-BAuT49yE.js +2 -0
- package/web/assets/ScenarioEditorPage-CROCV83A.js +14 -0
- package/web/assets/SendToChatDialog-BfaLVfzw.js +1 -0
- package/web/assets/ServiceControlsBar-DE0In-jH.js +1 -0
- package/web/assets/ServiceDetailPage-0iIv9RqU.js +2 -0
- package/web/assets/{ServiceDetailPage-BRg1byDZ.css → ServiceDetailPage-DDSyACSv.css} +1 -1
- package/web/assets/ServiceFormPage-D-tLX8jE.js +1 -0
- package/web/assets/SettingsPage-ClNw9uEA.js +2063 -0
- package/web/assets/{SettingsPage-BY3dG-wn.css → SettingsPage-ySsQCeYE.css} +1 -1
- package/web/assets/UnlockPage-flNU5Iky.js +1 -0
- package/web/assets/VariablesTable-BnCSYLD4.js +1 -0
- package/web/assets/VaultMigrationPage-BSv9anTA.js +1 -0
- package/web/assets/VersionBadge-zyKx_N_w.js +1 -0
- package/web/assets/WikiPage-C9Bruew6.js +1 -0
- package/web/assets/WikiView-BZZ5mybT.js +1 -0
- package/web/assets/add-scalar-classes-DZb8OCPH.js +387 -0
- package/web/assets/auth-errors-CU6pB2jP.js +1 -0
- package/web/assets/autocomplete-DGLJp6Xx.js +308 -0
- package/web/assets/{basic-shape-diamond.svg-BNEaZNR4.js → basic-shape-diamond.svg-D7lBpWcu.js} +1 -1
- package/web/assets/{basic-shape-hexagon.svg-DkLboKXh.js → basic-shape-hexagon.svg-BIDPqohs.js} +1 -1
- package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-CvXLE0nY.js → basic-shape-primary-circle-ellipse-round.svg-iWUF3CNK.js} +1 -1
- package/web/assets/{basic-shape-primary-square-rectangle.svg-DuBPGbCQ.js → basic-shape-primary-square-rectangle.svg-BQy8tY9i.js} +1 -1
- package/web/assets/{basic-shape-shield.svg-B76nqlMT.js → basic-shape-shield.svg-Dcov5U2N.js} +1 -1
- package/web/assets/{browser-D6cyxfts.js → browser-Dc4T6GL2.js} +1 -1
- package/web/assets/{browser-DCK7QKdh.js → browser-DrMkXWE3.js} +1 -1
- package/web/assets/{card-nlH99ZRa.js → card-DADdd2-i.js} +2 -2
- package/web/assets/{checkbox-tmaSRjCY.js → checkbox-By0TwAz_.js} +3 -3
- package/web/assets/{chip-C6agT-Lk.js → chip-W5HkUiZA.js} +2 -2
- package/web/assets/{client-DSIO6MRA.js → client-Be8SiY8s.js} +2 -2
- package/web/assets/cloud-chat.store-GKWq9kin.js +1 -0
- package/web/assets/{cloud-session-BRhRomCj.js → cloud-session-CJ4n3dbT.js} +1 -1
- package/web/assets/column-P4Yoqttz.js +907 -0
- package/web/assets/{computer-device-desktop-monitor.svg-ClG1er6E.js → computer-device-desktop-monitor.svg-BjBC4Q_B.js} +1 -1
- package/web/assets/{computer-device-desktop.svg-VJa9AdHB.js → computer-device-desktop.svg-C7H8X0sb.js} +1 -1
- package/web/assets/{computer-device-laptop.svg-CHluUfrC.js → computer-device-laptop.svg-GDU5987h.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-Bz7ymNgY.js → computer-device-mobile-phone-android-samsung-back.svg-BcWod8vA.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-android-samsung.svg-BZjCYTDe.js → computer-device-mobile-phone-android-samsung.svg-Cj58lY5g.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-B6hMc7YN.js → computer-device-mobile-phone-iphone-x-back.svg-CtJM7Ul_.js} +1 -1
- package/web/assets/{computer-device-mobile-phone-iphone-x.svg-BDackkJX.js → computer-device-mobile-phone-iphone-x.svg-BiSi4riW.js} +1 -1
- package/web/assets/{computer-device-mobile-tablet-touch.svg-DLepUSe0.js → computer-device-mobile-tablet-touch.svg-BAMVDwHH.js} +1 -1
- package/web/assets/{computer-device-mobile-tablet.svg-CpU7bs1J.js → computer-device-mobile-tablet.svg-quWU62em.js} +1 -1
- package/web/assets/{computer-device-network-ethernet-cat6.svg-BIPoN5dZ.js → computer-device-network-ethernet-cat6.svg-eoCbNWkF.js} +1 -1
- package/web/assets/{computer-device-network-lan-www.svg-DjBnI47W.js → computer-device-network-lan-www.svg-AR5jmH9w.js} +1 -1
- package/web/assets/{computer-device-network-wifi-connection.svg-jL_T26gB.js → computer-device-network-wifi-connection.svg-C3-i1o4I.js} +1 -1
- package/web/assets/{computer-device-network-wifi-router.svg-Bt0HKV-a.js → computer-device-network-wifi-router.svg-D8_62H2U.js} +1 -1
- package/web/assets/{cssMode-l83Ab_FG.js → cssMode-CfDickSl.js} +1 -1
- package/web/assets/{divider-0la1DK7n.js → divider-DOwWKUnp.js} +2 -2
- package/web/assets/{ecology-science-erlenmeyer-flask.svg-jGfcZJk6.js → ecology-science-erlenmeyer-flask.svg-bmj1315r.js} +1 -1
- package/web/assets/{fieldset-Dnw-5ZTN.js → fieldset-CM6GY5DX.js} +2 -2
- package/web/assets/{freemarker2-CKxYYqUc.js → freemarker2-B_0Gh4L-.js} +1 -1
- package/web/assets/{handlebars-DwAvjXsf.js → handlebars-D7Oha3sz.js} +1 -1
- package/web/assets/{html-C8HeZ91y.js → html-BDq-ql3l.js} +1 -1
- package/web/assets/{htmlMode-DJLqukef.js → htmlMode-DVuzX8LQ.js} +1 -1
- package/web/assets/{image-flash-lightning.svg-BRqL2x81.js → image-flash-lightning.svg-pdHiRywD.js} +1 -1
- package/web/assets/{image-picture-flower.svg-CD2Yyx7q.js → image-picture-flower.svg-BJarIt4k.js} +1 -1
- package/web/assets/{index-D-sVY9DJ.js → index-Dk3h9927.js} +225 -225
- package/web/assets/{interface-alert-exclamation-diamond.svg-C1jZyyiH.js → interface-alert-exclamation-diamond.svg-CZUf0TtR.js} +1 -1
- package/web/assets/{interface-alert-exclamation-triangle-warning.svg-CJRjxSxB.js → interface-alert-exclamation-triangle-warning.svg-B1qbmOvN.js} +1 -1
- package/web/assets/{interface-alert-information-circle.svg-_yhjZAB6.js → interface-alert-information-circle.svg-DAH6I2Rk.js} +1 -1
- package/web/assets/{interface-award-crown.svg-D11H_zuN.js → interface-award-crown.svg-Co-un_0E.js} +1 -1
- package/web/assets/{interface-bookmark.svg-CGayI3g0.js → interface-bookmark-tag.svg-DCeSL4gX.js} +1 -1
- package/web/assets/{interface-bookmark-tag.svg-B-l2i8Ii.js → interface-bookmark.svg-BoAQ5WAA.js} +1 -1
- package/web/assets/{interface-calendar-date-one.svg-CJxH-duP.js → interface-calendar-date-one.svg-KM5TH_vj.js} +1 -1
- package/web/assets/{interface-content-book-open-pages.svg-CKlbHpZM.js → interface-content-book-open-pages.svg-2t-0H_Nl.js} +1 -1
- package/web/assets/{interface-content-book-page.svg-ClW0JcO1.js → interface-content-book-page.svg-Dx9U_5uo.js} +1 -1
- package/web/assets/{interface-content-file.svg-vXnq-VTU.js → interface-content-file.svg-CW_9AGDl.js} +1 -1
- package/web/assets/interface-content-folder.svg-C9_1n23c.js +1 -0
- package/web/assets/{interface-content-folder.svg-DVuVP2_n.js → interface-content-folder.svg-DgU2KM8d.js} +1 -1
- package/web/assets/{interface-copy-clipboard.svg-By6SZiB4.js → interface-copy-clipboard.svg-BXCY1wtO.js} +1 -1
- package/web/assets/{interface-edit-attachment.svg-5FhntK3Y.js → interface-edit-attachment.svg-lJsBgdnd.js} +1 -1
- package/web/assets/{interface-edit-binocular.svg-BNAa3NU3.js → interface-edit-binocular.svg-N_us2_d_.js} +1 -1
- package/web/assets/{interface-edit-magic-wand.svg-CHIex2kh.js → interface-edit-magic-wand.svg-GlHpAoND.js} +1 -1
- package/web/assets/{interface-edit-tool-paint-roller.svg-BOU7EZxD.js → interface-edit-tool-paint-roller.svg-B3s6tHLT.js} +1 -1
- package/web/assets/{interface-edit-tool-pencil.svg-R4MWKJk8.js → interface-edit-tool-pencil.svg-BhJltLPD.js} +1 -1
- package/web/assets/{interface-favorite-award.svg-suD3XTUy.js → interface-favorite-award.svg-CAC4qrCL.js} +1 -1
- package/web/assets/{interface-favorite-flag.svg-hdyn6Kl_.js → interface-favorite-flag.svg-5dLI18uT.js} +1 -1
- package/web/assets/{interface-favorite-heart.svg-CsvpCgqr.js → interface-favorite-heart.svg-DAWMqUS8.js} +1 -1
- package/web/assets/{interface-favorite-star.svg-BYnYZej4.js → interface-favorite-star.svg-C9DICFBb.js} +1 -1
- package/web/assets/{interface-favorite-stars-sparkles.svg-CJF8bktG.js → interface-favorite-stars-sparkles.svg-zD5-a95u.js} +1 -1
- package/web/assets/{interface-hierarchy-flowchart.svg-Cz8Kp45m.js → interface-hierarchy-flowchart.svg-BzAAQM0v.js} +1 -1
- package/web/assets/{interface-home-house.svg-C9GgEmKT.js → interface-home-house.svg-CB_mzszz.js} +1 -1
- package/web/assets/{interface-hyperlink.svg-T_oHSPOF.js → interface-hyperlink.svg-BUMVlOAE.js} +1 -1
- package/web/assets/{interface-lighting-brightness.svg-Ds5ZO6oa.js → interface-lighting-brightness.svg-wPXo2pZW.js} +1 -1
- package/web/assets/{interface-lock-closed.svg-CoMPAlnI.js → interface-lock-closed.svg-Qj4fLj5D.js} +1 -1
- package/web/assets/{interface-lock-open-unlock.svg-jGLDczcA.js → interface-lock-open-unlock.svg-PaaFsUiU.js} +1 -1
- package/web/assets/{interface-login-key.svg-CDeEaRf5.js → interface-login-key.svg-By-IZ7EH.js} +1 -1
- package/web/assets/{interface-search.svg-Cy4Q-xYo.js → interface-search.svg-D1s5Gsyk.js} +1 -1
- package/web/assets/{interface-setting-cog.svg-BDhQZOJ4.js → interface-setting-cog.svg-BIjZVOwf.js} +1 -1
- package/web/assets/{interface-share-megaphone-bullhorn.svg-DagGdEnC.js → interface-share-megaphone-bullhorn.svg-1oPoDdIL.js} +1 -1
- package/web/assets/{interface-share-rocket.svg-CKJ5nUEh.js → interface-share-rocket.svg-B07UdOH6.js} +1 -1
- package/web/assets/{interface-share-satellite.svg-BK1pCz3B.js → interface-share-satellite.svg-AxSAr1JP.js} +1 -1
- package/web/assets/{interface-share-space-ship.svg-KdgeixBZ.js → interface-share-space-ship.svg-ghRJYkXG.js} +1 -1
- package/web/assets/{interface-share.svg-ChAZ94rW.js → interface-share.svg-jVvV5wf9.js} +1 -1
- package/web/assets/{interface-signal-square.svg-DzuPfXUh.js → interface-signal-square.svg-BB8KvxNI.js} +1 -1
- package/web/assets/{interface-time-clock-circle.svg-B4ynuu4H.js → interface-time-clock-circle.svg-Bt_vXLib.js} +1 -1
- package/web/assets/{interface-time-hour-glass.svg-CwKlC1Na.js → interface-time-hour-glass.svg-CqsWXDx3.js} +1 -1
- package/web/assets/{interface-users-multiple.svg-Dr3rSYsr.js → interface-users-multiple.svg-DfZzhCkI.js} +1 -1
- package/web/assets/{interface-weather-moon.svg-k_uau_ay.js → interface-weather-moon.svg-B5X37fBl.js} +1 -1
- package/web/assets/{javascript-Bx-xJAtR.js → javascript-DWxMn_QC.js} +1 -1
- package/web/assets/{jsonMode-CF72gzTX.js → jsonMode-BWOZzM3d.js} +2 -2
- package/web/assets/{liquid-Du7COcWK.js → liquid-B0VtIwJX.js} +1 -1
- package/web/assets/{lspLanguageFeatures-B0b9esI0.js → lspLanguageFeatures-DXFZNqLk.js} +3 -3
- package/web/assets/{mail-chat-bubble-square.svg-Dss1BS5k.js → mail-chat-bubble-square.svg-DVi8sPM-.js} +1 -1
- package/web/assets/{mail-send-email-paper-airplane.svg-BjlSFw6x.js → mail-send-email-paper-airplane.svg-Dlk35rWL.js} +1 -1
- package/web/assets/{mail-send-envelope.svg-CpGqZ3T_.js → mail-send-envelope.svg-DpEE06QS.js} +1 -1
- package/web/assets/{mdx-DgIEovcA.js → mdx-XjdLNHo4.js} +1 -1
- package/web/assets/{message-n9JmpxQB.js → message-TASaHoOx.js} +2 -2
- package/web/assets/{minus-Eq7h2HNM.js → minus-BO9HwuhK.js} +2 -2
- package/web/assets/modal-B0K9qtIb.js +1 -0
- package/web/assets/modal-Dadfdblm.js +5 -0
- package/web/assets/{money-cashier-receipt.svg-DVDhS_Zc.js → money-cashier-receipt.svg-D3T1gdZO.js} +1 -1
- package/web/assets/{money-currency-dollar-pay.svg-DRnO0pzf.js → money-currency-dollar-pay.svg-RhoFWz8z.js} +1 -1
- package/web/assets/{money-graph-arrow-increase.svg-Beaf3gcH.js → money-graph-arrow-increase.svg-BENn1K5R.js} +1 -1
- package/web/assets/{money-graph-bar-chart-increase.svg-Bx83srA6.js → money-graph-bar-chart-increase.svg-COGPwero.js} +1 -1
- package/web/assets/{multiselect-BzaOLdpd.js → multiselect-QI9V-o7t.js} +3 -3
- package/web/assets/{nature-ecology-leaf.svg-BPF0Z-R-.js → nature-ecology-leaf.svg-CyQJuX9P.js} +1 -1
- package/web/assets/{password-BOruXB1m.js → password-68lVUSdj.js} +4 -4
- package/web/assets/{phone-telephone.svg-CoyP7Dxe.js → phone-telephone.svg-Cp49xUlu.js} +1 -1
- package/web/assets/{plus-D3SKHEeC.js → plus-CtAUJUu2.js} +6 -6
- package/web/assets/process.store-DkO9CCEs.js +1 -0
- package/web/assets/{programming-bug.svg-BnJMsv8E.js → programming-bug.svg-DJCw-G7S.js} +1 -1
- package/web/assets/{programming-cloud.svg-CQYYCVwK.js → programming-cloud.svg-Dawch6nl.js} +1 -1
- package/web/assets/{programming-computer-database-server.svg-B8efwNUW.js → programming-computer-database-server.svg-wM3gh9nl.js} +1 -1
- package/web/assets/{programming-computer-database.svg-DNTDIyeu.js → programming-computer-database.svg-B-CBQ4FN.js} +1 -1
- package/web/assets/{programming-module-four-layout.svg-CDOGccyo.js → programming-module-four-layout.svg-B8K3IYUP.js} +1 -1
- package/web/assets/{programming-module-three.svg-Bge3Bi-7.js → programming-module-three.svg-BVHpxetj.js} +1 -1
- package/web/assets/{programming-module.svg-CzqYRXSb.js → programming-module.svg-CXID9wrd.js} +1 -1
- package/web/assets/{programming-script-code.svg-C5p0kA7v.js → programming-script-code.svg-lSz-9Xfp.js} +1 -1
- package/web/assets/{python-UpylI8e4.js → python-DL0o4ZFa.js} +1 -1
- package/web/assets/{razor-aZxkyrH6.js → razor-1T_DtFYV.js} +1 -1
- package/web/assets/router-BKOiPGCa.js +1 -0
- package/web/assets/settings-scope-DrVQNSTC.js +1 -0
- package/web/assets/{settings.store-BOiI53kz.js → settings.store-xvHd8xGI.js} +1 -1
- package/web/assets/{shopping-cart.svg-CdyNNU8T.js → shopping-cart.svg-TZA8HEQB.js} +1 -1
- package/web/assets/{shopping-gift-present.svg-oYuij_wi.js → shopping-gift-present.svg-CRGiXfBk.js} +1 -1
- package/web/assets/{shopping-shipping-box-parcel-package.svg-BHHfdC-w.js → shopping-shipping-box-parcel-package.svg-BuEMZoGf.js} +1 -1
- package/web/assets/src-Be-L2-AH.js +1 -0
- package/web/assets/{src-C0e6s3a3.js → src-Dy-bHT95.js} +12 -12
- package/web/assets/tabpanel-BP3edJSv.js +150 -0
- package/web/assets/{tag-new-circle.svg-De8jHJc3.js → tag-new-circle.svg-CsOt9-U5.js} +1 -1
- package/web/assets/{toggleswitch-7N_rrZCL.js → toggleswitch-B49OhBFD.js} +2 -2
- package/web/assets/{travel-map-earth-globe.svg-DDerkPaO.js → travel-map-earth-globe.svg-BTR5ahca.js} +1 -1
- package/web/assets/trpc-hOuUVUMw.js +1 -0
- package/web/assets/{tsMode-Bm3dVodi.js → tsMode-BUrbWgsu.js} +5 -5
- package/web/assets/{typescript-Cw6KoAtM.js → typescript-tKGzqX3Z.js} +1 -1
- package/web/assets/useAiChatDrawer-CZS0RLp8.js +1 -0
- package/web/assets/useCloudOrg-0VY86rgI.js +93 -0
- package/web/assets/{useCloudOrg-B4VXAHsj.css → useCloudOrg-CDe4gvZH.css} +1 -1
- package/web/assets/{useProjectCatalog-O62WCLX4.js → useProjectCatalog-C2BH8Bnr.js} +1 -1
- package/web/assets/{xml-D4uEa569.js → xml-4CdtJV3Y.js} +1 -1
- package/web/assets/{yaml-y9iDkUjP.js → yaml-Cj7LK0oJ.js} +1 -1
- package/web/index.html +1 -1
- package/agent-manager-452D7XW5.js +0 -7
- package/chunk-4665DVGB.js.map +0 -1
- package/src-6MVU3BDZ.js.map +0 -1
- package/web/assets/AgentScalarChatInterface.vue-82XG5NKb.js +0 -13
- package/web/assets/AgentTerminalPage-BwQhPdyw.js +0 -1
- package/web/assets/AiChatPage-BRnRhmhg.js +0 -1
- package/web/assets/AppLayout-BZrNmMDL.js +0 -1
- package/web/assets/AuditPage-Dds0912d.js +0 -1
- package/web/assets/CloudAccountPage-49RVZf9h.js +0 -1
- package/web/assets/CloudAccountPage-CVAMkCOF.css +0 -1
- package/web/assets/CloudAccountPage-KipqXtVJ.js +0 -1
- package/web/assets/CloudMessagingPage-H43XvI3d.js +0 -2
- package/web/assets/DashboardPage-D9kDTU7y.js +0 -1
- package/web/assets/EnvironmentForm-B_gviXu_.js +0 -108
- package/web/assets/EnvironmentFormPage-BHrfU03w.js +0 -1
- package/web/assets/EnvironmentListPage-CW3FpI79.js +0 -1
- package/web/assets/EnvironmentSelector-B9SZvB3I.css +0 -1
- package/web/assets/EnvironmentSelector-C49ocjBM.js +0 -1
- package/web/assets/KanbanBoardPage-DUgJRQK7.js +0 -4
- package/web/assets/KanbanListPage-D8LIr7sZ.js +0 -1
- package/web/assets/LaunchProjectPage-BBSKTUnF.js +0 -1
- package/web/assets/LaunchServicesPage-vJU87PHP.js +0 -1
- package/web/assets/LogViewer-D5tSUFpZ.js +0 -3
- package/web/assets/ProcessDetailPage-CzGUsQM9.css +0 -1
- package/web/assets/ProcessDetailPage-DqGqF1Eh.js +0 -1
- package/web/assets/ProjectDetailPage-AFuj4C-h.js +0 -1
- package/web/assets/ProjectDetailPage-hcU-rKhJ.css +0 -1
- package/web/assets/ProjectListPage-C1T_1Y0g.js +0 -1
- package/web/assets/RandomJobTestPage-C760ZS5-.js +0 -2
- package/web/assets/ScenarioEditorPage-DFYcNzHU.js +0 -14
- package/web/assets/SendToChatDialog-D4kPRj-6.js +0 -1
- package/web/assets/ServiceControlsBar-D_7_q5e3.js +0 -1
- package/web/assets/ServiceDetailPage-H8pJ6Dke.js +0 -2
- package/web/assets/ServiceFormPage-DSU5wiDe.js +0 -1
- package/web/assets/SettingsPage-Bo6jEumX.js +0 -2062
- package/web/assets/UnlockPage-B7oIw1Ep.js +0 -1
- package/web/assets/VariablesTable-DREhc-Vd.js +0 -1
- package/web/assets/VaultMigrationPage-BeCx1t1u.js +0 -1
- package/web/assets/VersionBadge-DZw8ffTr.js +0 -1
- package/web/assets/WikiPage-BYOLAA15.js +0 -1
- package/web/assets/WikiView-CA-fg4Xk.js +0 -1
- package/web/assets/add-scalar-classes-wjDwsWcH.js +0 -387
- package/web/assets/auth-errors-BG58jeF5.js +0 -1
- package/web/assets/autocomplete-DFFxoeg7.js +0 -308
- package/web/assets/cloud-chat.store-BAYcx61I.js +0 -1
- package/web/assets/column-sIpdz9_l.js +0 -907
- package/web/assets/interface-content-folder.svg-BDo_OJHD.js +0 -1
- package/web/assets/modal-COpDqWq9.js +0 -5
- package/web/assets/modal-CqJ-XbwV.js +0 -1
- package/web/assets/process.store-dEVcW1iy.js +0 -1
- package/web/assets/router-DgsjS0ri.js +0 -1
- package/web/assets/settings-scope-BXA0BjX7.js +0 -1
- package/web/assets/src-BscSu0ry.js +0 -1
- package/web/assets/tabpanel-CEUw3G7r.js +0 -150
- package/web/assets/trpc-Bv0dS7QZ.js +0 -1
- package/web/assets/useAiChatDrawer-D9n1q5jE.js +0 -1
- package/web/assets/useCloudOrg-RH2schZM.js +0 -93
- /package/{agent-manager-452D7XW5.js.map → agent-manager-EDFP4VXP.js.map} +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/free-port.ts
|
|
2
|
+
import { createServer } from "net";
|
|
3
|
+
import { randomInt } from "crypto";
|
|
4
|
+
var MIN_PORT = 1e4;
|
|
5
|
+
var MAX_PORT = 3e4;
|
|
6
|
+
function tryBind(port, host) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const server = createServer();
|
|
9
|
+
server.once("error", () => resolve(null));
|
|
10
|
+
server.listen(port, host, () => resolve(server));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function close(server) {
|
|
14
|
+
return new Promise((resolve) => server.close(() => resolve()));
|
|
15
|
+
}
|
|
16
|
+
async function isPortFree(port, host = "127.0.0.1") {
|
|
17
|
+
const server = await tryBind(port, host);
|
|
18
|
+
if (!server) return false;
|
|
19
|
+
await close(server);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
async function findFreePortPair({ host = "127.0.0.1", attempts = 200 } = {}) {
|
|
23
|
+
for (let i = 0; i < attempts; i++) {
|
|
24
|
+
const base = randomInt(MIN_PORT, MAX_PORT - 1);
|
|
25
|
+
const first = await tryBind(base, host);
|
|
26
|
+
if (!first) continue;
|
|
27
|
+
const second = await tryBind(base + 1, host);
|
|
28
|
+
if (!second) {
|
|
29
|
+
await close(first);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
await Promise.all([close(first), close(second)]);
|
|
33
|
+
return base;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function parsePort(raw) {
|
|
38
|
+
if (raw === void 0) return null;
|
|
39
|
+
const trimmed = raw.trim();
|
|
40
|
+
if (!/^\d+$/.test(trimmed)) return null;
|
|
41
|
+
const port = Number(trimmed);
|
|
42
|
+
if (port < 1 || port > 65535) return null;
|
|
43
|
+
return port;
|
|
44
|
+
}
|
|
45
|
+
async function probeRuneya(port) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/trpc/health.check`, {
|
|
48
|
+
signal: AbortSignal.timeout(1500)
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) return null;
|
|
51
|
+
const body = await res.json();
|
|
52
|
+
const data = body.result?.data;
|
|
53
|
+
if (data?.status !== "ok") return null;
|
|
54
|
+
return { version: data.version ?? "unknown" };
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
isPortFree,
|
|
62
|
+
findFreePortPair,
|
|
63
|
+
parsePort,
|
|
64
|
+
probeRuneya
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=chunk-O2KOX4LU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/free-port.ts"],"sourcesContent":["/**\n * Free-port discovery for desktop launchers.\n *\n * A launcher needs a port it can keep: the whole point of baking one into the\n * shortcut is that clicking it tomorrow lands on the same address. Runeya\n * derives the agent port from the server port (`server + 1`), so a usable\n * suggestion is a *pair* of consecutive free ports, not just one free port.\n */\nimport { createServer, type Server } from 'node:net';\nimport { randomInt } from 'node:crypto';\n\n/**\n * Range the suggestion is drawn from. Deliberately below every platform's\n * ephemeral range (Linux 32768+, macOS and Windows 49152+): a port handed out\n * there could be taken by a transient outbound socket between our check and\n * the next launch.\n */\nconst MIN_PORT = 10_000;\nconst MAX_PORT = 30_000;\n\nfunction tryBind(port: number, host: string): Promise<Server | null> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', () => resolve(null));\n server.listen(port, host, () => resolve(server));\n });\n}\n\nfunction close(server: Server): Promise<void> {\n return new Promise((resolve) => server.close(() => resolve()));\n}\n\n/** True when nothing is listening on `port` for `host`. */\nexport async function isPortFree(port: number, host = '127.0.0.1'): Promise<boolean> {\n const server = await tryBind(port, host);\n if (!server) return false;\n await close(server);\n return true;\n}\n\n/**\n * Find a port `p` such that `p` and `p + 1` are both free, and return `p`.\n *\n * Both are held at the same time before either is released: binding one,\n * closing it, then binding the next would leave a window where the first is\n * reclaimed and the pair is not actually a pair.\n *\n * The result is a suggestion, not a reservation — nothing stops another\n * process from taking the port a moment later. That is why the failure path\n * matters more than the check: a launcher that cannot bind now shows the\n * startup report instead of dying silently.\n */\nexport async function findFreePortPair(\n { host = '127.0.0.1', attempts = 200 }: { host?: string; attempts?: number } = {},\n): Promise<number | null> {\n for (let i = 0; i < attempts; i++) {\n // MAX_PORT - 1 so that base + 1 stays inside the range\n const base = randomInt(MIN_PORT, MAX_PORT - 1);\n\n const first = await tryBind(base, host);\n if (!first) continue;\n\n const second = await tryBind(base + 1, host);\n if (!second) {\n await close(first);\n continue;\n }\n\n await Promise.all([close(first), close(second)]);\n return base;\n }\n return null;\n}\n\n/**\n * Parse a port from user input or a flag. Returns null when unusable.\n *\n * Only plain decimal digits are accepted. `Number()` alone would take \"4e3\"\n * for 4000 and \"0x10\" for 16 — for a port someone typed, those are typos, and\n * silently resolving them to a valid number is worse than refusing them.\n */\nexport function parsePort(raw: string | undefined): number | null {\n if (raw === undefined) return null;\n const trimmed = raw.trim();\n if (!/^\\d+$/.test(trimmed)) return null;\n const port = Number(trimmed);\n if (port < 1 || port > 65_535) return null;\n return port;\n}\n\n/**\n * Ask a port whether a Runeya answers there.\n *\n * `health.check` is public and returns the version, so a positive answer\n * identifies Runeya rather than merely proving the port is taken. That\n * distinction is the whole point: a Runeya already serving the port should be\n * opened, while anything else is a genuine conflict the user must see.\n */\nexport async function probeRuneya(port: number): Promise<{ version: string } | null> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/trpc/health.check`, {\n signal: AbortSignal.timeout(1500),\n });\n if (!res.ok) return null;\n const body = await res.json() as { result?: { data?: { status?: string; version?: string } } };\n const data = body.result?.data;\n if (data?.status !== 'ok') return null;\n return { version: data.version ?? 'unknown' };\n } catch {\n return null;\n }\n}\n"],"mappings":";AAQA,SAAS,oBAAiC;AAC1C,SAAS,iBAAiB;AAQ1B,IAAM,WAAW;AACjB,IAAM,WAAW;AAEjB,SAAS,QAAQ,MAAc,MAAsC;AACnE,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,SAAS,aAAa;AAC5B,WAAO,KAAK,SAAS,MAAM,QAAQ,IAAI,CAAC;AACxC,WAAO,OAAO,MAAM,MAAM,MAAM,QAAQ,MAAM,CAAC;AAAA,EACjD,CAAC;AACH;AAEA,SAAS,MAAM,QAA+B;AAC5C,SAAO,IAAI,QAAQ,CAAC,YAAY,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAC/D;AAGA,eAAsB,WAAW,MAAc,OAAO,aAA+B;AACnF,QAAM,SAAS,MAAM,QAAQ,MAAM,IAAI;AACvC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,MAAM,MAAM;AAClB,SAAO;AACT;AAcA,eAAsB,iBACpB,EAAE,OAAO,aAAa,WAAW,IAAI,IAA0C,CAAC,GACxD;AACxB,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAEjC,UAAM,OAAO,UAAU,UAAU,WAAW,CAAC;AAE7C,UAAM,QAAQ,MAAM,QAAQ,MAAM,IAAI;AACtC,QAAI,CAAC,MAAO;AAEZ,UAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,IAAI;AAC3C,QAAI,CAAC,QAAQ;AACX,YAAM,MAAM,KAAK;AACjB;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,CAAC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,CAAC;AAC/C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASO,SAAS,UAAU,KAAwC;AAChE,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO;AACnC,QAAM,OAAO,OAAO,OAAO;AAC3B,MAAI,OAAO,KAAK,OAAO,MAAQ,QAAO;AACtC,SAAO;AACT;AAUA,eAAsB,YAAY,MAAmD;AACnF,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,oBAAoB,IAAI,0BAA0B;AAAA,MACxE,QAAQ,YAAY,QAAQ,IAAI;AAAA,IAClC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,MAAM,WAAW,KAAM,QAAO;AAClC,WAAO,EAAE,SAAS,KAAK,WAAW,UAAU;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/desktop-launcher.ts
|
|
2
|
+
import { mkdir, writeFile, rm, copyFile, access } from "fs/promises";
|
|
3
|
+
import { execFile } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join, basename, dirname } from "path";
|
|
7
|
+
var exec = promisify(execFile);
|
|
8
|
+
function launcherArgs(opts) {
|
|
9
|
+
const args = ["--from-desktop"];
|
|
10
|
+
if (opts.port !== null) args.push("--port", String(opts.port));
|
|
11
|
+
if (opts.agentPort !== null && opts.agentPort !== (opts.port ?? 4e3) + 1) {
|
|
12
|
+
args.push("--agent-port", String(opts.agentPort));
|
|
13
|
+
}
|
|
14
|
+
return args;
|
|
15
|
+
}
|
|
16
|
+
function slugify(name) {
|
|
17
|
+
const slug = name.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
18
|
+
return slug || "runeya";
|
|
19
|
+
}
|
|
20
|
+
function idFromName(name) {
|
|
21
|
+
const slug = slugify(name);
|
|
22
|
+
return slug.startsWith("runeya") ? slug : `runeya-${slug}`;
|
|
23
|
+
}
|
|
24
|
+
function deriveIdentity(explicitName, workingDir) {
|
|
25
|
+
const name = explicitName ?? (workingDir ? `Runeya \u2014 ${basename(workingDir)}` : "Runeya");
|
|
26
|
+
return { name, id: idFromName(name) };
|
|
27
|
+
}
|
|
28
|
+
async function exists(path) {
|
|
29
|
+
try {
|
|
30
|
+
await access(path);
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function tryExec(file, args, warnings) {
|
|
37
|
+
try {
|
|
38
|
+
await exec(file, args);
|
|
39
|
+
} catch {
|
|
40
|
+
warnings.push(`Commande optionnelle \xE9chou\xE9e : ${file} (le lanceur est cr\xE9\xE9, le menu peut mettre un instant \xE0 l'afficher)`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function desktopExecQuote(value) {
|
|
44
|
+
return `"${value.replace(/["`$\\]/g, (c) => `\\${c}`)}"`;
|
|
45
|
+
}
|
|
46
|
+
function desktopValue(value) {
|
|
47
|
+
return value.replace(/\\/g, "\\\\").replace(/\n/g, " ");
|
|
48
|
+
}
|
|
49
|
+
function xdgDataHome() {
|
|
50
|
+
return process.env["XDG_DATA_HOME"] || join(homedir(), ".local/share");
|
|
51
|
+
}
|
|
52
|
+
function linuxPaths(id) {
|
|
53
|
+
const dataHome = xdgDataHome();
|
|
54
|
+
const appsDir = join(dataHome, "applications");
|
|
55
|
+
return {
|
|
56
|
+
appsDir,
|
|
57
|
+
entry: join(appsDir, `${id}.desktop`),
|
|
58
|
+
iconDir: join(dataHome, "icons/hicolor/scalable/apps"),
|
|
59
|
+
icon: join(dataHome, `icons/hicolor/scalable/apps/${id}.svg`)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function installLinux(opts) {
|
|
63
|
+
const paths = [];
|
|
64
|
+
const warnings = [];
|
|
65
|
+
const p = linuxPaths(opts.id);
|
|
66
|
+
const sourceIcon = join(opts.distDir, "assets/icon.svg");
|
|
67
|
+
let iconName = "application-x-executable";
|
|
68
|
+
if (await exists(sourceIcon)) {
|
|
69
|
+
await mkdir(p.iconDir, { recursive: true });
|
|
70
|
+
await copyFile(sourceIcon, p.icon);
|
|
71
|
+
paths.push(p.icon);
|
|
72
|
+
iconName = opts.id;
|
|
73
|
+
} else {
|
|
74
|
+
warnings.push(`Ic\xF4ne introuvable (${sourceIcon}), ic\xF4ne g\xE9n\xE9rique utilis\xE9e`);
|
|
75
|
+
}
|
|
76
|
+
const exec_ = [opts.nodePath, opts.cliEntry, ...launcherArgs(opts)].map(desktopExecQuote).join(" ");
|
|
77
|
+
const lines = [
|
|
78
|
+
"[Desktop Entry]",
|
|
79
|
+
"Type=Application",
|
|
80
|
+
`Name=${desktopValue(opts.name)}`,
|
|
81
|
+
"Comment=Process and service manager",
|
|
82
|
+
`Exec=${exec_}`,
|
|
83
|
+
`Icon=${iconName}`,
|
|
84
|
+
"Terminal=false",
|
|
85
|
+
"Categories=Development;",
|
|
86
|
+
"StartupNotify=true",
|
|
87
|
+
// Marker used by uninstall-desktop to refuse deleting a hand-written entry
|
|
88
|
+
"X-Runeya-Managed=true"
|
|
89
|
+
];
|
|
90
|
+
if (opts.workingDir) lines.push(`Path=${desktopValue(opts.workingDir)}`);
|
|
91
|
+
await mkdir(p.appsDir, { recursive: true });
|
|
92
|
+
await writeFile(p.entry, lines.join("\n") + "\n", { mode: 493 });
|
|
93
|
+
paths.push(p.entry);
|
|
94
|
+
await tryExec("update-desktop-database", [p.appsDir], warnings);
|
|
95
|
+
return { paths, warnings };
|
|
96
|
+
}
|
|
97
|
+
async function uninstallLinux(id) {
|
|
98
|
+
const paths = [];
|
|
99
|
+
const warnings = [];
|
|
100
|
+
const p = linuxPaths(id);
|
|
101
|
+
for (const target of [p.entry, p.icon]) {
|
|
102
|
+
if (await exists(target)) {
|
|
103
|
+
await rm(target, { force: true });
|
|
104
|
+
paths.push(target);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (paths.length) await tryExec("update-desktop-database", [p.appsDir], warnings);
|
|
108
|
+
return { paths, warnings };
|
|
109
|
+
}
|
|
110
|
+
function plistString(value) {
|
|
111
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
112
|
+
}
|
|
113
|
+
function shQuote(value) {
|
|
114
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
115
|
+
}
|
|
116
|
+
function macAppPath(name) {
|
|
117
|
+
return join(homedir(), "Applications", `${name}.app`);
|
|
118
|
+
}
|
|
119
|
+
async function installMac(opts) {
|
|
120
|
+
const warnings = [];
|
|
121
|
+
const appPath = macAppPath(opts.name);
|
|
122
|
+
const macosDir = join(appPath, "Contents/MacOS");
|
|
123
|
+
const resourcesDir = join(appPath, "Contents/Resources");
|
|
124
|
+
const execName = opts.id;
|
|
125
|
+
await mkdir(macosDir, { recursive: true });
|
|
126
|
+
await mkdir(resourcesDir, { recursive: true });
|
|
127
|
+
const stub = [
|
|
128
|
+
"#!/bin/sh",
|
|
129
|
+
"# Generated by `runeya install-desktop` \u2014 safe to delete.",
|
|
130
|
+
opts.workingDir ? `cd ${shQuote(opts.workingDir)} || exit 1` : "",
|
|
131
|
+
`exec ${[opts.nodePath, opts.cliEntry, ...launcherArgs(opts)].map(shQuote).join(" ")} "$@"`,
|
|
132
|
+
""
|
|
133
|
+
].filter(Boolean).join("\n");
|
|
134
|
+
await writeFile(join(macosDir, execName), stub, { mode: 493 });
|
|
135
|
+
const sourceIcns = join(opts.distDir, "assets/icon.icns");
|
|
136
|
+
let iconKey = "";
|
|
137
|
+
if (await exists(sourceIcns)) {
|
|
138
|
+
await copyFile(sourceIcns, join(resourcesDir, `${execName}.icns`));
|
|
139
|
+
iconKey = `
|
|
140
|
+
<key>CFBundleIconFile</key><string>${plistString(execName)}</string>`;
|
|
141
|
+
} else {
|
|
142
|
+
warnings.push(`Ic\xF4ne introuvable (${sourceIcns}), ic\xF4ne g\xE9n\xE9rique utilis\xE9e`);
|
|
143
|
+
}
|
|
144
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
145
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
146
|
+
<plist version="1.0">
|
|
147
|
+
<dict>
|
|
148
|
+
<key>CFBundleName</key><string>${plistString(opts.name)}</string>
|
|
149
|
+
<key>CFBundleDisplayName</key><string>${plistString(opts.name)}</string>
|
|
150
|
+
<key>CFBundleExecutable</key><string>${plistString(execName)}</string>
|
|
151
|
+
<key>CFBundleIdentifier</key><string>dev.runeya.launcher.${plistString(opts.id)}</string>
|
|
152
|
+
<key>CFBundlePackageType</key><string>APPL</string>${iconKey}
|
|
153
|
+
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
|
|
154
|
+
<key>CFBundleShortVersionString</key><string>1.0</string>
|
|
155
|
+
<key>NSHighResolutionCapable</key><true/>
|
|
156
|
+
</dict>
|
|
157
|
+
</plist>
|
|
158
|
+
`;
|
|
159
|
+
await writeFile(join(appPath, "Contents/Info.plist"), plist);
|
|
160
|
+
await tryExec(
|
|
161
|
+
"/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",
|
|
162
|
+
["-f", appPath],
|
|
163
|
+
warnings
|
|
164
|
+
);
|
|
165
|
+
return { paths: [appPath], warnings };
|
|
166
|
+
}
|
|
167
|
+
async function uninstallMac(name) {
|
|
168
|
+
const appPath = macAppPath(name);
|
|
169
|
+
if (!await exists(appPath)) return { paths: [], warnings: [] };
|
|
170
|
+
await rm(appPath, { recursive: true, force: true });
|
|
171
|
+
return { paths: [appPath], warnings: [] };
|
|
172
|
+
}
|
|
173
|
+
function psQuote(value) {
|
|
174
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
175
|
+
}
|
|
176
|
+
function windowsLnkPath(name) {
|
|
177
|
+
const appData = process.env["APPDATA"] || join(homedir(), "AppData/Roaming");
|
|
178
|
+
return join(appData, "Microsoft/Windows/Start Menu/Programs", `${name}.lnk`);
|
|
179
|
+
}
|
|
180
|
+
async function runPowerShell(script, warnings) {
|
|
181
|
+
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
182
|
+
try {
|
|
183
|
+
await exec("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", encoded]);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
warnings.push(`PowerShell a \xE9chou\xE9 : ${err.message}`);
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function installWindows(opts) {
|
|
190
|
+
const warnings = [];
|
|
191
|
+
const lnk = windowsLnkPath(opts.name);
|
|
192
|
+
const icoPath = join(opts.distDir, "assets/icon.ico");
|
|
193
|
+
const iconPath = await exists(icoPath) ? icoPath : null;
|
|
194
|
+
if (!iconPath) warnings.push(`Ic\xF4ne introuvable (${icoPath}), ic\xF4ne de Node utilis\xE9e`);
|
|
195
|
+
const script = [
|
|
196
|
+
`$ws = New-Object -ComObject WScript.Shell`,
|
|
197
|
+
`$sc = $ws.CreateShortcut(${psQuote(lnk)})`,
|
|
198
|
+
`$sc.TargetPath = ${psQuote(opts.nodePath)}`,
|
|
199
|
+
`$sc.Arguments = ${psQuote([`"${opts.cliEntry}"`, ...launcherArgs(opts)].join(" "))}`,
|
|
200
|
+
opts.workingDir ? `$sc.WorkingDirectory = ${psQuote(opts.workingDir)}` : "",
|
|
201
|
+
`$sc.Description = 'Runeya \u2014 process and service manager'`,
|
|
202
|
+
iconPath ? `$sc.IconLocation = ${psQuote(iconPath)}` : "",
|
|
203
|
+
// WindowStyle 7 = minimized: the console still exists (Node needs one) but
|
|
204
|
+
// stays out of the way while the browser opens.
|
|
205
|
+
`$sc.WindowStyle = 7`,
|
|
206
|
+
`$sc.Save()`
|
|
207
|
+
].filter(Boolean).join("\n");
|
|
208
|
+
await mkdir(dirname(lnk), { recursive: true });
|
|
209
|
+
await runPowerShell(script, warnings);
|
|
210
|
+
return { paths: [lnk], warnings };
|
|
211
|
+
}
|
|
212
|
+
async function uninstallWindows(name) {
|
|
213
|
+
const lnk = windowsLnkPath(name);
|
|
214
|
+
if (!await exists(lnk)) return { paths: [], warnings: [] };
|
|
215
|
+
await rm(lnk, { force: true });
|
|
216
|
+
return { paths: [lnk], warnings: [] };
|
|
217
|
+
}
|
|
218
|
+
async function installDesktopLauncher(opts) {
|
|
219
|
+
switch (process.platform) {
|
|
220
|
+
case "linux":
|
|
221
|
+
case "freebsd":
|
|
222
|
+
case "openbsd":
|
|
223
|
+
return installLinux(opts);
|
|
224
|
+
case "darwin":
|
|
225
|
+
return installMac(opts);
|
|
226
|
+
case "win32":
|
|
227
|
+
return installWindows(opts);
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`Plateforme non support\xE9e : ${process.platform}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function uninstallDesktopLauncher(name, id) {
|
|
233
|
+
switch (process.platform) {
|
|
234
|
+
case "linux":
|
|
235
|
+
case "freebsd":
|
|
236
|
+
case "openbsd":
|
|
237
|
+
return uninstallLinux(id);
|
|
238
|
+
case "darwin":
|
|
239
|
+
return uninstallMac(name);
|
|
240
|
+
case "win32":
|
|
241
|
+
return uninstallWindows(name);
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`Plateforme non support\xE9e : ${process.platform}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
deriveIdentity,
|
|
248
|
+
desktopExecQuote,
|
|
249
|
+
idFromName,
|
|
250
|
+
installDesktopLauncher,
|
|
251
|
+
launcherArgs,
|
|
252
|
+
psQuote,
|
|
253
|
+
slugify,
|
|
254
|
+
uninstallDesktopLauncher
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=desktop-launcher-TM7AABMC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/desktop-launcher.ts"],"sourcesContent":["/**\n * Cross-platform desktop launcher (`runeya install-desktop`).\n *\n * Deliberately an explicit sub-command rather than a postinstall hook: npm 12\n * blocks lifecycle scripts by default, so a launcher created that way would be\n * silently missing. Here the user asks for it, and gets the paths back.\n *\n * Nothing is embedded — the launcher points at the Node binary and the CLI\n * entry point currently in use. If the user switches Node version manager,\n * re-running the command re-pins the paths.\n */\nimport { mkdir, writeFile, rm, copyFile, access } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { homedir } from 'node:os';\nimport { join, basename, dirname } from 'node:path';\n\nconst exec = promisify(execFile);\n\nexport type InstallOptions = {\n /** Absolute path to the Node binary the launcher must use. */\n nodePath: string;\n /** Absolute path to the CLI entry point (dist/index.js). */\n cliEntry: string;\n /** Directory holding the CLI dist — used to find the bundled icons in assets/. */\n distDir: string;\n /** Working directory to pin, or null to let the desktop decide. */\n workingDir: string | null;\n /** Display name, e.g. \"Runeya\" or \"Runeya — my-stack\". */\n name: string;\n /** Filesystem-safe id derived from the name, e.g. \"runeya-my-stack\". */\n id: string;\n /** Port baked into the launcher, or null to follow Runeya's default. */\n port: number | null;\n /** Agent port, or null to let Runeya derive it from `port + 1`. */\n agentPort: number | null;\n};\n\n/**\n * Arguments the launcher passes to the CLI.\n *\n * `--agent-port` is omitted when it is exactly what Runeya would derive on its\n * own, so the command line stays readable and keeps following the derivation\n * if it ever changes.\n */\nexport function launcherArgs(opts: Pick<InstallOptions, 'port' | 'agentPort'>): string[] {\n const args = ['--from-desktop'];\n if (opts.port !== null) args.push('--port', String(opts.port));\n if (opts.agentPort !== null && opts.agentPort !== (opts.port ?? 4000) + 1) {\n args.push('--agent-port', String(opts.agentPort));\n }\n return args;\n}\n\nexport type LauncherResult = {\n /** Files created or removed, for reporting back to the user. */\n paths: string[];\n /** Non-fatal problems (missing icon, desktop database refresh failed…). */\n warnings: string[];\n};\n\n/** Turn a display name into a filesystem- and desktop-id-safe slug. */\nexport function slugify(name: string): string {\n const slug = name\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug || 'runeya';\n}\n\n/**\n * The id is a pure function of the display name, so `uninstall-desktop --name`\n * always resolves to the launcher that `install-desktop` created. Names that\n * already slug to something starting with \"runeya\" are not prefixed again —\n * without that, the default \"Runeya — my-stack\" would yield the unreachable\n * id \"runeya-runeya-my-stack\".\n */\nexport function idFromName(name: string): string {\n const slug = slugify(name);\n return slug.startsWith('runeya') ? slug : `runeya-${slug}`;\n}\n\n/**\n * Derive the display name and id for a launcher.\n * Pinning a directory yields \"Runeya — <folder>\" so several stacks can each\n * have their own entry without colliding.\n */\nexport function deriveIdentity(explicitName: string | undefined, workingDir: string | null): { name: string; id: string } {\n const name = explicitName ?? (workingDir ? `Runeya — ${basename(workingDir)}` : 'Runeya');\n return { name, id: idFromName(name) };\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Best-effort command: never fails the install, just reports a warning. */\nasync function tryExec(file: string, args: string[], warnings: string[]): Promise<void> {\n try {\n await exec(file, args);\n } catch {\n warnings.push(`Commande optionnelle échouée : ${file} (le lanceur est créé, le menu peut mettre un instant à l'afficher)`);\n }\n}\n\n// ─── Linux ───────────────────────────────────────────────────────────────────\n\n/**\n * Quote a value for the `Exec=` key of a .desktop file.\n * Per the Desktop Entry spec, inside a quoted argument the characters\n * `\"`, backtick, `$` and `\\` must each be preceded by a backslash.\n */\nexport function desktopExecQuote(value: string): string {\n return `\"${value.replace(/[\"`$\\\\]/g, (c) => `\\\\${c}`)}\"`;\n}\n\n/** Escape a value for a non-Exec .desktop key (only backslashes are special). */\nfunction desktopValue(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\\n/g, ' ');\n}\n\nfunction xdgDataHome(): string {\n return process.env['XDG_DATA_HOME'] || join(homedir(), '.local/share');\n}\n\nfunction linuxPaths(id: string) {\n const dataHome = xdgDataHome();\n const appsDir = join(dataHome, 'applications');\n return {\n appsDir,\n entry: join(appsDir, `${id}.desktop`),\n iconDir: join(dataHome, 'icons/hicolor/scalable/apps'),\n icon: join(dataHome, `icons/hicolor/scalable/apps/${id}.svg`),\n };\n}\n\nasync function installLinux(opts: InstallOptions): Promise<LauncherResult> {\n const paths: string[] = [];\n const warnings: string[] = [];\n const p = linuxPaths(opts.id);\n\n // The icon is an SVG, which the scalable hicolor directory takes as-is.\n const sourceIcon = join(opts.distDir, 'assets/icon.svg');\n let iconName = 'application-x-executable';\n if (await exists(sourceIcon)) {\n await mkdir(p.iconDir, { recursive: true });\n await copyFile(sourceIcon, p.icon);\n paths.push(p.icon);\n iconName = opts.id;\n } else {\n warnings.push(`Icône introuvable (${sourceIcon}), icône générique utilisée`);\n }\n\n // --from-desktop: nobody is watching a terminal, so startup failures must be\n // reported in a browser window rather than written to a discarded stderr.\n const exec_ = [opts.nodePath, opts.cliEntry, ...launcherArgs(opts)]\n .map(desktopExecQuote)\n .join(' ');\n const lines = [\n '[Desktop Entry]',\n 'Type=Application',\n `Name=${desktopValue(opts.name)}`,\n 'Comment=Process and service manager',\n `Exec=${exec_}`,\n `Icon=${iconName}`,\n 'Terminal=false',\n 'Categories=Development;',\n 'StartupNotify=true',\n // Marker used by uninstall-desktop to refuse deleting a hand-written entry\n 'X-Runeya-Managed=true',\n ];\n if (opts.workingDir) lines.push(`Path=${desktopValue(opts.workingDir)}`);\n\n await mkdir(p.appsDir, { recursive: true });\n await writeFile(p.entry, lines.join('\\n') + '\\n', { mode: 0o755 });\n paths.push(p.entry);\n\n await tryExec('update-desktop-database', [p.appsDir], warnings);\n\n return { paths, warnings };\n}\n\nasync function uninstallLinux(id: string): Promise<LauncherResult> {\n const paths: string[] = [];\n const warnings: string[] = [];\n const p = linuxPaths(id);\n for (const target of [p.entry, p.icon]) {\n if (await exists(target)) {\n await rm(target, { force: true });\n paths.push(target);\n }\n }\n if (paths.length) await tryExec('update-desktop-database', [p.appsDir], warnings);\n return { paths, warnings };\n}\n\n// ─── macOS ───────────────────────────────────────────────────────────────────\n\n/** Escape a value destined for a <string> node of an Info.plist. */\nfunction plistString(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\n/** Single-quote a value for the POSIX shell inside the .app stub script. */\nfunction shQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction macAppPath(name: string): string {\n return join(homedir(), 'Applications', `${name}.app`);\n}\n\nasync function installMac(opts: InstallOptions): Promise<LauncherResult> {\n const warnings: string[] = [];\n const appPath = macAppPath(opts.name);\n const macosDir = join(appPath, 'Contents/MacOS');\n const resourcesDir = join(appPath, 'Contents/Resources');\n const execName = opts.id;\n\n await mkdir(macosDir, { recursive: true });\n await mkdir(resourcesDir, { recursive: true });\n\n // The bundle executable is a shell stub: no compiled code, no signing needed.\n // A locally generated .app carries no quarantine attribute, so Gatekeeper\n // lets it run even unsigned.\n const stub = [\n '#!/bin/sh',\n '# Generated by `runeya install-desktop` — safe to delete.',\n opts.workingDir ? `cd ${shQuote(opts.workingDir)} || exit 1` : '',\n `exec ${[opts.nodePath, opts.cliEntry, ...launcherArgs(opts)].map(shQuote).join(' ')} \"$@\"`,\n '',\n ].filter(Boolean).join('\\n');\n await writeFile(join(macosDir, execName), stub, { mode: 0o755 });\n\n // CFBundleIconFile is only emitted when the .icns actually made it into the\n // bundle — a key pointing at a missing file makes Finder fall back silently,\n // which is harder to diagnose than no key at all.\n const sourceIcns = join(opts.distDir, 'assets/icon.icns');\n let iconKey = '';\n if (await exists(sourceIcns)) {\n await copyFile(sourceIcns, join(resourcesDir, `${execName}.icns`));\n iconKey = `\\n <key>CFBundleIconFile</key><string>${plistString(execName)}</string>`;\n } else {\n warnings.push(`Icône introuvable (${sourceIcns}), icône générique utilisée`);\n }\n\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>CFBundleName</key><string>${plistString(opts.name)}</string>\n <key>CFBundleDisplayName</key><string>${plistString(opts.name)}</string>\n <key>CFBundleExecutable</key><string>${plistString(execName)}</string>\n <key>CFBundleIdentifier</key><string>dev.runeya.launcher.${plistString(opts.id)}</string>\n <key>CFBundlePackageType</key><string>APPL</string>${iconKey}\n <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>\n <key>CFBundleShortVersionString</key><string>1.0</string>\n <key>NSHighResolutionCapable</key><true/>\n</dict>\n</plist>\n`;\n await writeFile(join(appPath, 'Contents/Info.plist'), plist);\n\n // Ask LaunchServices to notice the new bundle so Spotlight finds it right away.\n await tryExec(\n '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister',\n ['-f', appPath],\n warnings,\n );\n\n return { paths: [appPath], warnings };\n}\n\nasync function uninstallMac(name: string): Promise<LauncherResult> {\n const appPath = macAppPath(name);\n if (!(await exists(appPath))) return { paths: [], warnings: [] };\n await rm(appPath, { recursive: true, force: true });\n return { paths: [appPath], warnings: [] };\n}\n\n// ─── Windows ─────────────────────────────────────────────────────────────────\n\n/** Single-quote a value for a PowerShell literal string. */\nexport function psQuote(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\nfunction windowsLnkPath(name: string): string {\n const appData = process.env['APPDATA'] || join(homedir(), 'AppData/Roaming');\n return join(appData, 'Microsoft/Windows/Start Menu/Programs', `${name}.lnk`);\n}\n\nasync function runPowerShell(script: string, warnings: string[]): Promise<void> {\n // -EncodedCommand avoids every layer of cmd.exe quoting.\n const encoded = Buffer.from(script, 'utf16le').toString('base64');\n try {\n await exec('powershell.exe', ['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded]);\n } catch (err) {\n warnings.push(`PowerShell a échoué : ${(err as Error).message}`);\n throw err;\n }\n}\n\nasync function installWindows(opts: InstallOptions): Promise<LauncherResult> {\n const warnings: string[] = [];\n const lnk = windowsLnkPath(opts.name);\n\n // The .ico is referenced in place inside the installed dist rather than\n // copied: the shortcut already breaks if that dist moves, so a second copy\n // would only add a file to leave behind.\n const icoPath = join(opts.distDir, 'assets/icon.ico');\n const iconPath = (await exists(icoPath)) ? icoPath : null;\n if (!iconPath) warnings.push(`Icône introuvable (${icoPath}), icône de Node utilisée`);\n\n const script = [\n `$ws = New-Object -ComObject WScript.Shell`,\n `$sc = $ws.CreateShortcut(${psQuote(lnk)})`,\n `$sc.TargetPath = ${psQuote(opts.nodePath)}`,\n `$sc.Arguments = ${psQuote([`\"${opts.cliEntry}\"`, ...launcherArgs(opts)].join(' '))}`,\n opts.workingDir ? `$sc.WorkingDirectory = ${psQuote(opts.workingDir)}` : '',\n `$sc.Description = 'Runeya — process and service manager'`,\n iconPath ? `$sc.IconLocation = ${psQuote(iconPath)}` : '',\n // WindowStyle 7 = minimized: the console still exists (Node needs one) but\n // stays out of the way while the browser opens.\n `$sc.WindowStyle = 7`,\n `$sc.Save()`,\n ].filter(Boolean).join('\\n');\n\n await mkdir(dirname(lnk), { recursive: true });\n await runPowerShell(script, warnings);\n\n return { paths: [lnk], warnings };\n}\n\nasync function uninstallWindows(name: string): Promise<LauncherResult> {\n const lnk = windowsLnkPath(name);\n if (!(await exists(lnk))) return { paths: [], warnings: [] };\n await rm(lnk, { force: true });\n return { paths: [lnk], warnings: [] };\n}\n\n// ─── Entry points ────────────────────────────────────────────────────────────\n\nexport async function installDesktopLauncher(opts: InstallOptions): Promise<LauncherResult> {\n switch (process.platform) {\n case 'linux':\n case 'freebsd':\n case 'openbsd':\n return installLinux(opts);\n case 'darwin':\n return installMac(opts);\n case 'win32':\n return installWindows(opts);\n default:\n throw new Error(`Plateforme non supportée : ${process.platform}`);\n }\n}\n\nexport async function uninstallDesktopLauncher(name: string, id: string): Promise<LauncherResult> {\n switch (process.platform) {\n case 'linux':\n case 'freebsd':\n case 'openbsd':\n return uninstallLinux(id);\n case 'darwin':\n return uninstallMac(name);\n case 'win32':\n return uninstallWindows(name);\n default:\n throw new Error(`Plateforme non supportée : ${process.platform}`);\n }\n}\n"],"mappings":";AAWA,SAAS,OAAO,WAAW,IAAI,UAAU,cAAc;AACvD,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,MAAM,UAAU,eAAe;AAExC,IAAM,OAAO,UAAU,QAAQ;AA4BxB,SAAS,aAAa,MAA4D;AACvF,QAAM,OAAO,CAAC,gBAAgB;AAC9B,MAAI,KAAK,SAAS,KAAM,MAAK,KAAK,UAAU,OAAO,KAAK,IAAI,CAAC;AAC7D,MAAI,KAAK,cAAc,QAAQ,KAAK,eAAe,KAAK,QAAQ,OAAQ,GAAG;AACzE,SAAK,KAAK,gBAAgB,OAAO,KAAK,SAAS,CAAC;AAAA,EAClD;AACA,SAAO;AACT;AAUO,SAAS,QAAQ,MAAsB;AAC5C,QAAM,OAAO,KACV,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AACzB,SAAO,QAAQ;AACjB;AASO,SAAS,WAAW,MAAsB;AAC/C,QAAM,OAAO,QAAQ,IAAI;AACzB,SAAO,KAAK,WAAW,QAAQ,IAAI,OAAO,UAAU,IAAI;AAC1D;AAOO,SAAS,eAAe,cAAkC,YAAyD;AACxH,QAAM,OAAO,iBAAiB,aAAa,iBAAY,SAAS,UAAU,CAAC,KAAK;AAChF,SAAO,EAAE,MAAM,IAAI,WAAW,IAAI,EAAE;AACtC;AAEA,eAAe,OAAO,MAAgC;AACpD,MAAI;AACF,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,QAAQ,MAAc,MAAgB,UAAmC;AACtF,MAAI;AACF,UAAM,KAAK,MAAM,IAAI;AAAA,EACvB,QAAQ;AACN,aAAS,KAAK,wCAAkC,IAAI,8EAAqE;AAAA,EAC3H;AACF;AASO,SAAS,iBAAiB,OAAuB;AACtD,SAAO,IAAI,MAAM,QAAQ,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;AACvD;AAGA,SAAS,aAAa,OAAuB;AAC3C,SAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,OAAO,GAAG;AACxD;AAEA,SAAS,cAAsB;AAC7B,SAAO,QAAQ,IAAI,eAAe,KAAK,KAAK,QAAQ,GAAG,cAAc;AACvE;AAEA,SAAS,WAAW,IAAY;AAC9B,QAAM,WAAW,YAAY;AAC7B,QAAM,UAAU,KAAK,UAAU,cAAc;AAC7C,SAAO;AAAA,IACL;AAAA,IACA,OAAO,KAAK,SAAS,GAAG,EAAE,UAAU;AAAA,IACpC,SAAS,KAAK,UAAU,6BAA6B;AAAA,IACrD,MAAM,KAAK,UAAU,+BAA+B,EAAE,MAAM;AAAA,EAC9D;AACF;AAEA,eAAe,aAAa,MAA+C;AACzE,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAqB,CAAC;AAC5B,QAAM,IAAI,WAAW,KAAK,EAAE;AAG5B,QAAM,aAAa,KAAK,KAAK,SAAS,iBAAiB;AACvD,MAAI,WAAW;AACf,MAAI,MAAM,OAAO,UAAU,GAAG;AAC5B,UAAM,MAAM,EAAE,SAAS,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,SAAS,YAAY,EAAE,IAAI;AACjC,UAAM,KAAK,EAAE,IAAI;AACjB,eAAW,KAAK;AAAA,EAClB,OAAO;AACL,aAAS,KAAK,yBAAsB,UAAU,yCAA6B;AAAA,EAC7E;AAIA,QAAM,QAAQ,CAAC,KAAK,UAAU,KAAK,UAAU,GAAG,aAAa,IAAI,CAAC,EAC/D,IAAI,gBAAgB,EACpB,KAAK,GAAG;AACX,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,QAAQ,aAAa,KAAK,IAAI,CAAC;AAAA,IAC/B;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,EACF;AACA,MAAI,KAAK,WAAY,OAAM,KAAK,QAAQ,aAAa,KAAK,UAAU,CAAC,EAAE;AAEvE,QAAM,MAAM,EAAE,SAAS,EAAE,WAAW,KAAK,CAAC;AAC1C,QAAM,UAAU,EAAE,OAAO,MAAM,KAAK,IAAI,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AACjE,QAAM,KAAK,EAAE,KAAK;AAElB,QAAM,QAAQ,2BAA2B,CAAC,EAAE,OAAO,GAAG,QAAQ;AAE9D,SAAO,EAAE,OAAO,SAAS;AAC3B;AAEA,eAAe,eAAe,IAAqC;AACjE,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAqB,CAAC;AAC5B,QAAM,IAAI,WAAW,EAAE;AACvB,aAAW,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,GAAG;AACtC,QAAI,MAAM,OAAO,MAAM,GAAG;AACxB,YAAM,GAAG,QAAQ,EAAE,OAAO,KAAK,CAAC;AAChC,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AACA,MAAI,MAAM,OAAQ,OAAM,QAAQ,2BAA2B,CAAC,EAAE,OAAO,GAAG,QAAQ;AAChF,SAAO,EAAE,OAAO,SAAS;AAC3B;AAKA,SAAS,YAAY,OAAuB;AAC1C,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM;AACzB;AAGA,SAAS,QAAQ,OAAuB;AACtC,SAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,KAAK,QAAQ,GAAG,gBAAgB,GAAG,IAAI,MAAM;AACtD;AAEA,eAAe,WAAW,MAA+C;AACvE,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAAU,WAAW,KAAK,IAAI;AACpC,QAAM,WAAW,KAAK,SAAS,gBAAgB;AAC/C,QAAM,eAAe,KAAK,SAAS,oBAAoB;AACvD,QAAM,WAAW,KAAK;AAEtB,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AACzC,QAAM,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAK7C,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA,KAAK,aAAa,MAAM,QAAQ,KAAK,UAAU,CAAC,eAAe;AAAA,IAC/D,QAAQ,CAAC,KAAK,UAAU,KAAK,UAAU,GAAG,aAAa,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,GAAG,CAAC;AAAA,IACpF;AAAA,EACF,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC3B,QAAM,UAAU,KAAK,UAAU,QAAQ,GAAG,MAAM,EAAE,MAAM,IAAM,CAAC;AAK/D,QAAM,aAAa,KAAK,KAAK,SAAS,kBAAkB;AACxD,MAAI,UAAU;AACd,MAAI,MAAM,OAAO,UAAU,GAAG;AAC5B,UAAM,SAAS,YAAY,KAAK,cAAc,GAAG,QAAQ,OAAO,CAAC;AACjE,cAAU;AAAA,uCAA0C,YAAY,QAAQ,CAAC;AAAA,EAC3E,OAAO;AACL,aAAS,KAAK,yBAAsB,UAAU,yCAA6B;AAAA,EAC7E;AAEA,QAAM,QAAQ;AAAA;AAAA;AAAA;AAAA,mCAImB,YAAY,KAAK,IAAI,CAAC;AAAA,0CACf,YAAY,KAAK,IAAI,CAAC;AAAA,yCACvB,YAAY,QAAQ,CAAC;AAAA,6DACD,YAAY,KAAK,EAAE,CAAC;AAAA,uDAC1B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAO5D,QAAM,UAAU,KAAK,SAAS,qBAAqB,GAAG,KAAK;AAG3D,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,MAAM,OAAO;AAAA,IACd;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS;AACtC;AAEA,eAAe,aAAa,MAAuC;AACjE,QAAM,UAAU,WAAW,IAAI;AAC/B,MAAI,CAAE,MAAM,OAAO,OAAO,EAAI,QAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,EAAE;AAC/D,QAAM,GAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAClD,SAAO,EAAE,OAAO,CAAC,OAAO,GAAG,UAAU,CAAC,EAAE;AAC1C;AAKO,SAAS,QAAQ,OAAuB;AAC7C,SAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AACtC;AAEA,SAAS,eAAe,MAAsB;AAC5C,QAAM,UAAU,QAAQ,IAAI,SAAS,KAAK,KAAK,QAAQ,GAAG,iBAAiB;AAC3E,SAAO,KAAK,SAAS,yCAAyC,GAAG,IAAI,MAAM;AAC7E;AAEA,eAAe,cAAc,QAAgB,UAAmC;AAE9E,QAAM,UAAU,OAAO,KAAK,QAAQ,SAAS,EAAE,SAAS,QAAQ;AAChE,MAAI;AACF,UAAM,KAAK,kBAAkB,CAAC,cAAc,mBAAmB,mBAAmB,OAAO,CAAC;AAAA,EAC5F,SAAS,KAAK;AACZ,aAAS,KAAK,+BAA0B,IAAc,OAAO,EAAE;AAC/D,UAAM;AAAA,EACR;AACF;AAEA,eAAe,eAAe,MAA+C;AAC3E,QAAM,WAAqB,CAAC;AAC5B,QAAM,MAAM,eAAe,KAAK,IAAI;AAKpC,QAAM,UAAU,KAAK,KAAK,SAAS,iBAAiB;AACpD,QAAM,WAAY,MAAM,OAAO,OAAO,IAAK,UAAU;AACrD,MAAI,CAAC,SAAU,UAAS,KAAK,yBAAsB,OAAO,iCAA2B;AAErF,QAAM,SAAS;AAAA,IACb;AAAA,IACA,4BAA4B,QAAQ,GAAG,CAAC;AAAA,IACxC,oBAAoB,QAAQ,KAAK,QAAQ,CAAC;AAAA,IAC1C,mBAAmB,QAAQ,CAAC,IAAI,KAAK,QAAQ,KAAK,GAAG,aAAa,IAAI,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC;AAAA,IACnF,KAAK,aAAa,0BAA0B,QAAQ,KAAK,UAAU,CAAC,KAAK;AAAA,IACzE;AAAA,IACA,WAAW,sBAAsB,QAAQ,QAAQ,CAAC,KAAK;AAAA;AAAA;AAAA,IAGvD;AAAA,IACA;AAAA,EACF,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAE3B,QAAM,MAAM,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAM,cAAc,QAAQ,QAAQ;AAEpC,SAAO,EAAE,OAAO,CAAC,GAAG,GAAG,SAAS;AAClC;AAEA,eAAe,iBAAiB,MAAuC;AACrE,QAAM,MAAM,eAAe,IAAI;AAC/B,MAAI,CAAE,MAAM,OAAO,GAAG,EAAI,QAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,EAAE;AAC3D,QAAM,GAAG,KAAK,EAAE,OAAO,KAAK,CAAC;AAC7B,SAAO,EAAE,OAAO,CAAC,GAAG,GAAG,UAAU,CAAC,EAAE;AACtC;AAIA,eAAsB,uBAAuB,MAA+C;AAC1F,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,aAAa,IAAI;AAAA,IAC1B,KAAK;AACH,aAAO,WAAW,IAAI;AAAA,IACxB,KAAK;AACH,aAAO,eAAe,IAAI;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,iCAA8B,QAAQ,QAAQ,EAAE;AAAA,EACpE;AACF;AAEA,eAAsB,yBAAyB,MAAc,IAAqC;AAChG,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,eAAe,EAAE;AAAA,IAC1B,KAAK;AACH,aAAO,aAAa,IAAI;AAAA,IAC1B,KAAK;AACH,aAAO,iBAAiB,IAAI;AAAA,IAC9B;AACE,YAAM,IAAI,MAAM,iCAA8B,QAAQ,QAAQ,EAAE;AAAA,EACpE;AACF;","names":[]}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findFreePortPair,
|
|
3
|
+
isPortFree,
|
|
4
|
+
parsePort
|
|
5
|
+
} from "./chunk-O2KOX4LU.js";
|
|
6
|
+
|
|
7
|
+
// src/desktop-wizard.ts
|
|
8
|
+
import { createInterface } from "readline/promises";
|
|
9
|
+
import { basename } from "path";
|
|
10
|
+
var DEFAULT_PORT = 4e3;
|
|
11
|
+
var useColor = process.stdout.isTTY && !process.env["NO_COLOR"];
|
|
12
|
+
var c = {
|
|
13
|
+
bold: (s) => useColor ? `\x1B[1m${s}\x1B[0m` : s,
|
|
14
|
+
dim: (s) => useColor ? `\x1B[2m${s}\x1B[0m` : s,
|
|
15
|
+
cyan: (s) => useColor ? `\x1B[36m${s}\x1B[0m` : s,
|
|
16
|
+
green: (s) => useColor ? `\x1B[32m${s}\x1B[0m` : s,
|
|
17
|
+
yellow: (s) => useColor ? `\x1B[33m${s}\x1B[0m` : s
|
|
18
|
+
};
|
|
19
|
+
function shouldRunWizard(rest, hasPositional) {
|
|
20
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
21
|
+
if (process.env["CI"]) return false;
|
|
22
|
+
if (hasPositional) return false;
|
|
23
|
+
return !rest.some((a) => a === "--name" || a === "--no-cwd" || a === "--yes" || a === "-y" || a === "--port" || a === "--agent-port");
|
|
24
|
+
}
|
|
25
|
+
var WizardAborted = class extends Error {
|
|
26
|
+
};
|
|
27
|
+
async function askChoice(rl, question, choices) {
|
|
28
|
+
console.log(`
|
|
29
|
+
${c.bold(question)}`);
|
|
30
|
+
choices.forEach((choice, i) => {
|
|
31
|
+
console.log(` ${c.cyan(String(i + 1))}. ${choice.label}`);
|
|
32
|
+
console.log(` ${c.dim(choice.hint)}`);
|
|
33
|
+
});
|
|
34
|
+
for (; ; ) {
|
|
35
|
+
let answer;
|
|
36
|
+
try {
|
|
37
|
+
answer = (await rl.question(`
|
|
38
|
+
${c.dim("Votre choix")} [1-${choices.length}] \u203A `)).trim();
|
|
39
|
+
} catch {
|
|
40
|
+
throw new WizardAborted();
|
|
41
|
+
}
|
|
42
|
+
const index = Number(answer) - 1;
|
|
43
|
+
if (Number.isInteger(index) && index >= 0 && index < choices.length) return index;
|
|
44
|
+
console.log(c.yellow(` R\xE9ponse attendue : un nombre entre 1 et ${choices.length}.`));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function askText(rl, question, fallback) {
|
|
48
|
+
console.log(`
|
|
49
|
+
${c.bold(question)}`);
|
|
50
|
+
console.log(` ${c.dim(`Entr\xE9e pour garder \xAB ${fallback} \xBB`)}`);
|
|
51
|
+
try {
|
|
52
|
+
const answer = (await rl.question(`
|
|
53
|
+
${c.dim("Nom")} \u203A `)).trim();
|
|
54
|
+
return answer || fallback;
|
|
55
|
+
} catch {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function askPorts(rl) {
|
|
60
|
+
const suggestion = await findFreePortPair();
|
|
61
|
+
const defaultsFree = await isPortFree(DEFAULT_PORT) && await isPortFree(DEFAULT_PORT + 1);
|
|
62
|
+
const choices = [
|
|
63
|
+
...suggestion === null ? [] : [{
|
|
64
|
+
label: `Ports d\xE9di\xE9s ${c.dim(`(${suggestion} et ${suggestion + 1})`)}`,
|
|
65
|
+
hint: "Tir\xE9s au hasard parmi les ports libres \u2014 recommand\xE9, aucun risque de conflit"
|
|
66
|
+
}],
|
|
67
|
+
{
|
|
68
|
+
label: `Ports par d\xE9faut de Runeya ${c.dim(`(${DEFAULT_PORT} et ${DEFAULT_PORT + 1})`)}`,
|
|
69
|
+
hint: defaultsFree ? "Libres actuellement" : `${DEFAULT_PORT} ou ${DEFAULT_PORT + 1} est d\xE9j\xE0 occup\xE9 \u2014 le raccourci \xE9chouera tant que ce sera le cas`
|
|
70
|
+
},
|
|
71
|
+
{ label: "Choisir moi-m\xEAme", hint: "Saisir le port de Runeya, puis celui de l'agent" }
|
|
72
|
+
];
|
|
73
|
+
const picked = await askChoice(rl, "Quels ports ?", choices);
|
|
74
|
+
const offset = suggestion === null ? 1 : 0;
|
|
75
|
+
if (picked + offset === 0) return { port: suggestion, agentPort: suggestion + 1 };
|
|
76
|
+
if (picked + offset === 1) return { port: null, agentPort: null };
|
|
77
|
+
const port = await askPort(rl, "Port de Runeya ?", suggestion ?? DEFAULT_PORT);
|
|
78
|
+
const agentPort = await askPort(rl, "Port de l'agent ?", port + 1);
|
|
79
|
+
return { port, agentPort };
|
|
80
|
+
}
|
|
81
|
+
async function askPort(rl, question, fallback) {
|
|
82
|
+
for (; ; ) {
|
|
83
|
+
console.log(`
|
|
84
|
+
${c.bold(question)}`);
|
|
85
|
+
console.log(` ${c.dim(`Entr\xE9e pour garder ${fallback}`)}`);
|
|
86
|
+
let answer;
|
|
87
|
+
try {
|
|
88
|
+
answer = (await rl.question(`
|
|
89
|
+
${c.dim("Port")} \u203A `)).trim();
|
|
90
|
+
} catch {
|
|
91
|
+
return fallback;
|
|
92
|
+
}
|
|
93
|
+
if (answer === "") return fallback;
|
|
94
|
+
const port = parsePort(answer);
|
|
95
|
+
if (port === null) {
|
|
96
|
+
console.log(c.yellow(" R\xE9ponse attendue : un entier entre 1 et 65535."));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (!await isPortFree(port)) {
|
|
100
|
+
console.log(c.yellow(` Attention : le port ${port} est occup\xE9 actuellement.`));
|
|
101
|
+
}
|
|
102
|
+
return port;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function runInstallWizard(cwd) {
|
|
106
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
107
|
+
let aborted = false;
|
|
108
|
+
rl.on("SIGINT", () => {
|
|
109
|
+
aborted = true;
|
|
110
|
+
rl.close();
|
|
111
|
+
});
|
|
112
|
+
try {
|
|
113
|
+
console.log(`
|
|
114
|
+
${c.bold("Cr\xE9ation d'un raccourci Runeya")}`);
|
|
115
|
+
console.log(c.dim("\u2500".repeat(46)));
|
|
116
|
+
const scope = await askChoice(rl, "Quel type de raccourci ?", [
|
|
117
|
+
{
|
|
118
|
+
label: `Ce projet ${c.dim(`(${basename(cwd)})`)}`,
|
|
119
|
+
hint: `D\xE9marre Runeya dans ${cwd}`
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: "Global",
|
|
123
|
+
hint: "D\xE9marre Runeya dans votre dossier personnel \u2014 les donn\xE9es iront dans ~/.runeya"
|
|
124
|
+
}
|
|
125
|
+
]);
|
|
126
|
+
const workingDir = scope === 0 ? cwd : null;
|
|
127
|
+
const suggested = workingDir ? `Runeya \u2014 ${basename(workingDir)}` : "Runeya";
|
|
128
|
+
const name = await askText(rl, "Nom du raccourci ?", suggested);
|
|
129
|
+
const { port, agentPort } = await askPorts(rl);
|
|
130
|
+
return { workingDir, name, port, agentPort };
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err instanceof WizardAborted) return null;
|
|
133
|
+
throw err;
|
|
134
|
+
} finally {
|
|
135
|
+
if (aborted) console.log("\nAnnul\xE9.");
|
|
136
|
+
rl.close();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function keyValueBlock(rows) {
|
|
140
|
+
const width = Math.max(...rows.map(([k]) => k.length));
|
|
141
|
+
return rows.map(([k, v]) => ` ${c.dim(k.padEnd(width))} ${v}`).join("\n");
|
|
142
|
+
}
|
|
143
|
+
function shellArg(value) {
|
|
144
|
+
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
145
|
+
}
|
|
146
|
+
function printInstallReport(report) {
|
|
147
|
+
const { name, workingDir, nodePath, port, agentPort, result } = report;
|
|
148
|
+
console.log(`
|
|
149
|
+
${c.green("\u2713")} ${c.bold("Raccourci cr\xE9\xE9")}`);
|
|
150
|
+
console.log(c.dim("\u2500".repeat(46)));
|
|
151
|
+
const effectivePort = port ?? 4e3;
|
|
152
|
+
console.log(keyValueBlock([
|
|
153
|
+
["Nom", name],
|
|
154
|
+
["Port\xE9e", workingDir ? `ce projet \u2014 ${workingDir}` : "global (aucun dossier fix\xE9)"],
|
|
155
|
+
["Adresse", `http://localhost:${effectivePort}`],
|
|
156
|
+
["Ports", `${effectivePort} (Runeya) \xB7 ${agentPort ?? effectivePort + 1} (agent)${port === null ? c.dim(" \u2014 d\xE9fauts") : ""}`],
|
|
157
|
+
["Node", nodePath]
|
|
158
|
+
]));
|
|
159
|
+
console.log(`
|
|
160
|
+
${c.bold("Fichiers \xE9crits")}`);
|
|
161
|
+
for (const path of result.paths) console.log(` ${path}`);
|
|
162
|
+
for (const warning of result.warnings) console.log(`
|
|
163
|
+
${c.yellow("\u26A0")} ${warning}`);
|
|
164
|
+
console.log(`
|
|
165
|
+
${c.bold("O\xF9 le trouver")}`);
|
|
166
|
+
console.log(` ${c.dim(whereToFind())}`);
|
|
167
|
+
console.log(`
|
|
168
|
+
${c.bold("Pour le supprimer")}`);
|
|
169
|
+
console.log(` ${c.cyan(`runeya uninstall-desktop --name ${shellArg(name)}`)}`);
|
|
170
|
+
console.log(` ${c.dim("Cette commande retire exactement les fichiers list\xE9s ci-dessus.")}`);
|
|
171
|
+
console.log(`
|
|
172
|
+
${c.dim("Le raccourci pointe vers le Node ci-dessus. Si vous changez de version")}`);
|
|
173
|
+
console.log(`${c.dim("de Node, relancez `runeya install-desktop` pour le repointer.")}
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
function whereToFind() {
|
|
177
|
+
switch (process.platform) {
|
|
178
|
+
case "darwin":
|
|
179
|
+
return "Dans ~/Applications, et via Spotlight (\u2318 + espace).";
|
|
180
|
+
case "win32":
|
|
181
|
+
return "Dans le menu D\xE9marrer.";
|
|
182
|
+
default:
|
|
183
|
+
return "Dans le menu des applications de votre bureau. Certains environnements demandent une reconnexion.";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function printUninstallReport(name, result) {
|
|
187
|
+
if (result.paths.length === 0) {
|
|
188
|
+
console.log(`
|
|
189
|
+
Aucun raccourci \xAB ${name} \xBB trouv\xE9.`);
|
|
190
|
+
console.log(`${c.dim("Astuce : pr\xE9cisez le nom exact avec --name, ou le dossier du projet en argument.")}
|
|
191
|
+
`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
console.log(`
|
|
195
|
+
${c.green("\u2713")} ${c.bold("Raccourci supprim\xE9")} ${c.dim(`\u2014 ${name}`)}`);
|
|
196
|
+
console.log(c.dim("\u2500".repeat(46)));
|
|
197
|
+
for (const path of result.paths) console.log(` ${path}`);
|
|
198
|
+
for (const warning of result.warnings) console.log(`
|
|
199
|
+
${c.yellow("\u26A0")} ${warning}`);
|
|
200
|
+
console.log("");
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
printInstallReport,
|
|
204
|
+
printUninstallReport,
|
|
205
|
+
runInstallWizard,
|
|
206
|
+
shellArg,
|
|
207
|
+
shouldRunWizard
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=desktop-wizard-4CYANX2O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/desktop-wizard.ts"],"sourcesContent":["/**\n * Interactive front-end for `runeya install-desktop`.\n *\n * Runs only when the sub-command is invoked bare on a TTY. Any flag that\n * already answers a question (--name, --no-cwd, a path, --yes) is taken as\n * explicit intent and skips the wizard entirely, so scripts and CI keep\n * working unattended.\n */\nimport { createInterface } from 'node:readline/promises';\nimport { basename } from 'node:path';\nimport { findFreePortPair, isPortFree, parsePort } from './free-port.js';\nimport type { LauncherResult } from './desktop-launcher.js';\n\n/** Runeya's own defaults, offered as one of the port choices. */\nconst DEFAULT_PORT = 4000;\n\n/** ANSI helpers — disabled when the output is piped or NO_COLOR is set. */\nconst useColor = process.stdout.isTTY && !process.env['NO_COLOR'];\nconst c = {\n bold: (s: string) => (useColor ? `\\x1b[1m${s}\\x1b[0m` : s),\n dim: (s: string) => (useColor ? `\\x1b[2m${s}\\x1b[0m` : s),\n cyan: (s: string) => (useColor ? `\\x1b[36m${s}\\x1b[0m` : s),\n green: (s: string) => (useColor ? `\\x1b[32m${s}\\x1b[0m` : s),\n yellow: (s: string) => (useColor ? `\\x1b[33m${s}\\x1b[0m` : s),\n};\n\nexport type WizardAnswers = {\n workingDir: string | null;\n name: string;\n port: number | null;\n agentPort: number | null;\n};\n\n/** True when we can and should prompt: a real terminal, and nothing pre-answered. */\nexport function shouldRunWizard(rest: string[], hasPositional: boolean): boolean {\n if (!process.stdin.isTTY || !process.stdout.isTTY) return false;\n if (process.env['CI']) return false;\n if (hasPositional) return false;\n return !rest.some((a) =>\n a === '--name' || a === '--no-cwd' || a === '--yes' || a === '-y'\n || a === '--port' || a === '--agent-port');\n}\n\n/** Thrown when stdin ends or the user interrupts on a question with no safe default. */\nclass WizardAborted extends Error {}\n\n/**\n * Ask a single-key numbered question. Returns the chosen index.\n * Anything unrecognised re-asks rather than silently picking the default —\n * this writes files into the user's desktop environment.\n */\nasync function askChoice(\n rl: ReturnType<typeof createInterface>,\n question: string,\n choices: { label: string; hint: string }[],\n): Promise<number> {\n console.log(`\\n${c.bold(question)}`);\n choices.forEach((choice, i) => {\n console.log(` ${c.cyan(String(i + 1))}. ${choice.label}`);\n console.log(` ${c.dim(choice.hint)}`);\n });\n\n for (;;) {\n // A closed stdin rejects the pending question. There is no safe default\n // here — the answer decides where files get written — so abort.\n let answer: string;\n try {\n answer = (await rl.question(`\\n${c.dim('Votre choix')} [1-${choices.length}] › `)).trim();\n } catch {\n throw new WizardAborted();\n }\n const index = Number(answer) - 1;\n if (Number.isInteger(index) && index >= 0 && index < choices.length) return index;\n console.log(c.yellow(` Réponse attendue : un nombre entre 1 et ${choices.length}.`));\n }\n}\n\n/** Ask for free text, falling back to `fallback` when the user just hits Enter. */\nasync function askText(\n rl: ReturnType<typeof createInterface>,\n question: string,\n fallback: string,\n): Promise<string> {\n console.log(`\\n${c.bold(question)}`);\n console.log(` ${c.dim(`Entrée pour garder « ${fallback} »`)}`);\n // Enter and end-of-input both mean \"keep the suggestion\".\n try {\n const answer = (await rl.question(`\\n${c.dim('Nom')} › `)).trim();\n return answer || fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Ask which ports the launcher should use.\n *\n * A dedicated pair is offered first, and is the right answer for most people:\n * the reason to pin a port in a shortcut is so a second Runeya — a dev server,\n * another project's launcher — cannot take it out from under you.\n */\nasync function askPorts(\n rl: ReturnType<typeof createInterface>,\n): Promise<{ port: number | null; agentPort: number | null }> {\n const suggestion = await findFreePortPair();\n const defaultsFree = (await isPortFree(DEFAULT_PORT)) && (await isPortFree(DEFAULT_PORT + 1));\n\n const choices = [\n ...(suggestion === null ? [] : [{\n label: `Ports dédiés ${c.dim(`(${suggestion} et ${suggestion + 1})`)}`,\n hint: 'Tirés au hasard parmi les ports libres — recommandé, aucun risque de conflit',\n }]),\n {\n label: `Ports par défaut de Runeya ${c.dim(`(${DEFAULT_PORT} et ${DEFAULT_PORT + 1})`)}`,\n hint: defaultsFree\n ? 'Libres actuellement'\n : `${DEFAULT_PORT} ou ${DEFAULT_PORT + 1} est déjà occupé — le raccourci échouera tant que ce sera le cas`,\n },\n { label: 'Choisir moi-même', hint: 'Saisir le port de Runeya, puis celui de l\\'agent' },\n ];\n\n const picked = await askChoice(rl, 'Quels ports ?', choices);\n // The suggestion occupies slot 0 only when there is one to offer\n const offset = suggestion === null ? 1 : 0;\n\n if (picked + offset === 0) return { port: suggestion, agentPort: suggestion! + 1 };\n if (picked + offset === 1) return { port: null, agentPort: null };\n\n const port = await askPort(rl, 'Port de Runeya ?', suggestion ?? DEFAULT_PORT);\n const agentPort = await askPort(rl, 'Port de l\\'agent ?', port + 1);\n return { port, agentPort };\n}\n\n/** Ask for a port, re-asking until the answer is a valid one. Warns if taken. */\nasync function askPort(\n rl: ReturnType<typeof createInterface>,\n question: string,\n fallback: number,\n): Promise<number> {\n for (;;) {\n console.log(`\\n${c.bold(question)}`);\n console.log(` ${c.dim(`Entrée pour garder ${fallback}`)}`);\n\n let answer: string;\n try {\n answer = (await rl.question(`\\n${c.dim('Port')} › `)).trim();\n } catch {\n return fallback; // end of input reads as \"keep the suggestion\"\n }\n if (answer === '') return fallback;\n\n const port = parsePort(answer);\n if (port === null) {\n console.log(c.yellow(' Réponse attendue : un entier entre 1 et 65535.'));\n continue;\n }\n // Taken is a warning, not a refusal: the user may be about to free it.\n if (!(await isPortFree(port))) {\n console.log(c.yellow(` Attention : le port ${port} est occupé actuellement.`));\n }\n return port;\n }\n}\n\n/**\n * Walk the user through scope and name.\n * Returns null if they abort (Ctrl+C / Ctrl+D), so the caller can exit quietly.\n */\nexport async function runInstallWizard(cwd: string): Promise<WizardAnswers | null> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n // Ctrl+C during a question closes the interface and rejects the pending\n // promise; treat that as an abort rather than a crash.\n let aborted = false;\n rl.on('SIGINT', () => { aborted = true; rl.close(); });\n\n try {\n console.log(`\\n${c.bold('Création d\\'un raccourci Runeya')}`);\n console.log(c.dim('─'.repeat(46)));\n\n const scope = await askChoice(rl, 'Quel type de raccourci ?', [\n {\n label: `Ce projet ${c.dim(`(${basename(cwd)})`)}`,\n hint: `Démarre Runeya dans ${cwd}`,\n },\n {\n label: 'Global',\n hint: 'Démarre Runeya dans votre dossier personnel — les données iront dans ~/.runeya',\n },\n ]);\n\n const workingDir = scope === 0 ? cwd : null;\n const suggested = workingDir ? `Runeya — ${basename(workingDir)}` : 'Runeya';\n const name = await askText(rl, 'Nom du raccourci ?', suggested);\n const { port, agentPort } = await askPorts(rl);\n\n return { workingDir, name, port, agentPort };\n } catch (err) {\n if (err instanceof WizardAborted) return null;\n throw err;\n } finally {\n if (aborted) console.log('\\nAnnulé.');\n rl.close();\n }\n}\n\n// ─── Result step ─────────────────────────────────────────────────────────────\n\n/** Right-pad the keys of a two-column block so the values line up. */\nfunction keyValueBlock(rows: [string, string][]): string {\n const width = Math.max(...rows.map(([k]) => k.length));\n return rows.map(([k, v]) => ` ${c.dim(k.padEnd(width))} ${v}`).join('\\n');\n}\n\n/**\n * Quote a value for the shell only when it needs it, so the printed uninstall\n * command stays copy-pasteable in both cases.\n */\nexport function shellArg(value: string): string {\n return /^[\\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport type InstallReport = {\n name: string;\n workingDir: string | null;\n nodePath: string;\n port: number | null;\n agentPort: number | null;\n result: LauncherResult;\n};\n\nexport function printInstallReport(report: InstallReport): void {\n const { name, workingDir, nodePath, port, agentPort, result } = report;\n\n console.log(`\\n${c.green('✓')} ${c.bold('Raccourci créé')}`);\n console.log(c.dim('─'.repeat(46)));\n const effectivePort = port ?? 4000;\n console.log(keyValueBlock([\n ['Nom', name],\n ['Portée', workingDir ? `ce projet — ${workingDir}` : 'global (aucun dossier fixé)'],\n ['Adresse', `http://localhost:${effectivePort}`],\n ['Ports', `${effectivePort} (Runeya) · ${agentPort ?? effectivePort + 1} (agent)${port === null ? c.dim(' — défauts') : ''}`],\n ['Node', nodePath],\n ]));\n\n console.log(`\\n${c.bold('Fichiers écrits')}`);\n for (const path of result.paths) console.log(` ${path}`);\n\n for (const warning of result.warnings) console.log(`\\n${c.yellow('⚠')} ${warning}`);\n\n console.log(`\\n${c.bold('Où le trouver')}`);\n console.log(` ${c.dim(whereToFind())}`);\n\n console.log(`\\n${c.bold('Pour le supprimer')}`);\n console.log(` ${c.cyan(`runeya uninstall-desktop --name ${shellArg(name)}`)}`);\n console.log(` ${c.dim('Cette commande retire exactement les fichiers listés ci-dessus.')}`);\n\n console.log(`\\n${c.dim('Le raccourci pointe vers le Node ci-dessus. Si vous changez de version')}`);\n console.log(`${c.dim('de Node, relancez `runeya install-desktop` pour le repointer.')}\\n`);\n}\n\n/** Per-platform hint about where the entry shows up. */\nfunction whereToFind(): string {\n switch (process.platform) {\n case 'darwin':\n return 'Dans ~/Applications, et via Spotlight (⌘ + espace).';\n case 'win32':\n return 'Dans le menu Démarrer.';\n default:\n return 'Dans le menu des applications de votre bureau. Certains environnements demandent une reconnexion.';\n }\n}\n\nexport function printUninstallReport(name: string, result: LauncherResult): void {\n if (result.paths.length === 0) {\n console.log(`\\nAucun raccourci « ${name} » trouvé.`);\n console.log(`${c.dim('Astuce : précisez le nom exact avec --name, ou le dossier du projet en argument.')}\\n`);\n return;\n }\n\n console.log(`\\n${c.green('✓')} ${c.bold('Raccourci supprimé')} ${c.dim(`— ${name}`)}`);\n console.log(c.dim('─'.repeat(46)));\n for (const path of result.paths) console.log(` ${path}`);\n for (const warning of result.warnings) console.log(`\\n${c.yellow('⚠')} ${warning}`);\n console.log('');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|