@runfusion/fusion 0.52.0 → 0.53.1
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/dist/bin.js +3064 -2337
- package/dist/client/.vite/manifest.json +248 -248
- package/dist/client/assets/{AgentDetailView-C-PM8cwG.js → AgentDetailView-DYiAlisV.js} +3 -3
- package/dist/client/assets/AgentPermissionPolicyEditor-CMtzKP_x.js +6 -0
- package/dist/client/assets/AgentPermissionPolicyEditor-TpdlOWZW.css +1 -0
- package/dist/client/assets/{AgentsView-DJJO0laF.js → AgentsView-YfJlxZoN.js} +3 -3
- package/dist/client/assets/ChatView-Diuc5LOe.js +6 -0
- package/dist/client/assets/{CommandCenter-CKvo0MuO.js → CommandCenter-CIwFDUKg.js} +2 -2
- package/dist/client/assets/{DevServerView-BHLG4Mxg.js → DevServerView-DDRHfATy.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CRY-rpnj.js → DirectoryPicker-C6HRGnSQ.js} +1 -1
- package/dist/client/assets/{DocumentsView-DhC4dnXD.js → DocumentsView-C5RTxVSE.js} +1 -1
- package/dist/client/assets/{EvalsView-DsnLOVZB.js → EvalsView-B9DCS8sV.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-B4iFX60E.js → ExperimentalAgentOnboardingModal-DGmpwghz.js} +1 -1
- package/dist/client/assets/{GoalsView-D5bzq6W9.js → GoalsView-B2hr3p6f.js} +1 -1
- package/dist/client/assets/{InsightsView-Drpu72M4.js → InsightsView-DM3eYN2S.js} +1 -1
- package/dist/client/assets/{MemoryView-auLBP3UW.js → MemoryView-8Yg8r9qS.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-RlbkXf_t.js → PiExtensionsManager-DbZ5qtAF.js} +1 -1
- package/dist/client/assets/{PluginManager-BJMUacpG.js → PluginManager-C1YuxOWu.js} +1 -1
- package/dist/client/assets/{PullRequestView-344B9WF-.js → PullRequestView-CMVvVGaP.js} +1 -1
- package/dist/client/assets/{ResearchView-BNqt-f53.js → ResearchView-BjvgxMjL.js} +1 -1
- package/dist/client/assets/{SecretsView-Be8Tm8It.js → SecretsView-gChNSW57.js} +1 -1
- package/dist/client/assets/SessionTerminal-BSeQ5i5F.js +2 -0
- package/dist/client/assets/SettingsModal-CQuHnNXu.js +21 -0
- package/dist/client/assets/{SettingsModal-B28DuhaN.js → SettingsModal-_04cEhgW.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-y2j9aZoi.js → SetupWizardModal-Dk9VO68p.js} +2 -2
- package/dist/client/assets/ShadcnColorPicker-3OgfenMo.css +1 -0
- package/dist/client/assets/ShadcnColorPicker-DyPMec2U.js +11 -0
- package/dist/client/assets/{SkillsView-HQp7UTdA.js → SkillsView-BAMinq2_.js} +1 -1
- package/dist/client/assets/{TodoView-DF7_qWGH.js → TodoView-DaXGwdTQ.js} +1 -1
- package/dist/client/assets/WorkflowNodeEditor-DmJI-3Qp.js +8 -0
- package/dist/client/assets/{agentSkills-Cy_HrYHd.js → agentSkills-B_n4hPLc.js} +1 -1
- package/dist/client/assets/app-5S4z84u5.js +13 -0
- package/dist/client/assets/app-B1VYocx2.js +13 -0
- package/dist/client/assets/app-BhtYYxin.js +13 -0
- package/dist/client/assets/app-Cts0__Vx.js +13 -0
- package/dist/client/assets/app-DczBmsP4.js +13 -0
- package/dist/client/assets/app-_GFUqcMK.js +13 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-4ur4sxlr.js → architectureDiagram-3BPJPVTR-DysoZtiI.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-BgZeqq_q.js → blockDiagram-GPEHLZMM-DpwJlK7i.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BQtdOIrm.js → c4Diagram-AAUBKEIU-DV115Z4B.js} +1 -1
- package/dist/client/assets/channel-BgnsK1mp.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-C1k1PBy1.js → chunk-2J33WTMH-B5BtZWCx.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-hNujwvSn.js → chunk-4BX2VUAB-CJvgQM9e.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CnXV9LyI.js → chunk-55IACEB6-ClrKLhVZ.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-B-ocMdYN.js → chunk-727SXJPM-DhfUMdP5.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-CWNRUL6C.js → chunk-AQP2D5EJ-CGKqiWfs.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DerrCiYe.js → chunk-FMBD7UC4-sLs4-jLA.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-BfcSPlX-.js → chunk-ND2GUHAM-BaptHWZM.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-BaWScAds.js → chunk-QZHKN3VN-CArEOzHE.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ZkHxi9su.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ZkHxi9su.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-n0Oeinz2.js → cose-bilkent-S5V4N54A-DZ4jAfQc.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-CUtehSt2.js → dagre-BM42HDAG-CMhtRslH.js} +1 -1
- package/dist/client/assets/{dashboard-view-BW3GHqgb.js → dashboard-view-Bc5cz2DG.js} +1 -1
- package/dist/client/assets/{dashboard-view-DWKCxtr5.js → dashboard-view-Buq7raMZ.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-Bz4aOzhR.js → diagram-2AECGRRQ-6uVCJGM-.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-UoxIMwPf.js → diagram-5GNKFQAL-fRigQDqm.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BYR7hloS.js → diagram-KO2AKTUF-DeLeKk1C.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-BR9MaP8U.js → diagram-LMA3HP47-BVRY5C4e.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BOfldYHv.js → diagram-OG6HWLK6-DdP1xMRM.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-B8x9mfOs.js → erDiagram-TEJ5UH35-BU5DG9IX.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DBnTroCV.js → flowDiagram-I6XJVG4X-CFSihB_T.js} +1 -1
- package/dist/client/assets/{folder-open-CFze34Eq.js → folder-open-CV5ek-IV.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BfzFy7iz.js → ganttDiagram-6RSMTGT7-BFVLXtHC.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CNTFu6IY.js → gitGraphDiagram-PVQCEYII-TiwSURa4.js} +1 -1
- package/dist/client/assets/index-CEUULqTR.js +1336 -0
- package/dist/client/assets/index-D1IHwGhg.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-DJiBRaAl.js → infoDiagram-5YYISTIA-Drkf3o26.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CIY9t4cc.js → ishikawaDiagram-YF4QCWOH-a9ZSlbx2.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-HLMZmcjY.js → journeyDiagram-JHISSGLW-DF_3fPPW.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-x9nKoxCI.js → kanban-definition-UN3LZRKU-B6dXVyXh.js} +1 -1
- package/dist/client/assets/{mermaid.core-pHkdB6fb.js → mermaid.core-BGFpwAba.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-Bs_4Tofu.js → mindmap-definition-RKZ34NQL-D5bZ0zCy.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BGHz9JGZ.js → pieDiagram-4H26LBE5-Dh6rUd7R.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-xHSR7cAR.js → quadrantDiagram-W4KKPZXB-D9Fy6Nl1.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-D7YMNM44.js → requirementDiagram-4Y6WPE33-Bky271Dr.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-OAXuab3Y.js → sankeyDiagram-5OEKKPKP-Cs9Bx7N3.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BZCjKFVL.js → sequenceDiagram-3UESZ5HK-BV7DAI0_.js} +1 -1
- package/dist/client/assets/{shield-alert-BBK8Risa.js → shield-alert-BEwCrI8k.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-COnJQqA4.js → stateDiagram-AJRCARHV-CTvWoU00.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-Diml4Nhm.js → stateDiagram-v2-BHNVJYJU-ZSZ46LWU.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DHIKvV9L.js → timeline-definition-PNZ67QCA-DSJscSa7.js} +1 -1
- package/dist/client/assets/{upload-CqUJUdZW.js → upload-CNW5goS6.js} +1 -1
- package/dist/client/assets/{users-CAE-FEzI.js → users-BhzdPkLN.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-o9AxLDoL.js → vennDiagram-CIIHVFJN-CziD-NoU.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-B7MIazje.js → wardley-L42UT6IY-Bt1vdMb3.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B6qUJx0N.js → wardleyDiagram-YWT4CUSO-B_Dlx683.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-13mSXuCA.js → xychartDiagram-2RQKCTM6-cYDRrhyM.js} +1 -1
- package/dist/client/index.html +3 -3
- package/dist/client/theme-data.css +156 -0
- package/dist/client/version.json +1 -1
- package/dist/desktop/client/.vite/manifest.json +1652 -0
- package/dist/desktop/client/assets/AgentDetailView-BzSyldiW.css +1 -0
- package/dist/desktop/client/assets/AgentDetailView-SJpg9JA5.js +18 -0
- package/dist/desktop/client/assets/AgentPermissionPolicyEditor-KMUDm0nu.js +6 -0
- package/dist/desktop/client/assets/AgentPermissionPolicyEditor-TpdlOWZW.css +1 -0
- package/dist/desktop/client/assets/AgentsView-CITigHGD.js +24 -0
- package/dist/desktop/client/assets/AgentsView-CzMa5Olf.css +1 -0
- package/dist/desktop/client/assets/ChatView-D_2wBIIx.css +1 -0
- package/dist/desktop/client/assets/ChatView-EeFLKmZB.js +6 -0
- package/dist/desktop/client/assets/CommandCenter-B9q-zM9p.css +1 -0
- package/dist/desktop/client/assets/CommandCenter-DmqJyMvs.js +71 -0
- package/dist/desktop/client/assets/DevServerView-Ckhpio_x.js +1 -0
- package/dist/desktop/client/assets/DevServerView-iBondC7z.css +1 -0
- package/dist/desktop/client/assets/DirectoryPicker-0rspvcMz.css +1 -0
- package/dist/desktop/client/assets/DirectoryPicker-j-rxo0kC.js +1 -0
- package/dist/desktop/client/assets/DocumentsView-DCS-N7cU.js +1 -0
- package/dist/desktop/client/assets/EvalsView-CTbgPUne.css +1 -0
- package/dist/desktop/client/assets/EvalsView-Dd26ipFU.js +1 -0
- package/dist/desktop/client/assets/ExperimentalAgentOnboardingModal-3ZEFv8sr.css +1 -0
- package/dist/desktop/client/assets/ExperimentalAgentOnboardingModal-BqgWIa9n.js +1 -0
- package/dist/desktop/client/assets/GoalsView-4-eEOeqw.css +1 -0
- package/dist/desktop/client/assets/GoalsView-C5gWzKqI.js +2 -0
- package/dist/desktop/client/assets/InsightsView-2SPZDH3m.js +11 -0
- package/dist/desktop/client/assets/InsightsView-C7yRTMPn.css +1 -0
- package/dist/desktop/client/assets/MemoryView-CBDUr2Jy.css +1 -0
- package/dist/desktop/client/assets/MemoryView-D2OAkbGv.js +2 -0
- package/dist/desktop/client/assets/PiExtensionsManager-DLrKJDwr.js +11 -0
- package/dist/desktop/client/assets/PiExtensionsManager-kgTOHPE9.css +1 -0
- package/dist/desktop/client/assets/PluginManager-B0CMG4RR.js +1 -0
- package/dist/desktop/client/assets/PluginManager-DSiHizRD.css +1 -0
- package/dist/desktop/client/assets/PullRequestView-DhTrKHeu.js +6 -0
- package/dist/desktop/client/assets/PullRequestView-NkeImY1n.css +1 -0
- package/dist/desktop/client/assets/ResearchView-BQwV9L4h.js +1 -0
- package/dist/desktop/client/assets/ResearchView-DIVNQF4_.css +1 -0
- package/dist/desktop/client/assets/SecretsView-Boby9tkU.js +1 -0
- package/dist/desktop/client/assets/SecretsView-CvFioX7R.css +1 -0
- package/dist/desktop/client/assets/SessionTerminal-CPbzm5Oi.css +1 -0
- package/dist/desktop/client/assets/SessionTerminal-CT_DUO7z.js +2 -0
- package/dist/desktop/client/assets/SettingsModal-BzZNPNIh.css +1 -0
- package/dist/desktop/client/assets/SettingsModal-CaE4JYxi.css +1 -0
- package/dist/desktop/client/assets/SettingsModal-CvsRQchW.js +1 -0
- package/dist/desktop/client/assets/SettingsModal-Dijfj3cb.js +21 -0
- package/dist/desktop/client/assets/SetupWizardModal-BIiJIXkh.js +2 -0
- package/dist/desktop/client/assets/ShadcnColorPicker-0ZZqsOqD.js +11 -0
- package/dist/desktop/client/assets/ShadcnColorPicker-3OgfenMo.css +1 -0
- package/dist/desktop/client/assets/SkillsView-Pzo5lSju.js +1 -0
- package/dist/desktop/client/assets/SkillsView-tAW7TTCz.css +1 -0
- package/dist/desktop/client/assets/TodoView--YoVYWvw.css +1 -0
- package/dist/desktop/client/assets/TodoView-ClCgLTmi.js +6 -0
- package/dist/desktop/client/assets/WorkflowNodeEditor-8XHmq-V2.js +8 -0
- package/dist/desktop/client/assets/WorkflowNodeEditor-Df-_206_.css +1 -0
- package/dist/desktop/client/assets/addon-fit-C1WQXRvu.js +1 -0
- package/dist/desktop/client/assets/addon-unicode11-BVmNDUBM.js +1 -0
- package/dist/desktop/client/assets/addon-web-links-CkVew5xs.js +1 -0
- package/dist/desktop/client/assets/addon-webgl-uLc0RKcC.js +58 -0
- package/dist/desktop/client/assets/agentSkills-D5osLOR5.js +1 -0
- package/dist/desktop/client/assets/agentSkills-bQJ4X1gH.css +1 -0
- package/dist/desktop/client/assets/agentsOrgChartLayout-BMvyuaLA.js +1 -0
- package/dist/desktop/client/assets/app-5S4z84u5.js +13 -0
- package/dist/desktop/client/assets/app-B1VYocx2.js +13 -0
- package/dist/desktop/client/assets/app-BhtYYxin.js +13 -0
- package/dist/desktop/client/assets/app-Cts0__Vx.js +13 -0
- package/dist/desktop/client/assets/app-DczBmsP4.js +13 -0
- package/dist/desktop/client/assets/app-_GFUqcMK.js +13 -0
- package/dist/desktop/client/assets/arc-cxkkNsnK.js +1 -0
- package/dist/desktop/client/assets/architectureDiagram-3BPJPVTR-CrjMDhPI.js +36 -0
- package/dist/desktop/client/assets/band-CquvqAHh.js +1 -0
- package/dist/desktop/client/assets/blockDiagram-GPEHLZMM-CTQo88ZR.js +132 -0
- package/dist/desktop/client/assets/c4Diagram-AAUBKEIU-YjMRExC0.js +10 -0
- package/dist/desktop/client/assets/channel-ByWi0lfq.js +1 -0
- package/dist/desktop/client/assets/chunk-2J33WTMH-O2741OpT.js +1 -0
- package/dist/desktop/client/assets/chunk-4BX2VUAB-E8A3umbR.js +1 -0
- package/dist/desktop/client/assets/chunk-55IACEB6-B052cwdC.js +1 -0
- package/dist/desktop/client/assets/chunk-727SXJPM-BKI0HQTP.js +206 -0
- package/dist/desktop/client/assets/chunk-AQP2D5EJ-DegV3EMW.js +231 -0
- package/dist/desktop/client/assets/chunk-FMBD7UC4-B7tLsOki.js +15 -0
- package/dist/desktop/client/assets/chunk-ND2GUHAM-2xF-Tg95.js +1 -0
- package/dist/desktop/client/assets/chunk-QZHKN3VN-DUl8dvkQ.js +1 -0
- package/dist/desktop/client/assets/classDiagram-4FO5ZUOK-C6nL18EV.js +1 -0
- package/dist/desktop/client/assets/classDiagram-v2-Q7XG4LA2-C6nL18EV.js +1 -0
- package/dist/desktop/client/assets/common-BDp2y6CF.js +1 -0
- package/dist/desktop/client/assets/common-BnEo5fLN.js +1 -0
- package/dist/desktop/client/assets/common-Bzr_Y8h4.js +1 -0
- package/dist/desktop/client/assets/common-CvwyFzfv.js +1 -0
- package/dist/desktop/client/assets/common-CztCXRkz.js +1 -0
- package/dist/desktop/client/assets/common-DPe2mbb7.js +1 -0
- package/dist/desktop/client/assets/cose-bilkent-S5V4N54A-DWz6vuMa.js +1 -0
- package/dist/desktop/client/assets/cytoscape.esm-OyMbaexL.js +331 -0
- package/dist/desktop/client/assets/dagre-BM42HDAG-GgO-3j8E.js +4 -0
- package/dist/desktop/client/assets/dashboard-view-CosTtyxo.js +1 -0
- package/dist/desktop/client/assets/dashboard-view-CuUPQZzi.css +1 -0
- package/dist/desktop/client/assets/dashboard-view-Cw1KM3Az.js +8161 -0
- package/dist/desktop/client/assets/dashboard-view-D_zsA-kz.css +1 -0
- package/dist/desktop/client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/desktop/client/assets/diagram-2AECGRRQ-BZQIXBBG.js +43 -0
- package/dist/desktop/client/assets/diagram-5GNKFQAL-B1pdxoln.js +10 -0
- package/dist/desktop/client/assets/diagram-KO2AKTUF-B2lApC5l.js +3 -0
- package/dist/desktop/client/assets/diagram-LMA3HP47-BRqKMh4D.js +24 -0
- package/dist/desktop/client/assets/diagram-OG6HWLK6-Dl7BJ0tE.js +24 -0
- package/dist/desktop/client/assets/erDiagram-TEJ5UH35-DS6QZa9s.js +85 -0
- package/dist/desktop/client/assets/errors-07IrmOYM.js +1 -0
- package/dist/desktop/client/assets/errors-BPsCufin.js +1 -0
- package/dist/desktop/client/assets/errors-CcVDgqlA.js +1 -0
- package/dist/desktop/client/assets/errors-D-i_Bxc6.js +1 -0
- package/dist/desktop/client/assets/errors-DDdWAK-U.js +1 -0
- package/dist/desktop/client/assets/errors-DOF5Td3F.js +1 -0
- package/dist/desktop/client/assets/flowDiagram-I6XJVG4X-Zk7IzQc2.js +162 -0
- package/dist/desktop/client/assets/folder-open-BhDX2lOd.js +6 -0
- package/dist/desktop/client/assets/ganttDiagram-6RSMTGT7-CJuYCcjE.js +292 -0
- package/dist/desktop/client/assets/gitGraphDiagram-PVQCEYII-BmCm6tvH.js +106 -0
- package/dist/desktop/client/assets/graph--OzhPTMs.js +1 -0
- package/dist/desktop/client/assets/index-BCe5TILt.css +1 -0
- package/dist/desktop/client/assets/index-CoS-MsCg.js +1336 -0
- package/dist/desktop/client/assets/infoDiagram-5YYISTIA-Ds6lrlTh.js +2 -0
- package/dist/desktop/client/assets/init-Dmth1JHB.js +1 -0
- package/dist/desktop/client/assets/ishikawaDiagram-YF4QCWOH-D3deq-vb.js +70 -0
- package/dist/desktop/client/assets/journeyDiagram-JHISSGLW-BuHVkT2m.js +139 -0
- package/dist/desktop/client/assets/kanban-definition-UN3LZRKU-DdoE5oU5.js +89 -0
- package/dist/desktop/client/assets/katex-HP8lGamR.js +257 -0
- package/dist/desktop/client/assets/layout-SsrduOYp.js +1 -0
- package/dist/desktop/client/assets/linear-CwI45w2t.js +1 -0
- package/dist/desktop/client/assets/mermaid.core-_nbzHc5D.js +309 -0
- package/dist/desktop/client/assets/mindmap-definition-RKZ34NQL-Bq-XuCz_.js +96 -0
- package/dist/desktop/client/assets/ordinal-DILIJJjt.js +1 -0
- package/dist/desktop/client/assets/pieDiagram-4H26LBE5-BgcfpW6J.js +30 -0
- package/dist/desktop/client/assets/projectDetection-G3XuxD2X.js +1 -0
- package/dist/desktop/client/assets/quadrantDiagram-W4KKPZXB-Xys80jUW.js +7 -0
- package/dist/desktop/client/assets/requirementDiagram-4Y6WPE33-C8S5lJeA.js +84 -0
- package/dist/desktop/client/assets/sankeyDiagram-5OEKKPKP-B_3GGQyd.js +40 -0
- package/dist/desktop/client/assets/sequenceDiagram-3UESZ5HK-DIu6iwmX.js +162 -0
- package/dist/desktop/client/assets/shield-alert-BHWZI_om.js +6 -0
- package/dist/desktop/client/assets/stateDiagram-AJRCARHV-CnaR2a9t.js +1 -0
- package/dist/desktop/client/assets/stateDiagram-v2-BHNVJYJU-VHc1xbe0.js +1 -0
- package/dist/desktop/client/assets/step-yuy0qJer.js +1 -0
- package/dist/desktop/client/assets/time-Ckmi30gH.js +1 -0
- package/dist/desktop/client/assets/timeline-definition-PNZ67QCA-BrtDAg8s.js +120 -0
- package/dist/desktop/client/assets/upload-DOLP3Bv3.js +6 -0
- package/dist/desktop/client/assets/users-2kEJQeOu.js +6 -0
- package/dist/desktop/client/assets/vendor-codemirror-jBKmHyH0.js +27 -0
- package/dist/desktop/client/assets/vendor-i18n-GF3RlKbq.js +9 -0
- package/dist/desktop/client/assets/vendor-react-SZ_qB27O.js +49 -0
- package/dist/desktop/client/assets/vendor-reactflow-C5ap-Sga.css +1 -0
- package/dist/desktop/client/assets/vendor-reactflow-CnI7NMXG.js +15 -0
- package/dist/desktop/client/assets/vendor-xterm-BNq00GSj.js +9 -0
- package/dist/desktop/client/assets/vendor-xterm-LZoznX6r.css +32 -0
- package/dist/desktop/client/assets/vennDiagram-CIIHVFJN-CVjr2RxJ.js +34 -0
- package/dist/desktop/client/assets/wardley-L42UT6IY-CVstgB5f.js +173 -0
- package/dist/desktop/client/assets/wardleyDiagram-YWT4CUSO-Da3szr-D.js +78 -0
- package/dist/desktop/client/assets/xychartDiagram-2RQKCTM6-B0o0ggVt.js +7 -0
- package/dist/desktop/client/assets/zoom-out-NWbVkreR.js +41 -0
- package/dist/desktop/client/brands/hermes-logo.svg +1 -0
- package/dist/desktop/client/fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
- package/dist/desktop/client/icons/icon-192.png +0 -0
- package/dist/desktop/client/icons/icon-512.png +0 -0
- package/dist/desktop/client/index.html +244 -0
- package/dist/desktop/client/logo.svg +6 -0
- package/dist/desktop/client/manifest.json +23 -0
- package/dist/desktop/client/sw.js +175 -0
- package/dist/desktop/client/theme-data.css +7154 -0
- package/dist/desktop/client/version.json +1 -0
- package/dist/desktop/main.js +1661 -0
- package/dist/desktop/main.js.map +7 -0
- package/dist/desktop/preload.js +110 -0
- package/dist/desktop/preload.js.map +7 -0
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +1692 -1418
- package/dist/pi-claude-cli/index.ts +40 -8
- package/dist/pi-claude-cli/package.json +5 -3
- package/dist/pi-claude-cli/src/__tests__/process-manager.test.ts +2 -2
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +39 -23
- package/dist/plugin-sdk/index.d.ts +9 -2
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +15 -3
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +4 -3
- package/dist/client/assets/AgentPermissionPolicyEditor-CGxjOr6G.css +0 -1
- package/dist/client/assets/AgentPermissionPolicyEditor-DrVW4VPc.js +0 -6
- package/dist/client/assets/ChatView-C7uTm5hO.js +0 -16
- package/dist/client/assets/SessionTerminal-CumHUelZ.js +0 -2
- package/dist/client/assets/SettingsModal-AJfer-ve.js +0 -21
- package/dist/client/assets/ShadcnColorPicker-Cc72uPKJ.js +0 -11
- package/dist/client/assets/ShadcnColorPicker-CyeAnJUU.css +0 -1
- package/dist/client/assets/WorkflowNodeEditor-BjpqTIYE.js +0 -8
- package/dist/client/assets/app-CGubyqAW.js +0 -13
- package/dist/client/assets/app-CrQpHbIs.js +0 -13
- package/dist/client/assets/app-DboK-0Ty.js +0 -13
- package/dist/client/assets/app-DjnYET2y.js +0 -13
- package/dist/client/assets/app-MacS555z.js +0 -13
- package/dist/client/assets/app-nvVayIeU.js +0 -13
- package/dist/client/assets/channel-Dn5XowfJ.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-zSOW-KNk.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-zSOW-KNk.js +0 -1
- package/dist/client/assets/index-QnHUwSjw.css +0 -1
- package/dist/client/assets/index-Y-vyXPPN.js +0 -1331
|
@@ -0,0 +1,1661 @@
|
|
|
1
|
+
// src/main.ts
|
|
2
|
+
import { app as app6, BrowserWindow as BrowserWindow4, nativeImage as nativeImage2, screen, Tray as Tray2 } from "electron";
|
|
3
|
+
import { join as join4, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
// src/deep-link.ts
|
|
7
|
+
import { app } from "electron";
|
|
8
|
+
var DEEP_LINK_EVENT = "deep-link";
|
|
9
|
+
var FUSION_SCHEME = "fusion:";
|
|
10
|
+
function registerDeepLinkProtocol() {
|
|
11
|
+
try {
|
|
12
|
+
const isRegistered = app.setAsDefaultProtocolClient("fusion");
|
|
13
|
+
if (!isRegistered) {
|
|
14
|
+
console.warn("[desktop/deep-link] Failed to register fusion:// protocol");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
console.log("[desktop/deep-link] Registered fusion:// protocol handler");
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("[desktop/deep-link] Error while registering fusion:// protocol", error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function parseDeepLink(rawUrl) {
|
|
23
|
+
if (!rawUrl) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const parsedUrl = new URL(rawUrl);
|
|
28
|
+
if (parsedUrl.protocol !== FUSION_SCHEME) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const type = parsedUrl.hostname;
|
|
32
|
+
if (type !== "task" && type !== "project") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const pathSegments = parsedUrl.pathname.split("/").filter((segment) => segment.length > 0);
|
|
36
|
+
const decodedId = pathSegments.length > 0 ? decodeURIComponent(pathSegments[0]) : "";
|
|
37
|
+
return {
|
|
38
|
+
type,
|
|
39
|
+
id: decodedId,
|
|
40
|
+
raw: rawUrl
|
|
41
|
+
};
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function handleDeepLink(mainWindow2, url) {
|
|
47
|
+
const parsed = parseDeepLink(url);
|
|
48
|
+
if (!parsed || parsed.type === "unknown") {
|
|
49
|
+
console.warn(`[desktop/deep-link] Ignoring unsupported deep link: ${url}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!mainWindow2.isVisible()) {
|
|
53
|
+
mainWindow2.show();
|
|
54
|
+
}
|
|
55
|
+
if (mainWindow2.isMinimized()) {
|
|
56
|
+
mainWindow2.restore();
|
|
57
|
+
}
|
|
58
|
+
mainWindow2.focus();
|
|
59
|
+
mainWindow2.webContents.send(DEEP_LINK_EVENT, parsed);
|
|
60
|
+
}
|
|
61
|
+
function setupDeepLinkHandler(mainWindow2) {
|
|
62
|
+
const hasLock = app.requestSingleInstanceLock();
|
|
63
|
+
if (!hasLock) {
|
|
64
|
+
app.quit();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
app.on("open-url", (event, url) => {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
handleDeepLink(mainWindow2, url);
|
|
70
|
+
});
|
|
71
|
+
app.on("second-instance", (_event, argv) => {
|
|
72
|
+
const deepLink = argv.find((arg) => arg.startsWith("fusion://"));
|
|
73
|
+
if (!deepLink) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
handleDeepLink(mainWindow2, deepLink);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/ipc.ts
|
|
81
|
+
import { app as app5, ipcMain } from "electron";
|
|
82
|
+
|
|
83
|
+
// src/native.ts
|
|
84
|
+
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
85
|
+
import { join } from "node:path";
|
|
86
|
+
import {
|
|
87
|
+
app as app2,
|
|
88
|
+
dialog,
|
|
89
|
+
Notification
|
|
90
|
+
} from "electron";
|
|
91
|
+
var MIN_VISIBLE_OVERLAP_PX = 64;
|
|
92
|
+
function clampWindowStateToVisibleDisplay(state, displays) {
|
|
93
|
+
if (state.x === void 0 || state.y === void 0) {
|
|
94
|
+
return state;
|
|
95
|
+
}
|
|
96
|
+
const windowLeft = state.x;
|
|
97
|
+
const windowTop = state.y;
|
|
98
|
+
const windowRight = state.x + state.width;
|
|
99
|
+
const windowBottom = state.y + state.height;
|
|
100
|
+
const hasSufficientOverlap = displays.some(({ workArea }) => {
|
|
101
|
+
const displayLeft = workArea.x;
|
|
102
|
+
const displayTop = workArea.y;
|
|
103
|
+
const displayRight = workArea.x + workArea.width;
|
|
104
|
+
const displayBottom = workArea.y + workArea.height;
|
|
105
|
+
const overlapWidth = Math.max(0, Math.min(windowRight, displayRight) - Math.max(windowLeft, displayLeft));
|
|
106
|
+
const overlapHeight = Math.max(0, Math.min(windowBottom, displayBottom) - Math.max(windowTop, displayTop));
|
|
107
|
+
return overlapWidth >= MIN_VISIBLE_OVERLAP_PX && overlapHeight >= MIN_VISIBLE_OVERLAP_PX;
|
|
108
|
+
});
|
|
109
|
+
if (hasSufficientOverlap) {
|
|
110
|
+
return state;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
width: state.width,
|
|
114
|
+
height: state.height,
|
|
115
|
+
isMaximized: state.isMaximized
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
var SHELL_HANDOFF_QUERY = {
|
|
119
|
+
shellKind: "shellKind",
|
|
120
|
+
shellMode: "shellMode",
|
|
121
|
+
profileId: "profileId",
|
|
122
|
+
serverBaseUrl: "serverBaseUrl",
|
|
123
|
+
serverLabel: "serverLabel",
|
|
124
|
+
token: "token",
|
|
125
|
+
canOpenConnectionManager: "shellCanOpenConnectionManager"
|
|
126
|
+
};
|
|
127
|
+
var DEFAULT_WINDOW_STATE = {
|
|
128
|
+
width: 1280,
|
|
129
|
+
height: 900,
|
|
130
|
+
isMaximized: false
|
|
131
|
+
};
|
|
132
|
+
function generateSettingsExportFilename(date = /* @__PURE__ */ new Date()) {
|
|
133
|
+
const year = date.getUTCFullYear();
|
|
134
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
135
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
136
|
+
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
137
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
138
|
+
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
|
139
|
+
return `fusion-settings-${year}-${month}-${day}-${hours}${minutes}${seconds}.json`;
|
|
140
|
+
}
|
|
141
|
+
function getWindowStatePath() {
|
|
142
|
+
return join(app2.getPath("userData"), "window-state.json");
|
|
143
|
+
}
|
|
144
|
+
function getDesktopLaunchModePath() {
|
|
145
|
+
return join(app2.getPath("userData"), "desktop-launch-mode.json");
|
|
146
|
+
}
|
|
147
|
+
function isValidWindowState(value) {
|
|
148
|
+
if (value === null || typeof value !== "object") {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const candidate = value;
|
|
152
|
+
const hasValidPosition = (candidate.x === void 0 || typeof candidate.x === "number") && (candidate.y === void 0 || typeof candidate.y === "number");
|
|
153
|
+
return hasValidPosition && typeof candidate.width === "number" && Number.isFinite(candidate.width) && typeof candidate.height === "number" && Number.isFinite(candidate.height) && typeof candidate.isMaximized === "boolean";
|
|
154
|
+
}
|
|
155
|
+
async function showExportSettingsDialog(parentWindow) {
|
|
156
|
+
const filename = generateSettingsExportFilename();
|
|
157
|
+
const defaultPath = join(app2.getPath("documents"), filename);
|
|
158
|
+
const dialogOptions = {
|
|
159
|
+
title: "Export Fusion Settings",
|
|
160
|
+
defaultPath,
|
|
161
|
+
filters: [{ name: "JSON Files", extensions: ["json"] }]
|
|
162
|
+
};
|
|
163
|
+
const result = parentWindow ? await dialog.showSaveDialog(parentWindow, dialogOptions) : await dialog.showSaveDialog(dialogOptions);
|
|
164
|
+
if (result.canceled || !result.filePath) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return result.filePath;
|
|
168
|
+
}
|
|
169
|
+
async function showImportSettingsDialog(parentWindow) {
|
|
170
|
+
const dialogOptions = {
|
|
171
|
+
title: "Import Fusion Settings",
|
|
172
|
+
properties: ["openFile"],
|
|
173
|
+
filters: [{ name: "JSON Files", extensions: ["json"] }]
|
|
174
|
+
};
|
|
175
|
+
const result = parentWindow ? await dialog.showOpenDialog(parentWindow, dialogOptions) : await dialog.showOpenDialog(dialogOptions);
|
|
176
|
+
if (result.canceled || result.filePaths.length === 0) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return result.filePaths[0] ?? null;
|
|
180
|
+
}
|
|
181
|
+
function showDesktopNotification(title, body, options = {}) {
|
|
182
|
+
if (!Notification.isSupported()) {
|
|
183
|
+
console.warn("[desktop/native] Notifications are not supported in this environment");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const notification = new Notification({
|
|
188
|
+
title,
|
|
189
|
+
body,
|
|
190
|
+
silent: options.silent
|
|
191
|
+
});
|
|
192
|
+
if (options.onClick) {
|
|
193
|
+
notification.on("click", options.onClick);
|
|
194
|
+
}
|
|
195
|
+
notification.show();
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error("[desktop/native] Failed to display desktop notification", error);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
var cachedAutoUpdater = null;
|
|
201
|
+
var autoUpdaterLoadPromise = null;
|
|
202
|
+
var listenersBound = false;
|
|
203
|
+
var hasRunInitialUpdateCheck = false;
|
|
204
|
+
var autoUpdaterWindow;
|
|
205
|
+
var updateIntervalTimer = null;
|
|
206
|
+
var updateIntervalDisposer = null;
|
|
207
|
+
var updateIntervalWindow = null;
|
|
208
|
+
function getErrorMessage(error) {
|
|
209
|
+
if (error instanceof Error) {
|
|
210
|
+
return error.message;
|
|
211
|
+
}
|
|
212
|
+
if (typeof error === "string") {
|
|
213
|
+
return error;
|
|
214
|
+
}
|
|
215
|
+
return "Unknown error";
|
|
216
|
+
}
|
|
217
|
+
async function resolveAutoUpdater() {
|
|
218
|
+
if (cachedAutoUpdater) {
|
|
219
|
+
return cachedAutoUpdater;
|
|
220
|
+
}
|
|
221
|
+
if (!autoUpdaterLoadPromise) {
|
|
222
|
+
autoUpdaterLoadPromise = (async () => {
|
|
223
|
+
try {
|
|
224
|
+
const mod = await import("electron-updater");
|
|
225
|
+
const namedAutoUpdater = "autoUpdater" in mod ? mod.autoUpdater : void 0;
|
|
226
|
+
const autoUpdater = mod.default?.autoUpdater ?? namedAutoUpdater;
|
|
227
|
+
if (!autoUpdater) {
|
|
228
|
+
console.warn("[desktop/native] Auto-updater module loaded without autoUpdater export");
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
cachedAutoUpdater = autoUpdater;
|
|
232
|
+
return autoUpdater;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.warn("[desktop/native] Auto-updater unavailable", error);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
})();
|
|
238
|
+
}
|
|
239
|
+
return autoUpdaterLoadPromise;
|
|
240
|
+
}
|
|
241
|
+
function bindAutoUpdaterListeners(autoUpdater) {
|
|
242
|
+
if (listenersBound) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
listenersBound = true;
|
|
246
|
+
autoUpdater.autoDownload = true;
|
|
247
|
+
autoUpdater.autoInstallOnAppQuit = true;
|
|
248
|
+
autoUpdater.on("update-available", (info) => {
|
|
249
|
+
showDesktopNotification("Fusion Update Available", "Update available \u2014 downloading in background", {
|
|
250
|
+
silent: true
|
|
251
|
+
});
|
|
252
|
+
autoUpdaterWindow?.webContents.send("update-available", info);
|
|
253
|
+
});
|
|
254
|
+
autoUpdater.on("update-downloaded", (info) => {
|
|
255
|
+
showDesktopNotification("Fusion Update Ready", "Update ready \u2014 will install on quit", {
|
|
256
|
+
silent: true
|
|
257
|
+
});
|
|
258
|
+
autoUpdaterWindow?.webContents.send("update-downloaded", info);
|
|
259
|
+
});
|
|
260
|
+
autoUpdater.on("update-not-available", (info) => {
|
|
261
|
+
showDesktopNotification("Fusion is up to date", "You're on the latest version", {
|
|
262
|
+
silent: true
|
|
263
|
+
});
|
|
264
|
+
autoUpdaterWindow?.webContents.send("update-not-available", info);
|
|
265
|
+
});
|
|
266
|
+
autoUpdater.on("error", (error) => {
|
|
267
|
+
const message = getErrorMessage(error);
|
|
268
|
+
console.error("[desktop/native] Auto-updater error", error);
|
|
269
|
+
autoUpdaterWindow?.webContents.send("update-error", { message });
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function setupAutoUpdater(mainWindow2) {
|
|
273
|
+
if (mainWindow2) {
|
|
274
|
+
autoUpdaterWindow = mainWindow2;
|
|
275
|
+
}
|
|
276
|
+
void (async () => {
|
|
277
|
+
try {
|
|
278
|
+
const autoUpdater = await resolveAutoUpdater();
|
|
279
|
+
if (!autoUpdater) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
bindAutoUpdaterListeners(autoUpdater);
|
|
283
|
+
if (hasRunInitialUpdateCheck) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
hasRunInitialUpdateCheck = true;
|
|
287
|
+
await autoUpdater.checkForUpdates().catch((error) => {
|
|
288
|
+
console.error("[desktop/native] Auto-updater check failed", error);
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.warn("[desktop/native] Auto-updater setup failed", error);
|
|
292
|
+
}
|
|
293
|
+
})();
|
|
294
|
+
}
|
|
295
|
+
async function triggerUpdateCheck(mainWindow2) {
|
|
296
|
+
setupAutoUpdater(mainWindow2);
|
|
297
|
+
const autoUpdater = await resolveAutoUpdater();
|
|
298
|
+
if (!autoUpdater) {
|
|
299
|
+
return { status: "unavailable", reason: "updater_unavailable" };
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
await autoUpdater.checkForUpdates();
|
|
303
|
+
return { status: "checking" };
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return { status: "error", error: getErrorMessage(error) };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function startUpdateCheckInterval(mainWindow2, intervalMs = 4 * 60 * 60 * 1e3) {
|
|
309
|
+
autoUpdaterWindow = mainWindow2;
|
|
310
|
+
if (updateIntervalDisposer && updateIntervalWindow === mainWindow2) {
|
|
311
|
+
return updateIntervalDisposer;
|
|
312
|
+
}
|
|
313
|
+
if (updateIntervalTimer) {
|
|
314
|
+
clearInterval(updateIntervalTimer);
|
|
315
|
+
updateIntervalTimer = null;
|
|
316
|
+
}
|
|
317
|
+
updateIntervalWindow = mainWindow2;
|
|
318
|
+
updateIntervalTimer = setInterval(() => {
|
|
319
|
+
void triggerUpdateCheck(mainWindow2);
|
|
320
|
+
}, intervalMs);
|
|
321
|
+
updateIntervalDisposer = () => {
|
|
322
|
+
if (updateIntervalTimer) {
|
|
323
|
+
clearInterval(updateIntervalTimer);
|
|
324
|
+
updateIntervalTimer = null;
|
|
325
|
+
}
|
|
326
|
+
updateIntervalDisposer = null;
|
|
327
|
+
updateIntervalWindow = null;
|
|
328
|
+
};
|
|
329
|
+
return updateIntervalDisposer;
|
|
330
|
+
}
|
|
331
|
+
function normalizeServerBaseUrl(serverUrl) {
|
|
332
|
+
try {
|
|
333
|
+
const parsed = new URL(serverUrl);
|
|
334
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
return parsed.toString().replace(/\/$/, "");
|
|
338
|
+
} catch {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function normalizeDesktopRemoteLaunch(settings) {
|
|
343
|
+
if (settings.desktopMode !== "remote" || !settings.activeProfileId) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const activeProfile = settings.profiles.find((profile) => profile.id === settings.activeProfileId);
|
|
347
|
+
if (!activeProfile) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
const serverBaseUrl = normalizeServerBaseUrl(activeProfile.serverUrl);
|
|
351
|
+
if (!serverBaseUrl) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
mode: "remote",
|
|
356
|
+
profileId: activeProfile.id,
|
|
357
|
+
serverBaseUrl,
|
|
358
|
+
...activeProfile.name ? { serverLabel: activeProfile.name } : {},
|
|
359
|
+
...activeProfile.authToken ? { authToken: activeProfile.authToken } : {}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function buildRemoteShellHandoffUrl(launch) {
|
|
363
|
+
const url = new URL(launch.serverBaseUrl);
|
|
364
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.shellKind, "desktop");
|
|
365
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.shellMode, "remote");
|
|
366
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.profileId, launch.profileId);
|
|
367
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.serverBaseUrl, launch.serverBaseUrl);
|
|
368
|
+
if (launch.serverLabel) {
|
|
369
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.serverLabel, launch.serverLabel);
|
|
370
|
+
}
|
|
371
|
+
if (launch.authToken) {
|
|
372
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.token, launch.authToken);
|
|
373
|
+
}
|
|
374
|
+
url.searchParams.set(SHELL_HANDOFF_QUERY.canOpenConnectionManager, "1");
|
|
375
|
+
return url.toString();
|
|
376
|
+
}
|
|
377
|
+
function isValidDesktopLaunchMode(value) {
|
|
378
|
+
return value === "choose" || value === "local" || value === "remote";
|
|
379
|
+
}
|
|
380
|
+
async function loadDesktopLaunchMode() {
|
|
381
|
+
const launchModePath = getDesktopLaunchModePath();
|
|
382
|
+
try {
|
|
383
|
+
const raw = await readFile(launchModePath, "utf-8");
|
|
384
|
+
const parsed = JSON.parse(raw);
|
|
385
|
+
if (parsed && typeof parsed === "object" && "mode" in parsed) {
|
|
386
|
+
const mode = parsed.mode;
|
|
387
|
+
if (isValidDesktopLaunchMode(mode)) {
|
|
388
|
+
return mode;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return "choose";
|
|
392
|
+
} catch {
|
|
393
|
+
return "choose";
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function saveDesktopLaunchMode(mode) {
|
|
397
|
+
const launchModePath = getDesktopLaunchModePath();
|
|
398
|
+
const tempPath = `${launchModePath}.tmp`;
|
|
399
|
+
await writeFile(tempPath, JSON.stringify({ mode }, null, 2), "utf-8");
|
|
400
|
+
await rename(tempPath, launchModePath);
|
|
401
|
+
}
|
|
402
|
+
async function loadWindowState() {
|
|
403
|
+
const statePath = getWindowStatePath();
|
|
404
|
+
try {
|
|
405
|
+
const raw = await readFile(statePath, "utf-8");
|
|
406
|
+
const parsed = JSON.parse(raw);
|
|
407
|
+
if (!isValidWindowState(parsed)) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
return parsed;
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (error.code === "ENOENT") {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function saveWindowState(mainWindow2) {
|
|
419
|
+
if (mainWindow2.isDestroyed()) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const bounds = mainWindow2.getBounds();
|
|
423
|
+
const state = {
|
|
424
|
+
x: bounds.x,
|
|
425
|
+
y: bounds.y,
|
|
426
|
+
width: bounds.width,
|
|
427
|
+
height: bounds.height,
|
|
428
|
+
isMaximized: mainWindow2.isMaximized()
|
|
429
|
+
};
|
|
430
|
+
const statePath = getWindowStatePath();
|
|
431
|
+
const tempPath = `${statePath}.tmp`;
|
|
432
|
+
void writeFile(tempPath, JSON.stringify(state, null, 2), "utf-8").then(() => rename(tempPath, statePath)).catch((error) => {
|
|
433
|
+
console.error("[desktop/native] Failed to save window state", error);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/tray.ts
|
|
438
|
+
import path from "node:path";
|
|
439
|
+
import {
|
|
440
|
+
app as app3,
|
|
441
|
+
Menu,
|
|
442
|
+
nativeImage
|
|
443
|
+
} from "electron";
|
|
444
|
+
var trayState = /* @__PURE__ */ new WeakMap();
|
|
445
|
+
function toggleMainWindow(mainWindow2) {
|
|
446
|
+
if (mainWindow2.isVisible()) {
|
|
447
|
+
mainWindow2.hide();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
mainWindow2.show();
|
|
451
|
+
mainWindow2.focus();
|
|
452
|
+
}
|
|
453
|
+
function resolveEngineMenuLabel(engineStatus) {
|
|
454
|
+
return engineStatus === "running" ? "Pause Engine" : "Resume Engine";
|
|
455
|
+
}
|
|
456
|
+
function applyTrayMenu(tray2, state) {
|
|
457
|
+
const baseTemplate = buildTrayContextMenu({
|
|
458
|
+
isWindowVisible: state.mainWindow.isVisible(),
|
|
459
|
+
engineStatus: state.engineStatus
|
|
460
|
+
});
|
|
461
|
+
const contextTemplate = baseTemplate.map((item) => {
|
|
462
|
+
if (item.type === "separator") {
|
|
463
|
+
return item;
|
|
464
|
+
}
|
|
465
|
+
if (item.label === "Show Window" || item.label === "Hide Window") {
|
|
466
|
+
return {
|
|
467
|
+
...item,
|
|
468
|
+
click: () => toggleMainWindow(state.mainWindow)
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (item.label === "Pause Engine" || item.label === "Resume Engine") {
|
|
472
|
+
return {
|
|
473
|
+
...item,
|
|
474
|
+
click: () => {
|
|
475
|
+
if (state.engineStatus === "stopped") {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
state.engineStatus = state.engineStatus === "running" ? "paused" : "running";
|
|
479
|
+
applyTrayMenu(tray2, state);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
...item,
|
|
485
|
+
click: () => {
|
|
486
|
+
state.isQuitting = true;
|
|
487
|
+
app3.quit();
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
tray2.setToolTip(getTrayTooltip(state.engineStatus));
|
|
492
|
+
tray2.setContextMenu(Menu.buildFromTemplate(contextTemplate));
|
|
493
|
+
}
|
|
494
|
+
function createTrayIcon() {
|
|
495
|
+
if (process.platform === "darwin") {
|
|
496
|
+
const iconPath2 = path.join(import.meta.dirname, "icons", "tray-32.png");
|
|
497
|
+
const retinaIcon = nativeImage.createFromPath(iconPath2);
|
|
498
|
+
return retinaIcon.resize({ width: 16, height: 16 });
|
|
499
|
+
}
|
|
500
|
+
const iconPath = path.join(import.meta.dirname, "icons", "tray-48.png");
|
|
501
|
+
return nativeImage.createFromPath(iconPath);
|
|
502
|
+
}
|
|
503
|
+
function buildTrayContextMenu(options) {
|
|
504
|
+
return [
|
|
505
|
+
{
|
|
506
|
+
label: options.isWindowVisible ? "Hide Window" : "Show Window"
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
type: "separator"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
label: resolveEngineMenuLabel(options.engineStatus),
|
|
513
|
+
enabled: options.engineStatus !== "stopped"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
type: "separator"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
label: "Quit Fusion"
|
|
520
|
+
}
|
|
521
|
+
];
|
|
522
|
+
}
|
|
523
|
+
function getTrayTooltip(status) {
|
|
524
|
+
switch (status) {
|
|
525
|
+
case "paused":
|
|
526
|
+
return "Fusion \u2014 Paused";
|
|
527
|
+
case "stopped":
|
|
528
|
+
return "Fusion \u2014 Stopped";
|
|
529
|
+
case "running":
|
|
530
|
+
default:
|
|
531
|
+
return "Fusion \u2014 Running";
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function setupTray(mainWindow2, tray2) {
|
|
535
|
+
const state = {
|
|
536
|
+
mainWindow: mainWindow2,
|
|
537
|
+
engineStatus: "running",
|
|
538
|
+
isQuitting: false
|
|
539
|
+
};
|
|
540
|
+
trayState.set(tray2, state);
|
|
541
|
+
tray2.setImage(createTrayIcon());
|
|
542
|
+
applyTrayMenu(tray2, state);
|
|
543
|
+
tray2.on("click", () => {
|
|
544
|
+
toggleMainWindow(mainWindow2);
|
|
545
|
+
});
|
|
546
|
+
mainWindow2.on("show", () => {
|
|
547
|
+
applyTrayMenu(tray2, state);
|
|
548
|
+
});
|
|
549
|
+
mainWindow2.on("hide", () => {
|
|
550
|
+
applyTrayMenu(tray2, state);
|
|
551
|
+
});
|
|
552
|
+
mainWindow2.on("close", (event) => {
|
|
553
|
+
if (state.isQuitting) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
event.preventDefault();
|
|
557
|
+
mainWindow2.hide();
|
|
558
|
+
});
|
|
559
|
+
app3.on("before-quit", () => {
|
|
560
|
+
state.isQuitting = true;
|
|
561
|
+
});
|
|
562
|
+
return tray2;
|
|
563
|
+
}
|
|
564
|
+
function updateTrayStatus(tray2, status) {
|
|
565
|
+
const state = trayState.get(tray2);
|
|
566
|
+
if (!state) {
|
|
567
|
+
tray2.setToolTip(getTrayTooltip(status));
|
|
568
|
+
const menu = Menu.buildFromTemplate(
|
|
569
|
+
buildTrayContextMenu({
|
|
570
|
+
isWindowVisible: false,
|
|
571
|
+
engineStatus: status
|
|
572
|
+
})
|
|
573
|
+
);
|
|
574
|
+
tray2.setContextMenu(menu);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
state.engineStatus = status;
|
|
578
|
+
applyTrayMenu(tray2, state);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/shell-settings.ts
|
|
582
|
+
import { readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
583
|
+
import { createHash } from "node:crypto";
|
|
584
|
+
import { join as join2 } from "node:path";
|
|
585
|
+
import { app as app4 } from "electron";
|
|
586
|
+
var DEFAULT_SETTINGS = {
|
|
587
|
+
desktopMode: null,
|
|
588
|
+
hasCompletedModeSelection: false,
|
|
589
|
+
activeProfileId: null,
|
|
590
|
+
profiles: []
|
|
591
|
+
};
|
|
592
|
+
function getSettingsPath() {
|
|
593
|
+
return join2(app4.getPath("userData"), "shell-connections.json");
|
|
594
|
+
}
|
|
595
|
+
function nowIso() {
|
|
596
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
597
|
+
}
|
|
598
|
+
function normalizeDesktopMode(value) {
|
|
599
|
+
return value === "local" || value === "remote" ? value : null;
|
|
600
|
+
}
|
|
601
|
+
function normalizeServerUrl(serverUrl) {
|
|
602
|
+
const normalized = serverUrl.trim().replace(/\/$/, "");
|
|
603
|
+
let parsed;
|
|
604
|
+
try {
|
|
605
|
+
parsed = new URL(normalized);
|
|
606
|
+
} catch {
|
|
607
|
+
throw new Error("Server URL must be a valid absolute URL");
|
|
608
|
+
}
|
|
609
|
+
if (!/^https?:$/.test(parsed.protocol)) {
|
|
610
|
+
throw new Error("Server URL must use http or https");
|
|
611
|
+
}
|
|
612
|
+
return normalized;
|
|
613
|
+
}
|
|
614
|
+
function normalizeProfileName(name) {
|
|
615
|
+
const normalized = name.trim().replace(/\s+/g, " ");
|
|
616
|
+
return normalized.length > 0 ? normalized : "Remote Server";
|
|
617
|
+
}
|
|
618
|
+
function profileBaseId(name, serverUrl) {
|
|
619
|
+
const hash = createHash("sha1").update(`${name}|${serverUrl}`).digest("hex").slice(0, 10);
|
|
620
|
+
return `profile_${hash}`;
|
|
621
|
+
}
|
|
622
|
+
function ensureUniqueProfileName(name, profiles, skipId) {
|
|
623
|
+
const used = new Set(
|
|
624
|
+
profiles.filter((profile) => profile.id !== skipId).map((profile) => profile.name.toLocaleLowerCase())
|
|
625
|
+
);
|
|
626
|
+
if (!used.has(name.toLocaleLowerCase())) {
|
|
627
|
+
return name;
|
|
628
|
+
}
|
|
629
|
+
let suffix = 2;
|
|
630
|
+
let candidate = `${name} (${suffix})`;
|
|
631
|
+
while (used.has(candidate.toLocaleLowerCase())) {
|
|
632
|
+
suffix += 1;
|
|
633
|
+
candidate = `${name} (${suffix})`;
|
|
634
|
+
}
|
|
635
|
+
return candidate;
|
|
636
|
+
}
|
|
637
|
+
function createDeterministicProfileId(name, serverUrl, profiles, skipId) {
|
|
638
|
+
const used = new Set(profiles.filter((profile) => profile.id !== skipId).map((profile) => profile.id));
|
|
639
|
+
const base = profileBaseId(name, serverUrl);
|
|
640
|
+
if (!used.has(base)) {
|
|
641
|
+
return base;
|
|
642
|
+
}
|
|
643
|
+
let suffix = 2;
|
|
644
|
+
let candidate = `${base}_${suffix}`;
|
|
645
|
+
while (used.has(candidate)) {
|
|
646
|
+
suffix += 1;
|
|
647
|
+
candidate = `${base}_${suffix}`;
|
|
648
|
+
}
|
|
649
|
+
return candidate;
|
|
650
|
+
}
|
|
651
|
+
function normalizeProfileRecord(input, fallbackIndex) {
|
|
652
|
+
if (!input || typeof input !== "object") {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
const candidate = input;
|
|
656
|
+
if (typeof candidate.serverUrl !== "string") {
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
let serverUrl;
|
|
660
|
+
try {
|
|
661
|
+
serverUrl = normalizeServerUrl(candidate.serverUrl);
|
|
662
|
+
} catch {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
const name = normalizeProfileName(typeof candidate.name === "string" ? candidate.name : "");
|
|
666
|
+
const createdAt = typeof candidate.createdAt === "string" && candidate.createdAt.length > 0 ? candidate.createdAt : nowIso();
|
|
667
|
+
const updatedAt = typeof candidate.updatedAt === "string" && candidate.updatedAt.length > 0 ? candidate.updatedAt : createdAt;
|
|
668
|
+
return {
|
|
669
|
+
id: typeof candidate.id === "string" && candidate.id.length > 0 ? candidate.id : `profile_imported_${fallbackIndex}`,
|
|
670
|
+
name,
|
|
671
|
+
serverUrl,
|
|
672
|
+
authToken: typeof candidate.authToken === "string" ? candidate.authToken : null,
|
|
673
|
+
createdAt,
|
|
674
|
+
updatedAt,
|
|
675
|
+
lastUsedAt: typeof candidate.lastUsedAt === "string" ? candidate.lastUsedAt : null
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function normalize(input) {
|
|
679
|
+
if (!input || typeof input !== "object") {
|
|
680
|
+
return { ...DEFAULT_SETTINGS };
|
|
681
|
+
}
|
|
682
|
+
const candidate = input;
|
|
683
|
+
const desktopMode = normalizeDesktopMode(candidate.desktopMode);
|
|
684
|
+
const inferredCompleted = desktopMode !== null;
|
|
685
|
+
const profiles = [];
|
|
686
|
+
const profileSource = Array.isArray(candidate.profiles) ? candidate.profiles : [];
|
|
687
|
+
for (const [index, profileValue] of profileSource.entries()) {
|
|
688
|
+
const normalizedProfile = normalizeProfileRecord(profileValue, index);
|
|
689
|
+
if (!normalizedProfile) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const uniqueName = ensureUniqueProfileName(normalizedProfile.name, profiles);
|
|
693
|
+
const idAlreadyUsed = profiles.some((profile) => profile.id === normalizedProfile.id);
|
|
694
|
+
const id = idAlreadyUsed ? createDeterministicProfileId(uniqueName, normalizedProfile.serverUrl, profiles) : normalizedProfile.id;
|
|
695
|
+
profiles.push({ ...normalizedProfile, name: uniqueName, id });
|
|
696
|
+
}
|
|
697
|
+
const persistedActiveId = typeof candidate.activeProfileId === "string" ? candidate.activeProfileId : null;
|
|
698
|
+
const activeProfileId = persistedActiveId && profiles.some((profile) => profile.id === persistedActiveId) ? persistedActiveId : null;
|
|
699
|
+
return {
|
|
700
|
+
desktopMode,
|
|
701
|
+
hasCompletedModeSelection: typeof candidate.hasCompletedModeSelection === "boolean" ? candidate.hasCompletedModeSelection : inferredCompleted,
|
|
702
|
+
activeProfileId,
|
|
703
|
+
profiles
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function buildSavedProfile(settings, input) {
|
|
707
|
+
const existing = input.id ? settings.profiles.find((item) => item.id === input.id) : void 0;
|
|
708
|
+
const normalizedServerUrl = normalizeServerUrl(input.serverUrl);
|
|
709
|
+
const normalizedName = normalizeProfileName(input.name);
|
|
710
|
+
const name = ensureUniqueProfileName(normalizedName, settings.profiles, existing?.id);
|
|
711
|
+
const timestamp = nowIso();
|
|
712
|
+
return {
|
|
713
|
+
id: existing?.id ?? createDeterministicProfileId(name, normalizedServerUrl, settings.profiles, existing?.id),
|
|
714
|
+
name,
|
|
715
|
+
serverUrl: normalizedServerUrl,
|
|
716
|
+
authToken: input.authToken ?? null,
|
|
717
|
+
createdAt: existing?.createdAt ?? timestamp,
|
|
718
|
+
updatedAt: timestamp,
|
|
719
|
+
lastUsedAt: existing?.lastUsedAt ?? null
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function applyDeleteProfile(settings, profileId) {
|
|
723
|
+
const profiles = settings.profiles.filter((item) => item.id !== profileId);
|
|
724
|
+
const activeProfileId = settings.activeProfileId !== profileId ? settings.activeProfileId : profiles.length > 0 ? profiles[0]?.id ?? null : null;
|
|
725
|
+
return { ...settings, profiles, activeProfileId };
|
|
726
|
+
}
|
|
727
|
+
function applySetActiveProfile(settings, profileId) {
|
|
728
|
+
const activeProfileId = profileId && settings.profiles.some((item) => item.id === profileId) ? profileId : null;
|
|
729
|
+
const timestamp = nowIso();
|
|
730
|
+
const profiles = settings.profiles.map(
|
|
731
|
+
(item) => item.id === activeProfileId ? { ...item, lastUsedAt: timestamp, updatedAt: timestamp } : item
|
|
732
|
+
);
|
|
733
|
+
return {
|
|
734
|
+
...settings,
|
|
735
|
+
activeProfileId,
|
|
736
|
+
profiles
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
async function readShellSettings() {
|
|
740
|
+
try {
|
|
741
|
+
const raw = await readFile2(getSettingsPath(), "utf-8");
|
|
742
|
+
return normalize(JSON.parse(raw));
|
|
743
|
+
} catch {
|
|
744
|
+
return { ...DEFAULT_SETTINGS };
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function getDesktopShellModeState(settings) {
|
|
748
|
+
if (!settings.hasCompletedModeSelection || settings.desktopMode === null) {
|
|
749
|
+
return {
|
|
750
|
+
isFirstRun: true,
|
|
751
|
+
desktopMode: null
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
isFirstRun: false,
|
|
756
|
+
desktopMode: settings.desktopMode
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
async function writeShellSettings(settings) {
|
|
760
|
+
const path2 = getSettingsPath();
|
|
761
|
+
const temp = `${path2}.tmp`;
|
|
762
|
+
await writeFile2(temp, JSON.stringify(settings, null, 2), "utf-8");
|
|
763
|
+
await rename2(temp, path2);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/ipc.ts
|
|
767
|
+
function isDesktopLaunchMode(value) {
|
|
768
|
+
return value === "choose" || value === "local" || value === "remote";
|
|
769
|
+
}
|
|
770
|
+
function toShellState(settings, runtimeStatus) {
|
|
771
|
+
return {
|
|
772
|
+
host: "desktop-shell",
|
|
773
|
+
desktopModeState: getDesktopShellModeState(settings),
|
|
774
|
+
desktopMode: settings.desktopMode ?? void 0,
|
|
775
|
+
activeProfileId: settings.activeProfileId,
|
|
776
|
+
profiles: settings.profiles,
|
|
777
|
+
localRuntime: runtimeStatus ?? { source: "none", state: "stopped" }
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
async function emitShellState(mainWindow2, getRuntimeStatus) {
|
|
781
|
+
const state = toShellState(await readShellSettings(), getRuntimeStatus?.());
|
|
782
|
+
mainWindow2.webContents.send("shell:state", state);
|
|
783
|
+
return state;
|
|
784
|
+
}
|
|
785
|
+
function registerIpcHandlers(mainWindow2, tray2, options = {}) {
|
|
786
|
+
ipcMain.handle("window:minimize", () => mainWindow2.minimize());
|
|
787
|
+
ipcMain.handle("window:maximize", () => {
|
|
788
|
+
const isCurrentlyMaximized = mainWindow2.isMaximized();
|
|
789
|
+
if (isCurrentlyMaximized) {
|
|
790
|
+
mainWindow2.unmaximize();
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
mainWindow2.maximize();
|
|
794
|
+
return true;
|
|
795
|
+
});
|
|
796
|
+
ipcMain.handle("window:close", () => mainWindow2.close());
|
|
797
|
+
ipcMain.handle("window:isMaximized", () => mainWindow2.isMaximized());
|
|
798
|
+
ipcMain.handle("platform:get", () => process.platform);
|
|
799
|
+
ipcMain.handle("app:getSystemInfo", () => ({
|
|
800
|
+
platform: process.platform,
|
|
801
|
+
arch: process.arch,
|
|
802
|
+
electronVersion: process.versions.electron,
|
|
803
|
+
nodeVersion: process.versions.node,
|
|
804
|
+
appVersion: app5.getVersion()
|
|
805
|
+
}));
|
|
806
|
+
ipcMain.handle("app:checkForUpdates", async () => {
|
|
807
|
+
try {
|
|
808
|
+
return await triggerUpdateCheck(mainWindow2);
|
|
809
|
+
} catch (error) {
|
|
810
|
+
return { status: "error", error: error instanceof Error ? error.message : String(error) };
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
ipcMain.handle("tray:updateStatus", (_event, status) => updateTrayStatus(tray2, status));
|
|
814
|
+
ipcMain.handle("native:showExportDialog", () => showExportSettingsDialog(mainWindow2));
|
|
815
|
+
ipcMain.handle("native:showImportDialog", () => showImportSettingsDialog(mainWindow2));
|
|
816
|
+
ipcMain.handle("app:getServerPort", () => options.getServerPort?.());
|
|
817
|
+
ipcMain.handle("desktopRuntime:getStatus", async () => options.getRuntimeStatus?.() ?? { source: "none", state: "stopped" });
|
|
818
|
+
ipcMain.handle("desktopRuntime:startLocal", async () => options.startLocalRuntime?.() ?? { source: "none", state: "stopped" });
|
|
819
|
+
ipcMain.handle("desktopRuntime:stopLocal", async () => options.stopLocalRuntime?.() ?? { source: "none", state: "stopped" });
|
|
820
|
+
ipcMain.handle("desktopLaunchMode:getMode", async () => options.getDesktopLaunchMode?.() ?? "choose");
|
|
821
|
+
ipcMain.handle("desktopLaunchMode:getContext", async () => options.getDesktopLaunchContext?.() ?? null);
|
|
822
|
+
ipcMain.handle("desktopLaunchMode:setMode", async (_event, mode) => {
|
|
823
|
+
if (!isDesktopLaunchMode(mode)) {
|
|
824
|
+
throw new Error("Invalid desktop launch mode");
|
|
825
|
+
}
|
|
826
|
+
if (options.onDesktopLaunchModeChange) {
|
|
827
|
+
await options.onDesktopLaunchModeChange(mode);
|
|
828
|
+
return options.getDesktopLaunchMode?.() ?? mode;
|
|
829
|
+
}
|
|
830
|
+
if ((mode === "local" || mode === "remote") && options.onDesktopModeChange) {
|
|
831
|
+
await options.onDesktopModeChange(mode);
|
|
832
|
+
return options.getDesktopLaunchMode?.() ?? mode;
|
|
833
|
+
}
|
|
834
|
+
return options.getDesktopLaunchMode?.() ?? mode;
|
|
835
|
+
});
|
|
836
|
+
ipcMain.handle("shell:getState", () => readShellSettings().then((settings) => toShellState(settings, options.getRuntimeStatus?.())));
|
|
837
|
+
ipcMain.handle("shell:listProfiles", async () => (await readShellSettings()).profiles);
|
|
838
|
+
ipcMain.handle("shell:saveProfile", async (_event, profile) => {
|
|
839
|
+
const settings = await readShellSettings();
|
|
840
|
+
const nextProfile = buildSavedProfile(settings, profile);
|
|
841
|
+
const existing = settings.profiles.find((item) => item.id === nextProfile.id);
|
|
842
|
+
settings.profiles = existing ? settings.profiles.map((item) => item.id === existing.id ? nextProfile : item) : [...settings.profiles, nextProfile];
|
|
843
|
+
await writeShellSettings(settings);
|
|
844
|
+
await emitShellState(mainWindow2, options.getRuntimeStatus);
|
|
845
|
+
return nextProfile;
|
|
846
|
+
});
|
|
847
|
+
ipcMain.handle("shell:deleteProfile", async (_event, profileId) => {
|
|
848
|
+
const settings = applyDeleteProfile(await readShellSettings(), profileId);
|
|
849
|
+
await writeShellSettings(settings);
|
|
850
|
+
await emitShellState(mainWindow2, options.getRuntimeStatus);
|
|
851
|
+
});
|
|
852
|
+
ipcMain.handle("shell:setActiveProfile", async (_event, profileId) => {
|
|
853
|
+
const settings = applySetActiveProfile(await readShellSettings(), profileId);
|
|
854
|
+
await writeShellSettings(settings);
|
|
855
|
+
return emitShellState(mainWindow2, options.getRuntimeStatus);
|
|
856
|
+
});
|
|
857
|
+
ipcMain.handle("shell:getDesktopModeState", async () => {
|
|
858
|
+
const settings = await readShellSettings();
|
|
859
|
+
return getDesktopShellModeState(settings);
|
|
860
|
+
});
|
|
861
|
+
ipcMain.handle("shell:setDesktopMode", async (_event, mode) => {
|
|
862
|
+
const settings = await readShellSettings();
|
|
863
|
+
settings.desktopMode = mode;
|
|
864
|
+
settings.hasCompletedModeSelection = true;
|
|
865
|
+
await writeShellSettings(settings);
|
|
866
|
+
await options.onDesktopModeChange?.(mode);
|
|
867
|
+
return emitShellState(mainWindow2, options.getRuntimeStatus);
|
|
868
|
+
});
|
|
869
|
+
ipcMain.handle("shell:resetDesktopMode", async () => {
|
|
870
|
+
const settings = await readShellSettings();
|
|
871
|
+
settings.desktopMode = null;
|
|
872
|
+
settings.hasCompletedModeSelection = false;
|
|
873
|
+
await writeShellSettings(settings);
|
|
874
|
+
await options.stopLocalRuntime?.().catch(() => void 0);
|
|
875
|
+
return emitShellState(mainWindow2, options.getRuntimeStatus);
|
|
876
|
+
});
|
|
877
|
+
ipcMain.handle("shell:startQrScan", async () => {
|
|
878
|
+
throw new Error("QR scanning is not available in desktop shell");
|
|
879
|
+
});
|
|
880
|
+
ipcMain.handle("shell:openConnectionManager", () => {
|
|
881
|
+
mainWindow2.webContents.send("shell:open-connection-manager");
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/menu.ts
|
|
886
|
+
import {
|
|
887
|
+
Menu as Menu2,
|
|
888
|
+
shell
|
|
889
|
+
} from "electron";
|
|
890
|
+
function runMenuAction(label, action) {
|
|
891
|
+
if (!action) return;
|
|
892
|
+
void Promise.resolve(action()).catch((error) => {
|
|
893
|
+
console.error(`[desktop/menu] ${label} failed`, error);
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
function buildConnectionSubmenu(options) {
|
|
897
|
+
return {
|
|
898
|
+
label: "Connection",
|
|
899
|
+
submenu: [
|
|
900
|
+
{
|
|
901
|
+
label: "Use Local Server",
|
|
902
|
+
click: () => runMenuAction("onStartLocalRuntime", options.onStartLocalRuntime)
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
label: "Shut Down Local Server",
|
|
906
|
+
click: () => runMenuAction("onStopLocalRuntime", options.onStopLocalRuntime)
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
type: "separator"
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
label: "Connect to Remote Server\u2026",
|
|
913
|
+
click: () => runMenuAction("onConnectRemoteServer", options.onConnectRemoteServer ?? options.onChangeLaunchMode)
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
label: "Change Launch Mode\u2026",
|
|
917
|
+
click: () => runMenuAction("onChangeLaunchMode", options.onChangeLaunchMode)
|
|
918
|
+
}
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function buildAppSubmenu(options) {
|
|
923
|
+
return {
|
|
924
|
+
label: options.appName,
|
|
925
|
+
submenu: [
|
|
926
|
+
{
|
|
927
|
+
label: `About ${options.appName}`
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
label: "Check for Updates\u2026",
|
|
931
|
+
click: () => runMenuAction("onCheckForUpdates", options.onCheckForUpdates)
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
type: "separator"
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
label: "Preferences",
|
|
938
|
+
accelerator: "CmdOrCtrl+,"
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
type: "separator"
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
label: `Hide ${options.appName}`,
|
|
945
|
+
role: "hide"
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
label: "Hide Others",
|
|
949
|
+
role: "hideOthers"
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
label: "Show All",
|
|
953
|
+
role: "unhide"
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
type: "separator"
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
label: `Quit ${options.appName}`,
|
|
960
|
+
accelerator: "CmdOrCtrl+Q",
|
|
961
|
+
role: "quit"
|
|
962
|
+
}
|
|
963
|
+
]
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function buildEditSubmenu() {
|
|
967
|
+
return {
|
|
968
|
+
label: "Edit",
|
|
969
|
+
submenu: [
|
|
970
|
+
{
|
|
971
|
+
label: "Undo",
|
|
972
|
+
accelerator: "CmdOrCtrl+Z",
|
|
973
|
+
role: "undo"
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
label: "Redo",
|
|
977
|
+
accelerator: "Shift+CmdOrCtrl+Z",
|
|
978
|
+
role: "redo"
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
type: "separator"
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
label: "Cut",
|
|
985
|
+
accelerator: "CmdOrCtrl+X",
|
|
986
|
+
role: "cut"
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
label: "Copy",
|
|
990
|
+
accelerator: "CmdOrCtrl+C",
|
|
991
|
+
role: "copy"
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
label: "Paste",
|
|
995
|
+
accelerator: "CmdOrCtrl+V",
|
|
996
|
+
role: "paste"
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
label: "Select All",
|
|
1000
|
+
accelerator: "CmdOrCtrl+A",
|
|
1001
|
+
role: "selectAll"
|
|
1002
|
+
}
|
|
1003
|
+
]
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function buildViewSubmenu(options) {
|
|
1007
|
+
const { webContents } = options.mainWindow;
|
|
1008
|
+
return {
|
|
1009
|
+
label: "View",
|
|
1010
|
+
submenu: [
|
|
1011
|
+
{
|
|
1012
|
+
label: "Reload",
|
|
1013
|
+
accelerator: "CmdOrCtrl+R",
|
|
1014
|
+
click: () => webContents.reload()
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
label: "Force Reload",
|
|
1018
|
+
accelerator: "CmdOrCtrl+Shift+R",
|
|
1019
|
+
click: () => webContents.reloadIgnoringCache()
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
type: "separator"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
label: "Toggle Dev Tools",
|
|
1026
|
+
accelerator: "Alt+CmdOrCtrl+I",
|
|
1027
|
+
click: () => webContents.toggleDevTools()
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
type: "separator"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
label: "Zoom In",
|
|
1034
|
+
accelerator: "CmdOrCtrl+Plus",
|
|
1035
|
+
click: () => {
|
|
1036
|
+
const level = webContents.getZoomLevel();
|
|
1037
|
+
webContents.setZoomLevel(level + 0.5);
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
label: "Zoom Out",
|
|
1042
|
+
accelerator: "CmdOrCtrl+-",
|
|
1043
|
+
click: () => {
|
|
1044
|
+
const level = webContents.getZoomLevel();
|
|
1045
|
+
webContents.setZoomLevel(level - 0.5);
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
label: "Reset Zoom",
|
|
1050
|
+
accelerator: "CmdOrCtrl+0",
|
|
1051
|
+
click: () => {
|
|
1052
|
+
webContents.setZoomLevel(0);
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
type: "separator"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
label: "Toggle Full Screen",
|
|
1060
|
+
accelerator: "F11",
|
|
1061
|
+
role: "togglefullscreen"
|
|
1062
|
+
}
|
|
1063
|
+
]
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
function buildWindowSubmenu(isMac) {
|
|
1067
|
+
const windowItems = [
|
|
1068
|
+
{
|
|
1069
|
+
label: "Minimize",
|
|
1070
|
+
accelerator: "CmdOrCtrl+M",
|
|
1071
|
+
role: "minimize"
|
|
1072
|
+
}
|
|
1073
|
+
];
|
|
1074
|
+
if (isMac) {
|
|
1075
|
+
windowItems.push({
|
|
1076
|
+
label: "Zoom",
|
|
1077
|
+
role: "zoom"
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
windowItems.push(
|
|
1081
|
+
{
|
|
1082
|
+
type: "separator"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
label: "Close",
|
|
1086
|
+
accelerator: "CmdOrCtrl+W",
|
|
1087
|
+
role: "close"
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
if (isMac) {
|
|
1091
|
+
windowItems.push(
|
|
1092
|
+
{
|
|
1093
|
+
type: "separator"
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
label: "Bring All to Front",
|
|
1097
|
+
role: "front"
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
return {
|
|
1102
|
+
label: "Window",
|
|
1103
|
+
submenu: windowItems
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function buildHelpSubmenu(options) {
|
|
1107
|
+
return {
|
|
1108
|
+
label: "Help",
|
|
1109
|
+
submenu: [
|
|
1110
|
+
{
|
|
1111
|
+
label: "Check for Updates\u2026",
|
|
1112
|
+
click: () => runMenuAction("onCheckForUpdates", options.onCheckForUpdates)
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
label: "Fusion Documentation",
|
|
1116
|
+
click: () => {
|
|
1117
|
+
void shell.openExternal("https://github.com/Runfusion/Fusion#readme");
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
]
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function buildMenuTemplate(options) {
|
|
1124
|
+
const isMac = process.platform === "darwin";
|
|
1125
|
+
const template = [
|
|
1126
|
+
buildEditSubmenu(),
|
|
1127
|
+
buildViewSubmenu(options),
|
|
1128
|
+
buildConnectionSubmenu(options),
|
|
1129
|
+
buildWindowSubmenu(isMac),
|
|
1130
|
+
buildHelpSubmenu(options)
|
|
1131
|
+
];
|
|
1132
|
+
if (isMac) {
|
|
1133
|
+
template.unshift(buildAppSubmenu(options));
|
|
1134
|
+
}
|
|
1135
|
+
return template;
|
|
1136
|
+
}
|
|
1137
|
+
function buildAppMenu(options) {
|
|
1138
|
+
const menu = Menu2.buildFromTemplate(buildMenuTemplate(options));
|
|
1139
|
+
Menu2.setApplicationMenu(menu);
|
|
1140
|
+
return menu;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// src/renderer.ts
|
|
1144
|
+
import { pathToFileURL } from "node:url";
|
|
1145
|
+
import { join as join3 } from "node:path";
|
|
1146
|
+
var DEFAULT_DEV_DASHBOARD_URL = "http://localhost:5173";
|
|
1147
|
+
function isDevelopmentMode() {
|
|
1148
|
+
return process.env.NODE_ENV === "development" || process.argv.includes("--dev");
|
|
1149
|
+
}
|
|
1150
|
+
function getRendererUrl() {
|
|
1151
|
+
if (isDevelopmentMode()) {
|
|
1152
|
+
return process.env.FUSION_DASHBOARD_URL ?? DEFAULT_DEV_DASHBOARD_URL;
|
|
1153
|
+
}
|
|
1154
|
+
const rendererIndexPath = join3(import.meta.dirname, "client", "index.html");
|
|
1155
|
+
return pathToFileURL(rendererIndexPath).toString();
|
|
1156
|
+
}
|
|
1157
|
+
function getRendererFilePath() {
|
|
1158
|
+
if (isDevelopmentMode()) {
|
|
1159
|
+
return "";
|
|
1160
|
+
}
|
|
1161
|
+
return join3(import.meta.dirname, "client", "index.html");
|
|
1162
|
+
}
|
|
1163
|
+
function isUrlRenderer() {
|
|
1164
|
+
if (isDevelopmentMode()) {
|
|
1165
|
+
const override = process.env.FUSION_USE_FILE_RENDERER;
|
|
1166
|
+
return override !== "true";
|
|
1167
|
+
}
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
var IS_DEVELOPMENT = isDevelopmentMode();
|
|
1171
|
+
|
|
1172
|
+
// src/local-runtime.ts
|
|
1173
|
+
import { once } from "node:events";
|
|
1174
|
+
|
|
1175
|
+
// src/engine-runtime.ts
|
|
1176
|
+
import { basename } from "node:path";
|
|
1177
|
+
async function ensureDesktopRuntimeProject(centralCore, rootDir) {
|
|
1178
|
+
const existing = await centralCore.getProjectByPath(rootDir);
|
|
1179
|
+
if (existing) {
|
|
1180
|
+
return existing;
|
|
1181
|
+
}
|
|
1182
|
+
const projects = await centralCore.listProjects();
|
|
1183
|
+
if (projects.length > 0) {
|
|
1184
|
+
return projects[0];
|
|
1185
|
+
}
|
|
1186
|
+
const registered = await centralCore.registerProject({
|
|
1187
|
+
path: rootDir,
|
|
1188
|
+
name: basename(rootDir) || "Fusion Desktop",
|
|
1189
|
+
isolationMode: "in-process"
|
|
1190
|
+
});
|
|
1191
|
+
return centralCore.updateProject(registered.id, { status: "active" });
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// src/local-runtime.ts
|
|
1195
|
+
async function createStoreDefault(rootDir) {
|
|
1196
|
+
const { TaskStore } = await import("@fusion/core");
|
|
1197
|
+
return new TaskStore(rootDir);
|
|
1198
|
+
}
|
|
1199
|
+
async function createDashboardServerDefault(store, rootDir) {
|
|
1200
|
+
const { CentralCore } = await import("@fusion/core");
|
|
1201
|
+
const { createServer } = await import("@fusion/dashboard");
|
|
1202
|
+
const { ProjectEngineManager } = await import("@fusion/engine");
|
|
1203
|
+
const centralCore = new CentralCore();
|
|
1204
|
+
const engineManager = new ProjectEngineManager(centralCore);
|
|
1205
|
+
const cleanup = async () => {
|
|
1206
|
+
await engineManager.stopAll();
|
|
1207
|
+
await centralCore.close?.();
|
|
1208
|
+
};
|
|
1209
|
+
try {
|
|
1210
|
+
await centralCore.init();
|
|
1211
|
+
const rootProject = await ensureDesktopRuntimeProject(centralCore, rootDir);
|
|
1212
|
+
await engineManager.startAll();
|
|
1213
|
+
engineManager.startReconciliation();
|
|
1214
|
+
const primaryEngine = await engineManager.ensureEngine(rootProject.id);
|
|
1215
|
+
const app7 = createServer(store, {
|
|
1216
|
+
engine: primaryEngine,
|
|
1217
|
+
engineManager,
|
|
1218
|
+
centralCore,
|
|
1219
|
+
onProjectFirstAccessed: (projectId) => engineManager.onProjectAccessed(projectId)
|
|
1220
|
+
});
|
|
1221
|
+
return {
|
|
1222
|
+
server: app7.listen(0),
|
|
1223
|
+
cleanup
|
|
1224
|
+
};
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
await cleanup();
|
|
1227
|
+
throw error;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function parsePort(raw) {
|
|
1231
|
+
if (!raw) {
|
|
1232
|
+
return void 0;
|
|
1233
|
+
}
|
|
1234
|
+
const value = Number(raw);
|
|
1235
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
1236
|
+
return void 0;
|
|
1237
|
+
}
|
|
1238
|
+
return value;
|
|
1239
|
+
}
|
|
1240
|
+
function getAddressPort(server) {
|
|
1241
|
+
const address = server.address();
|
|
1242
|
+
const port = address?.port;
|
|
1243
|
+
if (!port) {
|
|
1244
|
+
throw new Error("Failed to resolve local server port");
|
|
1245
|
+
}
|
|
1246
|
+
return port;
|
|
1247
|
+
}
|
|
1248
|
+
var LocalRuntimeManager = class {
|
|
1249
|
+
constructor(options) {
|
|
1250
|
+
this.options = options;
|
|
1251
|
+
this.getExternalPort = options.getExternalPort ?? (() => parsePort(process.env.FUSION_SERVER_PORT));
|
|
1252
|
+
this.createStore = options.createStore ?? createStoreDefault;
|
|
1253
|
+
this.createDashboardServer = options.createDashboardServer ?? createDashboardServerDefault;
|
|
1254
|
+
}
|
|
1255
|
+
runtime = null;
|
|
1256
|
+
startupPromise = null;
|
|
1257
|
+
stopPromise = null;
|
|
1258
|
+
status = { source: "none", state: "stopped" };
|
|
1259
|
+
getExternalPort;
|
|
1260
|
+
createStore;
|
|
1261
|
+
createDashboardServer;
|
|
1262
|
+
getStatus() {
|
|
1263
|
+
if (this.runtime) {
|
|
1264
|
+
return {
|
|
1265
|
+
source: "embedded-local",
|
|
1266
|
+
state: "running",
|
|
1267
|
+
port: this.runtime.port,
|
|
1268
|
+
baseUrl: this.runtime.baseUrl
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
const externalPort = this.getExternalPort();
|
|
1272
|
+
if (externalPort) {
|
|
1273
|
+
this.status = {
|
|
1274
|
+
source: "external-cli",
|
|
1275
|
+
state: "running",
|
|
1276
|
+
port: externalPort,
|
|
1277
|
+
baseUrl: `http://127.0.0.1:${externalPort}`
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
return this.status;
|
|
1281
|
+
}
|
|
1282
|
+
getServerPort() {
|
|
1283
|
+
return this.getStatus().port;
|
|
1284
|
+
}
|
|
1285
|
+
async startLocal() {
|
|
1286
|
+
const externalPort = this.getExternalPort();
|
|
1287
|
+
if (externalPort) {
|
|
1288
|
+
this.status = {
|
|
1289
|
+
source: "external-cli",
|
|
1290
|
+
state: "running",
|
|
1291
|
+
port: externalPort,
|
|
1292
|
+
baseUrl: `http://127.0.0.1:${externalPort}`
|
|
1293
|
+
};
|
|
1294
|
+
return this.status;
|
|
1295
|
+
}
|
|
1296
|
+
if (this.runtime) {
|
|
1297
|
+
return this.getStatus();
|
|
1298
|
+
}
|
|
1299
|
+
if (this.startupPromise) {
|
|
1300
|
+
return this.startupPromise;
|
|
1301
|
+
}
|
|
1302
|
+
this.status = { source: "embedded-local", state: "starting" };
|
|
1303
|
+
this.startupPromise = this.startEmbedded();
|
|
1304
|
+
try {
|
|
1305
|
+
return await this.startupPromise;
|
|
1306
|
+
} finally {
|
|
1307
|
+
this.startupPromise = null;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
async startEmbedded() {
|
|
1311
|
+
let store = null;
|
|
1312
|
+
let server = null;
|
|
1313
|
+
let cleanup;
|
|
1314
|
+
try {
|
|
1315
|
+
store = await this.createStore(this.options.rootDir);
|
|
1316
|
+
await store.init();
|
|
1317
|
+
await store.watch();
|
|
1318
|
+
const dashboardServer = await this.createDashboardServer(store, this.options.rootDir);
|
|
1319
|
+
cleanup = "server" in dashboardServer ? dashboardServer.cleanup : void 0;
|
|
1320
|
+
server = "server" in dashboardServer ? dashboardServer.server : dashboardServer;
|
|
1321
|
+
await Promise.race([
|
|
1322
|
+
once(server, "listening"),
|
|
1323
|
+
once(server, "error").then(([error]) => {
|
|
1324
|
+
throw error;
|
|
1325
|
+
})
|
|
1326
|
+
]);
|
|
1327
|
+
const port = getAddressPort(server);
|
|
1328
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
1329
|
+
this.runtime = { store, server, port, baseUrl, cleanup };
|
|
1330
|
+
this.status = { source: "embedded-local", state: "running", port, baseUrl };
|
|
1331
|
+
return this.status;
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
if (server) {
|
|
1334
|
+
await new Promise((resolve2) => {
|
|
1335
|
+
server.close(() => resolve2());
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
await cleanup?.();
|
|
1339
|
+
if (store) {
|
|
1340
|
+
store.close();
|
|
1341
|
+
}
|
|
1342
|
+
this.runtime = null;
|
|
1343
|
+
this.status = {
|
|
1344
|
+
source: "embedded-local",
|
|
1345
|
+
state: "error",
|
|
1346
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1347
|
+
};
|
|
1348
|
+
throw error;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
async stopLocal() {
|
|
1352
|
+
if (this.stopPromise) {
|
|
1353
|
+
return this.stopPromise;
|
|
1354
|
+
}
|
|
1355
|
+
this.stopPromise = this.stopInternal();
|
|
1356
|
+
try {
|
|
1357
|
+
return await this.stopPromise;
|
|
1358
|
+
} finally {
|
|
1359
|
+
this.stopPromise = null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
async stopInternal() {
|
|
1363
|
+
if (this.runtime) {
|
|
1364
|
+
const runtime = this.runtime;
|
|
1365
|
+
this.runtime = null;
|
|
1366
|
+
await new Promise((resolve2) => runtime.server.close(() => resolve2()));
|
|
1367
|
+
await runtime.cleanup?.();
|
|
1368
|
+
runtime.store.close();
|
|
1369
|
+
this.status = { source: "none", state: "stopped" };
|
|
1370
|
+
return this.status;
|
|
1371
|
+
}
|
|
1372
|
+
if (this.getStatus().source === "external-cli") {
|
|
1373
|
+
return this.status;
|
|
1374
|
+
}
|
|
1375
|
+
this.status = { source: "none", state: "stopped" };
|
|
1376
|
+
return this.status;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/main.ts
|
|
1381
|
+
function enableSourceMaps() {
|
|
1382
|
+
const processWithSourceMaps = process;
|
|
1383
|
+
processWithSourceMaps.setSourceMapsEnabled?.(true);
|
|
1384
|
+
}
|
|
1385
|
+
enableSourceMaps();
|
|
1386
|
+
var mainWindow = null;
|
|
1387
|
+
var tray = null;
|
|
1388
|
+
var localRuntimeManager = null;
|
|
1389
|
+
var currentDesktopLaunchMode = "choose";
|
|
1390
|
+
var currentRemoteLaunch = null;
|
|
1391
|
+
var localRuntimeStartupAttempted = false;
|
|
1392
|
+
var stopUpdateCheckInterval = null;
|
|
1393
|
+
function getAppWithQuitFlag() {
|
|
1394
|
+
return app6;
|
|
1395
|
+
}
|
|
1396
|
+
async function startLocalRuntimeOnce() {
|
|
1397
|
+
if (!localRuntimeManager || localRuntimeStartupAttempted) {
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
const status = localRuntimeManager.getStatus();
|
|
1401
|
+
if (status.source === "embedded-local" && status.state === "running") {
|
|
1402
|
+
localRuntimeStartupAttempted = true;
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
localRuntimeStartupAttempted = true;
|
|
1406
|
+
await localRuntimeManager.startLocal();
|
|
1407
|
+
}
|
|
1408
|
+
function getCurrentDesktopLaunchMode() {
|
|
1409
|
+
return currentDesktopLaunchMode;
|
|
1410
|
+
}
|
|
1411
|
+
async function resetLaunchModeAndReload(window) {
|
|
1412
|
+
try {
|
|
1413
|
+
const settings = await readShellSettings();
|
|
1414
|
+
settings.desktopMode = null;
|
|
1415
|
+
settings.hasCompletedModeSelection = false;
|
|
1416
|
+
await writeShellSettings(settings);
|
|
1417
|
+
await saveDesktopLaunchMode("choose");
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
console.error("[desktop/main] Failed to reset shell settings", error);
|
|
1420
|
+
}
|
|
1421
|
+
if (localRuntimeManager) {
|
|
1422
|
+
try {
|
|
1423
|
+
await localRuntimeManager.stopLocal();
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
console.error("[desktop/main] Failed to stop local runtime during reset", error);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
currentDesktopLaunchMode = "choose";
|
|
1429
|
+
currentRemoteLaunch = null;
|
|
1430
|
+
localRuntimeStartupAttempted = false;
|
|
1431
|
+
try {
|
|
1432
|
+
if (isUrlRenderer()) {
|
|
1433
|
+
await window.loadURL(getRendererUrl());
|
|
1434
|
+
} else {
|
|
1435
|
+
await window.loadFile(getRendererFilePath());
|
|
1436
|
+
}
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
console.error("[desktop/main] reload failed", error);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
function createMainWindow(state, launchTargetUrl) {
|
|
1442
|
+
const hasValidPosition = typeof state?.x === "number" && typeof state?.y === "number";
|
|
1443
|
+
const window = new BrowserWindow4({
|
|
1444
|
+
width: state?.width ?? DEFAULT_WINDOW_STATE.width,
|
|
1445
|
+
height: state?.height ?? DEFAULT_WINDOW_STATE.height,
|
|
1446
|
+
...hasValidPosition ? { x: state.x, y: state.y } : {},
|
|
1447
|
+
title: "Fusion",
|
|
1448
|
+
webPreferences: {
|
|
1449
|
+
preload: join4(import.meta.dirname, "preload.js"),
|
|
1450
|
+
contextIsolation: true,
|
|
1451
|
+
nodeIntegration: false
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
if (launchTargetUrl) {
|
|
1455
|
+
void window.loadURL(launchTargetUrl);
|
|
1456
|
+
} else if (isUrlRenderer()) {
|
|
1457
|
+
void window.loadURL(getRendererUrl());
|
|
1458
|
+
} else {
|
|
1459
|
+
void window.loadFile(getRendererFilePath());
|
|
1460
|
+
}
|
|
1461
|
+
const showFallbackTimer = setTimeout(() => {
|
|
1462
|
+
window.show();
|
|
1463
|
+
window.focus();
|
|
1464
|
+
}, 2e3);
|
|
1465
|
+
window.once("ready-to-show", () => {
|
|
1466
|
+
clearTimeout(showFallbackTimer);
|
|
1467
|
+
window.show();
|
|
1468
|
+
window.focus();
|
|
1469
|
+
});
|
|
1470
|
+
window.on("close", (event) => {
|
|
1471
|
+
saveWindowState(window);
|
|
1472
|
+
if (getAppWithQuitFlag().isQuitting) {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
event.preventDefault();
|
|
1476
|
+
window.hide();
|
|
1477
|
+
});
|
|
1478
|
+
window.on("closed", () => {
|
|
1479
|
+
mainWindow = null;
|
|
1480
|
+
});
|
|
1481
|
+
mainWindow = window;
|
|
1482
|
+
return window;
|
|
1483
|
+
}
|
|
1484
|
+
function resolveLocalRuntimeRoot() {
|
|
1485
|
+
const override = process.env.FUSION_HOME?.trim();
|
|
1486
|
+
if (override) {
|
|
1487
|
+
return resolve(override);
|
|
1488
|
+
}
|
|
1489
|
+
return app6.getPath("home");
|
|
1490
|
+
}
|
|
1491
|
+
async function initializeApp() {
|
|
1492
|
+
const state = await loadWindowState();
|
|
1493
|
+
const rememberedLaunchMode = await loadDesktopLaunchMode();
|
|
1494
|
+
localRuntimeManager = new LocalRuntimeManager({ rootDir: resolveLocalRuntimeRoot() });
|
|
1495
|
+
currentDesktopLaunchMode = rememberedLaunchMode;
|
|
1496
|
+
currentRemoteLaunch = null;
|
|
1497
|
+
localRuntimeStartupAttempted = false;
|
|
1498
|
+
if (rememberedLaunchMode === "remote") {
|
|
1499
|
+
const shellSettings = await readShellSettings();
|
|
1500
|
+
const normalizedRemoteLaunch = normalizeDesktopRemoteLaunch(shellSettings);
|
|
1501
|
+
if (normalizedRemoteLaunch) {
|
|
1502
|
+
currentRemoteLaunch = normalizedRemoteLaunch;
|
|
1503
|
+
} else {
|
|
1504
|
+
currentDesktopLaunchMode = "choose";
|
|
1505
|
+
await saveDesktopLaunchMode("choose");
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
if (rememberedLaunchMode === "local") {
|
|
1509
|
+
try {
|
|
1510
|
+
await startLocalRuntimeOnce();
|
|
1511
|
+
} catch (error) {
|
|
1512
|
+
await localRuntimeManager.stopLocal();
|
|
1513
|
+
currentDesktopLaunchMode = "choose";
|
|
1514
|
+
localRuntimeStartupAttempted = false;
|
|
1515
|
+
await saveDesktopLaunchMode("choose");
|
|
1516
|
+
console.error("[desktop/main] Failed to restore local mode; falling back to chooser", error);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (currentDesktopLaunchMode === "choose" && process.env.FUSION_DESKTOP_MODE === "local") {
|
|
1520
|
+
await startLocalRuntimeOnce();
|
|
1521
|
+
currentDesktopLaunchMode = "local";
|
|
1522
|
+
}
|
|
1523
|
+
const windowState = state ? clampWindowStateToVisibleDisplay(
|
|
1524
|
+
state,
|
|
1525
|
+
typeof screen?.getAllDisplays === "function" ? screen.getAllDisplays().map((display) => ({ workArea: display.workArea })) : []
|
|
1526
|
+
) : void 0;
|
|
1527
|
+
const createdWindow = createMainWindow(
|
|
1528
|
+
windowState,
|
|
1529
|
+
currentDesktopLaunchMode === "remote" && currentRemoteLaunch ? buildRemoteShellHandoffUrl(currentRemoteLaunch) : void 0
|
|
1530
|
+
);
|
|
1531
|
+
buildAppMenu({
|
|
1532
|
+
mainWindow: createdWindow,
|
|
1533
|
+
appName: "Fusion",
|
|
1534
|
+
onChangeLaunchMode: async () => {
|
|
1535
|
+
await resetLaunchModeAndReload(createdWindow);
|
|
1536
|
+
},
|
|
1537
|
+
/*
|
|
1538
|
+
* FNXC:DesktopRuntimeMode 2026-06-21-02:04:
|
|
1539
|
+
* Menu-driven local/remote switching must only persist local mode after the embedded runtime starts successfully; shutdown stops the local server without rewriting the launch-mode preference.
|
|
1540
|
+
*/
|
|
1541
|
+
onStartLocalRuntime: async () => {
|
|
1542
|
+
if (!localRuntimeManager) return;
|
|
1543
|
+
localRuntimeStartupAttempted = false;
|
|
1544
|
+
await startLocalRuntimeOnce();
|
|
1545
|
+
currentRemoteLaunch = null;
|
|
1546
|
+
currentDesktopLaunchMode = "local";
|
|
1547
|
+
await saveDesktopLaunchMode("local");
|
|
1548
|
+
createdWindow.webContents.reload();
|
|
1549
|
+
},
|
|
1550
|
+
onStopLocalRuntime: async () => {
|
|
1551
|
+
await localRuntimeManager?.stopLocal();
|
|
1552
|
+
},
|
|
1553
|
+
onConnectRemoteServer: async () => {
|
|
1554
|
+
await resetLaunchModeAndReload(createdWindow);
|
|
1555
|
+
},
|
|
1556
|
+
onCheckForUpdates: async () => {
|
|
1557
|
+
await triggerUpdateCheck(createdWindow);
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
tray = new Tray2(nativeImage2.createEmpty());
|
|
1561
|
+
setupTray(createdWindow, tray);
|
|
1562
|
+
registerIpcHandlers(createdWindow, tray, {
|
|
1563
|
+
onDesktopModeChange: async (mode) => {
|
|
1564
|
+
if (!localRuntimeManager) {
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
currentDesktopLaunchMode = mode;
|
|
1568
|
+
if (mode === "local") {
|
|
1569
|
+
currentRemoteLaunch = null;
|
|
1570
|
+
localRuntimeStartupAttempted = false;
|
|
1571
|
+
await startLocalRuntimeOnce();
|
|
1572
|
+
} else {
|
|
1573
|
+
localRuntimeStartupAttempted = false;
|
|
1574
|
+
await localRuntimeManager.stopLocal();
|
|
1575
|
+
const shellSettings = await readShellSettings();
|
|
1576
|
+
currentRemoteLaunch = normalizeDesktopRemoteLaunch({ ...shellSettings, desktopMode: "remote" });
|
|
1577
|
+
}
|
|
1578
|
+
await saveDesktopLaunchMode(mode);
|
|
1579
|
+
},
|
|
1580
|
+
onDesktopLaunchModeChange: async (mode) => {
|
|
1581
|
+
if (!localRuntimeManager) {
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
currentDesktopLaunchMode = mode;
|
|
1585
|
+
localRuntimeStartupAttempted = false;
|
|
1586
|
+
if (mode === "local") {
|
|
1587
|
+
currentRemoteLaunch = null;
|
|
1588
|
+
await startLocalRuntimeOnce();
|
|
1589
|
+
} else {
|
|
1590
|
+
await localRuntimeManager.stopLocal();
|
|
1591
|
+
const shellSettings = await readShellSettings();
|
|
1592
|
+
currentRemoteLaunch = normalizeDesktopRemoteLaunch({ ...shellSettings, desktopMode: "remote" });
|
|
1593
|
+
}
|
|
1594
|
+
await saveDesktopLaunchMode(mode);
|
|
1595
|
+
},
|
|
1596
|
+
getRuntimeStatus: () => localRuntimeManager?.getStatus() ?? { source: "none", state: "stopped" },
|
|
1597
|
+
startLocalRuntime: () => localRuntimeManager?.startLocal() ?? Promise.resolve({ source: "none", state: "stopped" }),
|
|
1598
|
+
stopLocalRuntime: () => localRuntimeManager?.stopLocal() ?? Promise.resolve({ source: "none", state: "stopped" }),
|
|
1599
|
+
getServerPort: () => localRuntimeManager?.getServerPort(),
|
|
1600
|
+
getDesktopLaunchMode: () => currentDesktopLaunchMode,
|
|
1601
|
+
getDesktopLaunchContext: () => currentRemoteLaunch
|
|
1602
|
+
});
|
|
1603
|
+
registerDeepLinkProtocol();
|
|
1604
|
+
setupDeepLinkHandler(createdWindow);
|
|
1605
|
+
setupAutoUpdater(createdWindow);
|
|
1606
|
+
stopUpdateCheckInterval = startUpdateCheckInterval(createdWindow);
|
|
1607
|
+
if (windowState?.isMaximized === true) {
|
|
1608
|
+
createdWindow.maximize();
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
function run() {
|
|
1612
|
+
const appWithQuitFlag = getAppWithQuitFlag();
|
|
1613
|
+
appWithQuitFlag.isQuitting = false;
|
|
1614
|
+
void app6.whenReady().then(() => initializeApp());
|
|
1615
|
+
app6.on("window-all-closed", () => {
|
|
1616
|
+
if (process.platform !== "darwin") {
|
|
1617
|
+
app6.quit();
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
app6.on("before-quit", () => {
|
|
1621
|
+
appWithQuitFlag.isQuitting = true;
|
|
1622
|
+
if (stopUpdateCheckInterval) {
|
|
1623
|
+
stopUpdateCheckInterval();
|
|
1624
|
+
stopUpdateCheckInterval = null;
|
|
1625
|
+
}
|
|
1626
|
+
if (tray) {
|
|
1627
|
+
tray.destroy();
|
|
1628
|
+
tray = null;
|
|
1629
|
+
}
|
|
1630
|
+
if (localRuntimeManager) {
|
|
1631
|
+
void localRuntimeManager.stopLocal();
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
app6.on("activate", () => {
|
|
1635
|
+
if (mainWindow === null) {
|
|
1636
|
+
const window = createMainWindow();
|
|
1637
|
+
window.show();
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
if (!mainWindow.isVisible()) {
|
|
1641
|
+
mainWindow.show();
|
|
1642
|
+
}
|
|
1643
|
+
mainWindow.focus();
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
var modulePath = fileURLToPath(import.meta.url);
|
|
1647
|
+
var isElectronMain = process.type === "browser";
|
|
1648
|
+
var isDirectInvocation = !!process.argv[1] && resolve(process.argv[1]) === modulePath;
|
|
1649
|
+
if (isElectronMain || isDirectInvocation) {
|
|
1650
|
+
run();
|
|
1651
|
+
}
|
|
1652
|
+
export {
|
|
1653
|
+
getRendererUrl as DASHBOARD_URL,
|
|
1654
|
+
IS_DEVELOPMENT,
|
|
1655
|
+
createMainWindow,
|
|
1656
|
+
getCurrentDesktopLaunchMode,
|
|
1657
|
+
initializeApp,
|
|
1658
|
+
resolveLocalRuntimeRoot,
|
|
1659
|
+
run
|
|
1660
|
+
};
|
|
1661
|
+
//# sourceMappingURL=main.js.map
|