@shawnstack/quickforge 1.7.3 → 1.7.4
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/README.md +1 -1
- package/bin/quickforge.mjs +0 -21
- package/dist/assets/AgentProfilesPage-B84w-r9S.js +1 -0
- package/dist/assets/{ChatPanelHost-wfGCaCzD.js → ChatPanelHost-CyO8fEoj.js} +91 -91
- package/dist/assets/{PluginsPage-NAGroBm3.js → PluginsPage-D2862wul.js} +1 -1
- package/dist/assets/{ScheduledTasksPage-Bp0MNfQD.js → ScheduledTasksPage-GqbiJDPX.js} +2 -2
- package/dist/assets/SettingsWorkspacePage-B53_eDf2.js +1628 -0
- package/dist/assets/SharedConversationPage-Cw2QFnbC.js +1 -0
- package/dist/assets/TerminalDock-Dgg-_ZmU.js +2 -0
- package/dist/assets/WorkspaceInspector-3uLiu6Bv.js +13 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-Dl_Fvlqg.js → abnfDiagram-VRR7QNED-Dy7veffk.js} +1 -1
- package/dist/assets/{arc-Bhg4c0ht.js → arc-Bo3p2lGy.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-7FQo5GbP.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BuojN27B.js → architectureDiagram-ZJ3FMSHR-Cwx8qOvu.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-alDLbrR1.js → blockDiagram-677ZJIJ3-BiuaAOFa.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-D1lKa4VT.js → c4Diagram-LMCZKHZV-BTcGfhK2.js} +1 -1
- package/dist/assets/channel-DToA8d-4.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-D_Wxpk-T.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-CwWLNjMZ.js → chunk-32BRIVSS-B9PuF_RF.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-Be569m7d.js → chunk-52WLFC77-3zGItfIM.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-BSvoiWV1.js → chunk-5FCAYU7R-DQ66qlmt.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-CRgsy8WS.js → chunk-5HE753X5-CLqTO-VF.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CdW477UR.js → chunk-5JV3BV7I-Csxv1jr6.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-CdtKzSmp.js → chunk-5TONJI2A-BCQttpBr.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BKjjssLZ.js → chunk-5VM5RSS4-DEWJGSTb.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-ByAXiXdn.js → chunk-7BUUIJ7U-DNorm9MM.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-DeG8SCT1.js → chunk-BIQX33UG-DgvY_BOT.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-D_zHT68Q.js → chunk-C7G6YPKG-DMvn9IPs.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-CHpHoUSu.js → chunk-CQNSW5MT-Dq2duUgr.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-CRP50Vov.js → chunk-CYSBUYHQ-BovYyc4Y.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-DX6uQQ2v.js → chunk-EMLP6XTP-BMDOpuYN.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DAJRNkHR.js → chunk-EX3LRPZG-6eP2UPOf.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-XbM5yomC.js → chunk-FWX5IMBZ-D4q-wTSy.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Ci0ljnb1.js → chunk-HOUHSVGY-XjIcuzH7.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-Dy2WJ71z.js → chunk-ICXQ74PX-YBXbanq9.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-2PrkzHkt.js → chunk-JG7HCLWE-CcN3uNXF.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-D32uacSM.js → chunk-JWPE2WC7-Cwg6AUAt.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DIAFLtiS.js → chunk-MOJQB5TN-NNspcGIU.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-BbscOaEz.js → chunk-MOZMSUNE-pMJ_jMos.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-ivBVxzKe.js → chunk-OGEWGWER-CJy-3Ldv.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-frD0hkdx.js → chunk-OSBZ3O6U-D7cLHHOA.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-B-37Rxae.js → chunk-PUDLZKDR-D5YBi1Pi.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Y90m2RTe.js → chunk-Q4XR5HBZ-DYmqQdIf.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-DixBqslB.js → chunk-QBLGF6JB-Bfcx00X0.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-DXS1UXn_.js → chunk-R7FJI6CG-oXzadpZY.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-n4JxhNd0.js → chunk-RYQCIY6F-Dt3-8yUh.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-yHlgxMmn.js → chunk-U6XO7XAA-B55eSvpx.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-Z6kuZfbl.js → chunk-V7JOEXUC-Btk4JwCt.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CIZseNLi.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-Cs_SIQma.js → chunk-VR4S4FIN-DgaSiVOY.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-CGExYbV-.js → chunk-WYO6CB5R-rRDZ_Tta.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-Do-XYxat.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-DopPXWSl.js → chunk-YOTPTUD7-BnFxj5o4.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-CJHOeqMb.js → chunk-ZGVPDNZ5-CrnlPlmJ.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-BBVc5mf6.js → chunk-ZIRB5QZD-BYTiATIH.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Bu24Sy-k.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DKhh5HSW.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-DLjV-dTm.js → cose-bilkent-JH36ORCC-BORKJRwF.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-oyiAgYC4.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-BfH3zVS-.js → cynefinDiagram-TSTJHNR4-BXKeaSl8.js} +1 -1
- package/dist/assets/{dagre-CY3D84Cl.js → dagre-CrA0XVwJ.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-BIhwZCIZ.js → dagre-VKFMJZFB-ouq_lA1a.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BHnzNM2w.js → diagram-FQU43EPY-BxR5kUiQ.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C7-ZlmFT.js → diagram-G47NLZAW-DWonk1sL.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-C-P9Y7IR.js → diagram-NH7WQ7WH-fmV_wV5e.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-Ci5lZ_T5.js → diagram-OA4YK3LP-DvOSRRiV.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-C1xuK2bs.js → diagram-WEI45ONY-Bp-xb3uS.js} +1 -1
- package/dist/assets/dist-DLKC_odd.js +1 -0
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DKQ5NWMD.js → ebnfDiagram-CCIWWBDH-BTlnwneA.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-HAWySadP.js → erDiagram-Q63AITRT-C3fFWGCm.js} +1 -1
- package/dist/assets/esm-oa69b5u4.js +2 -0
- package/dist/assets/eventmodeling-45OFAUF4-YB_0MefX.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-Bc73xdBd.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-C1hwIcOA.js → ganttDiagram-NO4QXBWP-DzAiojNi.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-Bvp27L0n.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-DYUYxs1R.js → gitGraphDiagram-IHSO6WYX-Cv57oNEA.js} +1 -1
- package/dist/assets/icons-cTkqD_Gi.js +1 -0
- package/dist/assets/index-B48prgLL.js +65 -0
- package/dist/assets/index-DQhwHHer.css +3 -0
- package/dist/assets/info-DKCQHKI2-BsLOivgq.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-CnIL6tv4.js → infoDiagram-FWYZ7A6U-B5Z_O3Ro.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-B7WXNycQ.js → ishikawaDiagram-FXEZZL3T-CKTK_TKV.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-D4HvioyY.js → journeyDiagram-5HDEW3XC-Cm9k4ywW.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-CDs763eS.js → kanban-definition-HUTT4EX6-DzNteLwx.js} +1 -1
- package/dist/assets/{line-ikKkQiFp.js → line-BG78c_WJ.js} +1 -1
- package/dist/assets/{linear-GqPyxQkN.js → linear-1X20_EGu.js} +1 -1
- package/dist/assets/{map-CJwjCvwr.js → map-CCI8WZkS.js} +1 -1
- package/dist/assets/{mcp-servers-dialog-BQbFywVL.js → mcp-servers-dialog-GXXD9K0U.js} +2 -2
- package/dist/assets/{mermaid-parser.core-CoE8cccr.js → mermaid-parser.core-Bb8HI_OS.js} +2 -2
- package/dist/assets/{mermaid-renderer-BmbSAmpG.js → mermaid-renderer-PIaNiXAz.js} +2 -2
- package/dist/assets/{mermaid.core-DlFSL90j.js → mermaid.core-DUHOIwL5.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-DLdq0K9C.js → mindmap-definition-LN4V7U3C-Tnbszo7b.js} +1 -1
- package/dist/assets/{monaco-BTsVCDWS.js → monaco-BVtCspYZ.js} +1 -1
- package/dist/assets/{ordinal-CibZU3PM.js → ordinal-v4prQcuq.js} +1 -1
- package/dist/assets/{otel-BNGM50C-.js → otel-D1LswXpM.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-BKQkXRtk.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-Cm1kPRC2.js → pegDiagram-2B236MQR-CX824Kd0.js} +1 -1
- package/dist/assets/{pi-ai-Cr3rpCXT.js → pi-ai-DAJlO9ZB.js} +2 -2
- package/dist/assets/{pi-web-ui-DoRGtREc.js → pi-web-ui-B-RJSxjR.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-BTiTN5Ge.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-Df05wjjo.js → pieDiagram-ENE6RG2P-BT04VWPQ.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BjCwGxJC.js → quadrantDiagram-ABIIQ3AL-BvOZxkBr.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-BU-oVjKM.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-7H2hxNbL.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-Mx6wf6XQ.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-BgXSKhYy.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-DTEl2rAC.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-CTUvpKkv.js → railroadDiagram-RFXS5EU6-DnTU--BV.js} +1 -1
- package/dist/assets/{react-vendor-Dr5xvL-e.js → react-vendor-eWH0QhNU.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-BU1kvEku.js → requirementDiagram-TGXJPOKE--MZX_yEv.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-BHmvxVqH.js → sankeyDiagram-HTMAVEWB-SEZkHhYn.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-CTpwICgg.js → sequenceDiagram-DBY2YBRQ-BhC2OHiz.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-BbLhGaa4.js → sizeCapture-X5ZJPWSS-Ci5LeToV.js} +1 -1
- package/dist/assets/{skills-dialog-REBeTFSH.js → skills-dialog-DDh1h8eH.js} +3 -1
- package/dist/assets/{src-B4Tvg3gT.js → src-CovulcRC.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-BxuDwIgT.js → stateDiagram-2N3HPSRC-DKK5tFT4.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-D8x3mkni.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-EkZsiylV.js → swimlanes-5IMT3BWC-DxxGCk67.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-C4nOBNp8.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-C9ewOCTa.js → timeline-definition-FHXFAJF6-BPXB3Sue.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-DOE6NSdf.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-Ca7QD_b4.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CR49Y4Lp.js → vennDiagram-L72KCM5P-CxcBCKye.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-D9O4RKRm.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-Cn0GgwWi.js → wardleyDiagram-EHGQE667-DEL6Yes5.js} +1 -1
- package/dist/assets/web-RgX7H-OH.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-DlmPNAvd.js → xychartDiagram-FW5EYKEG-N8cPbcjC.js} +1 -1
- package/dist/index.html +12 -11
- package/dist/mobile-error.html +24 -0
- package/package.json +4 -1
- package/server/acp/server.mjs +14 -9
- package/server/agent-manager.mjs +215 -20
- package/server/ai-http-logger.mjs +68 -11
- package/server/ai-provider-options.mjs +3 -0
- package/server/channels/channel-logs.mjs +13 -0
- package/server/channels/event-relay.mjs +33 -0
- package/server/channels/process-channel.mjs +29 -0
- package/server/channels/providers/wechat.mjs +9 -3
- package/server/channels/registry.mjs +5 -1
- package/server/index.mjs +36 -23
- package/server/lan-access-store.mjs +93 -3
- package/server/network-proxy.mjs +68 -3
- package/server/plugins/registry.mjs +64 -4
- package/server/public-api.mjs +6 -2
- package/server/routes/agent-profiles.mjs +2 -2
- package/server/routes/agent.mjs +28 -0
- package/server/routes/channels.mjs +47 -0
- package/server/routes/lan-access.mjs +246 -21
- package/server/routes/models.mjs +6 -3
- package/server/routes/project.mjs +19 -1
- package/server/routes/scheduled-tasks.mjs +2 -2
- package/server/routes/static.mjs +26 -1
- package/server/routes/system.mjs +11 -0
- package/server/routes/terminal.mjs +7 -2
- package/server/routes/workspace.mjs +60 -9
- package/server/session-utils.mjs +2 -2
- package/server/subagents.mjs +1 -1
- package/server/terminal/terminal-manager.mjs +2 -4
- package/server/tools/definitions.mjs +2 -2
- package/server/tools/index.mjs +5 -3
- package/server/utils/package-update.mjs +70 -47
- package/server/utils/process-env.mjs +7 -0
- package/server/utils/response.mjs +2 -2
- package/dist/assets/AgentProfilesPage-D7pbg8nm.js +0 -1
- package/dist/assets/SettingsWorkspacePage-BtJZMIsZ.js +0 -1576
- package/dist/assets/SharedConversationPage-Ce3KoVZt.js +0 -1
- package/dist/assets/TerminalDock-4eUuXP7X.js +0 -2
- package/dist/assets/WorkspaceInspector-kM3BRPjY.js +0 -13
- package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +0 -1
- package/dist/assets/channel-Rz20dKTK.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +0 -1
- package/dist/assets/icons-BP8YOS-Z.js +0 -1
- package/dist/assets/index-C6k5taeb.js +0 -66
- package/dist/assets/index-CzN8NSKC.css +0 -3
- package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +0 -1
- package/dist/assets/packet-7NZHBO7P-decrrxyX.js +0 -1
- package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +0 -1
- package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +0 -1
- /package/dist/assets/{array-CYkMkqnU.js → array-DqLCdDFv.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-77hsebd7.js → chunk-KEIR6QF5-s7Qq-R3i.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-CQltPwYk.js → chunk-Y2CYZVJY-BgISNSiw.js} +0 -0
- /package/dist/assets/{css-utils-rkE68RDy.js → css-utils-31qzCXsk.js} +0 -0
- /package/dist/assets/{cytoscape.esm-DwdZnGOm.js → cytoscape.esm-iekjSZ2n.js} +0 -0
- /package/dist/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-CrJzLgRD.js} +0 -0
- /package/dist/assets/{dist-DelZBZpK.js → dist-zzfqk5hx.js} +0 -0
- /package/dist/assets/{graphlib-CAISHZh4.js → graphlib-CIz_CfxW.js} +0 -0
- /package/dist/assets/{init-Rr1s_RiX.js → init-C0r9Gk5G.js} +0 -0
- /package/dist/assets/{lit-vendor-Dr3cpBGF.js → lit-vendor-DvaQX_UQ.js} +0 -0
- /package/dist/assets/{logger-B65Akg8A.js → logger-8REuLPGK.js} +0 -0
- /package/dist/assets/{path-BAQ3hXlG.js → path-DIKpVbHL.js} +0 -0
- /package/dist/assets/{plugin-api-BbUkYm3Y.js → plugin-api-C8hMT5W6.js} +0 -0
- /package/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-BMOtDaaS.js} +0 -0
- /package/dist/assets/{xterm-CGiQzDiR.js → xterm-BFzZ1FH4.js} +0 -0
|
@@ -4,6 +4,7 @@ import { promises as fs } from 'node:fs'
|
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import { ProcessChannelProvider } from '../process-channel.mjs'
|
|
6
6
|
import { getDefaultWorkspaceRoot, readProjectConfig } from '../../project-config.mjs'
|
|
7
|
+
import { createNodeProcessEnv } from '../../utils/process-env.mjs'
|
|
7
8
|
|
|
8
9
|
const WEIXIN_ACP_PACKAGE = 'weixin-acp'
|
|
9
10
|
const MIN_NODE_MAJOR = 22
|
|
@@ -134,7 +135,7 @@ function runCommand(command, args, options = {}) {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
export class WechatChannelProvider extends ProcessChannelProvider {
|
|
137
|
-
constructor({ projectRoot }) {
|
|
138
|
+
constructor({ projectRoot, channelEventsUrl, logsDir }) {
|
|
138
139
|
super({
|
|
139
140
|
id: 'wechat',
|
|
140
141
|
name: '微信',
|
|
@@ -149,8 +150,9 @@ export class WechatChannelProvider extends ProcessChannelProvider {
|
|
|
149
150
|
{ id: 'relogin', label: '重新登录' },
|
|
150
151
|
],
|
|
151
152
|
requirements: [`Node.js >= ${MIN_NODE_MAJOR}`, 'npm/npx 可用', '首次启动需要微信扫码登录'],
|
|
152
|
-
})
|
|
153
|
+
}, { logsDir })
|
|
153
154
|
this.projectRoot = projectRoot
|
|
155
|
+
this.channelEventsUrl = channelEventsUrl
|
|
154
156
|
this.launchWorkspace = null
|
|
155
157
|
}
|
|
156
158
|
|
|
@@ -179,7 +181,11 @@ export class WechatChannelProvider extends ProcessChannelProvider {
|
|
|
179
181
|
command: npxCommand(),
|
|
180
182
|
args: ['-y', WEIXIN_ACP_PACKAGE, 'start', '--', acpCommand, ...acpArgs],
|
|
181
183
|
cwd: this.launchWorkspace?.path || getDefaultWorkspaceRoot() || this.projectRoot,
|
|
182
|
-
env: process.env,
|
|
184
|
+
env: createNodeProcessEnv(process.env, {
|
|
185
|
+
QUICKFORGE_ACP_CHANNEL_ID: this.definition.id,
|
|
186
|
+
QUICKFORGE_ACP_CHANNEL_NAME: this.definition.name,
|
|
187
|
+
...(this.channelEventsUrl ? { QUICKFORGE_CHANNEL_EVENTS_URL: this.channelEventsUrl } : {}),
|
|
188
|
+
}),
|
|
183
189
|
shell: shouldUseShellForNpx(),
|
|
184
190
|
}
|
|
185
191
|
}
|
|
@@ -16,7 +16,11 @@ function attachProvider(provider) {
|
|
|
16
16
|
export function initializeChannels(options = {}) {
|
|
17
17
|
if (initialized) return
|
|
18
18
|
initialized = true
|
|
19
|
-
attachProvider(createWechatChannelProvider({
|
|
19
|
+
attachProvider(createWechatChannelProvider({
|
|
20
|
+
projectRoot: options.projectRoot,
|
|
21
|
+
channelEventsUrl: options.channelEventsUrl,
|
|
22
|
+
logsDir: options.logsDir,
|
|
23
|
+
}))
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
function requireProvider(id) {
|
package/server/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path'
|
|
|
5
5
|
import { randomUUID } from 'node:crypto'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
7
|
import { sendJson, sendError } from './utils/response.mjs'
|
|
8
|
-
import { openBrowser } from './utils/platform.mjs'
|
|
8
|
+
import { openBrowser, openPathInFileManager } from './utils/platform.mjs'
|
|
9
9
|
import { ensureStorage, dataDir, configDir, storageDir, cacheDir, logsDir } from './storage.mjs'
|
|
10
10
|
import { getNetworkProxyConfig, initializeNetworkProxy, refreshSystemProxy, updateNetworkProxyConfig } from './network-proxy.mjs'
|
|
11
11
|
import { setDefaultWorkspaceRoot, initializeActiveProject, readProjectConfig, getActiveProject, readTerminalShellSetting, updateTerminalShellSetting, readTerminalShellConfig, updateTerminalShellConfig } from './project-config.mjs'
|
|
@@ -44,6 +44,7 @@ import { shutdown as shutdownAgentManager, resetStaleTaskStatuses } from './agen
|
|
|
44
44
|
import { initializeChannels, shutdownChannels } from './channels/registry.mjs'
|
|
45
45
|
import { shutdownMcpConnections } from './mcp/registry.mjs'
|
|
46
46
|
import { shutdownTerminalSessions } from './terminal/terminal-manager.mjs'
|
|
47
|
+
import { createNodeProcessEnv } from './utils/process-env.mjs'
|
|
47
48
|
|
|
48
49
|
const __filename = fileURLToPath(import.meta.url)
|
|
49
50
|
const __dirname = path.dirname(__filename)
|
|
@@ -71,10 +72,11 @@ function getRestartSupport() {
|
|
|
71
72
|
return { supported: true, reason: null }
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
async function getSystemStatus() {
|
|
75
|
+
async function getSystemStatus({ isLocalRequest = true } = {}) {
|
|
75
76
|
const config = await readProjectConfig()
|
|
76
77
|
const restartSupport = getRestartSupport()
|
|
77
78
|
const packageInfo = await getPackageInfo(projectRoot)
|
|
79
|
+
const restartSupported = isLocalRequest && restartSupport.supported
|
|
78
80
|
return {
|
|
79
81
|
ok: true,
|
|
80
82
|
mode: isDev ? 'development' : 'production',
|
|
@@ -83,8 +85,14 @@ async function getSystemStatus() {
|
|
|
83
85
|
startedAt,
|
|
84
86
|
version: packageInfo.version,
|
|
85
87
|
package: packageInfo,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
isLocalRequest,
|
|
89
|
+
capabilities: {
|
|
90
|
+
restart: restartSupported,
|
|
91
|
+
openLocalApps: isLocalRequest,
|
|
92
|
+
terminal: isLocalRequest,
|
|
93
|
+
},
|
|
94
|
+
restartSupported,
|
|
95
|
+
restartUnsupportedReason: isLocalRequest ? restartSupport.reason : 'Restart is only allowed from this computer',
|
|
88
96
|
dataDir,
|
|
89
97
|
configDir,
|
|
90
98
|
storageDir,
|
|
@@ -113,10 +121,9 @@ function spawnRestartSupervisor() {
|
|
|
113
121
|
stdio: 'ignore',
|
|
114
122
|
windowsHide: true,
|
|
115
123
|
shell: false,
|
|
116
|
-
env: {
|
|
117
|
-
...process.env,
|
|
124
|
+
env: createNodeProcessEnv(process.env, {
|
|
118
125
|
QUICKFORGE_NO_OPEN: '1',
|
|
119
|
-
},
|
|
126
|
+
}),
|
|
120
127
|
})
|
|
121
128
|
|
|
122
129
|
child.once('error', reject)
|
|
@@ -206,10 +213,9 @@ function spawnUpdateSupervisor(update) {
|
|
|
206
213
|
stdio: 'ignore',
|
|
207
214
|
windowsHide: true,
|
|
208
215
|
shell: false,
|
|
209
|
-
env: {
|
|
210
|
-
...process.env,
|
|
216
|
+
env: createNodeProcessEnv(process.env, {
|
|
211
217
|
QUICKFORGE_NO_OPEN: '1',
|
|
212
|
-
},
|
|
218
|
+
}),
|
|
213
219
|
})
|
|
214
220
|
|
|
215
221
|
child.once('error', reject)
|
|
@@ -277,7 +283,7 @@ async function updateQuickForge() {
|
|
|
277
283
|
}
|
|
278
284
|
|
|
279
285
|
// --- Route dispatching ---
|
|
280
|
-
async function handleApi(req, res, url) {
|
|
286
|
+
async function handleApi(req, res, url, requestContext = {}) {
|
|
281
287
|
const pathname = url.pathname
|
|
282
288
|
const parts = pathname.split('/').filter(Boolean)
|
|
283
289
|
|
|
@@ -297,22 +303,22 @@ async function handleApi(req, res, url) {
|
|
|
297
303
|
return
|
|
298
304
|
}
|
|
299
305
|
|
|
300
|
-
if (pathname
|
|
306
|
+
if (pathname.startsWith('/api/lan-access/')) {
|
|
301
307
|
await handleLanAccessApi(req, res, url, {
|
|
302
308
|
port,
|
|
303
|
-
isLocalRequest:
|
|
309
|
+
isLocalRequest: requestContext.isLocalRequest === true,
|
|
304
310
|
})
|
|
305
311
|
return
|
|
306
312
|
}
|
|
307
313
|
|
|
308
314
|
// Health check
|
|
309
315
|
if (req.method === 'GET' && pathname === '/api/health') {
|
|
310
|
-
sendJson(res, 200, await getSystemStatus())
|
|
316
|
+
sendJson(res, 200, await getSystemStatus(requestContext))
|
|
311
317
|
return
|
|
312
318
|
}
|
|
313
319
|
|
|
314
320
|
if (req.method === 'GET' && pathname === '/api/project/commands') {
|
|
315
|
-
await handleProjectApi(req, res, url)
|
|
321
|
+
await handleProjectApi(req, res, url, requestContext)
|
|
316
322
|
return
|
|
317
323
|
}
|
|
318
324
|
|
|
@@ -355,7 +361,9 @@ async function handleApi(req, res, url) {
|
|
|
355
361
|
// Channels
|
|
356
362
|
if (pathname === '/api/channels' || pathname.startsWith('/api/channels/')) {
|
|
357
363
|
await handleChannelsApi(req, res, url, {
|
|
358
|
-
isLocalRequest:
|
|
364
|
+
isLocalRequest: requestContext.isLocalRequest === true,
|
|
365
|
+
logsDir,
|
|
366
|
+
openPathInFileManager,
|
|
359
367
|
})
|
|
360
368
|
return
|
|
361
369
|
}
|
|
@@ -368,13 +376,13 @@ async function handleApi(req, res, url) {
|
|
|
368
376
|
|
|
369
377
|
// Project routes
|
|
370
378
|
if (pathname === '/api/project' || pathname.startsWith('/api/project/')) {
|
|
371
|
-
await handleProjectApi(req, res, url)
|
|
379
|
+
await handleProjectApi(req, res, url, requestContext)
|
|
372
380
|
return
|
|
373
381
|
}
|
|
374
382
|
|
|
375
383
|
// Project workspace inspector routes
|
|
376
384
|
if (pathname === '/api/workspace/tree' || pathname === '/api/workspace/file' || pathname === '/api/workspace/resolve-path' || pathname === '/api/workspace/open-external' || pathname.startsWith('/api/workspace/preview/')) {
|
|
377
|
-
await handleWorkspaceApi(req, res, url)
|
|
385
|
+
await handleWorkspaceApi(req, res, url, requestContext)
|
|
378
386
|
return
|
|
379
387
|
}
|
|
380
388
|
|
|
@@ -422,13 +430,13 @@ async function handleApi(req, res, url) {
|
|
|
422
430
|
// System routes
|
|
423
431
|
if (pathname === '/api/system/status' || pathname === '/api/system/restart' || pathname === '/api/system/network' || pathname === '/api/system/network-proxy' || pathname === '/api/system/network-proxy/refresh' || pathname === '/api/system/terminal-shell' || pathname === '/api/system/about' || pathname === '/api/system/update/check' || pathname === '/api/system/update/desktop' || pathname === '/api/system/update') {
|
|
424
432
|
await handleSystemApi(req, res, url, {
|
|
425
|
-
getSystemStatus,
|
|
433
|
+
getSystemStatus: () => getSystemStatus(requestContext),
|
|
426
434
|
requestRestart,
|
|
427
435
|
getPackageInfo: () => getPackageInfo(projectRoot),
|
|
428
436
|
checkForUpdates: () => checkForUpdates(projectRoot),
|
|
429
437
|
checkDesktopRelease: () => checkDesktopRelease(projectRoot),
|
|
430
438
|
updateQuickForge,
|
|
431
|
-
isLocalRequest:
|
|
439
|
+
isLocalRequest: requestContext.isLocalRequest === true,
|
|
432
440
|
getTerminalShellSetting: readTerminalShellSetting,
|
|
433
441
|
updateTerminalShellSetting,
|
|
434
442
|
getTerminalShellConfig: readTerminalShellConfig,
|
|
@@ -446,7 +454,7 @@ async function handleApi(req, res, url) {
|
|
|
446
454
|
// Terminal routes (local-only; real shell access)
|
|
447
455
|
if (pathname === '/api/terminal/capabilities' || pathname === '/api/terminal/sessions' || pathname.startsWith('/api/terminal/sessions/')) {
|
|
448
456
|
await handleTerminalApi(req, res, url, {
|
|
449
|
-
isLocalRequest:
|
|
457
|
+
isLocalRequest: requestContext.isLocalRequest === true,
|
|
450
458
|
})
|
|
451
459
|
return
|
|
452
460
|
}
|
|
@@ -532,6 +540,7 @@ function isLanAccessBootstrapPath(pathname) {
|
|
|
532
540
|
|
|
533
541
|
function isStaticAssetPath(pathname) {
|
|
534
542
|
return pathname.startsWith('/assets/')
|
|
543
|
+
|| pathname.startsWith('/downloads/')
|
|
535
544
|
|| pathname === '/favicon.svg'
|
|
536
545
|
|| pathname === '/vite.svg'
|
|
537
546
|
|| pathname === '/manifest.webmanifest'
|
|
@@ -610,7 +619,7 @@ const server = createServer(async (req, res) => {
|
|
|
610
619
|
}
|
|
611
620
|
|
|
612
621
|
if (url.pathname.startsWith('/api/')) {
|
|
613
|
-
await handleApi(req, res, url)
|
|
622
|
+
await handleApi(req, res, url, { isLocalRequest: !isRemoteRequest })
|
|
614
623
|
return
|
|
615
624
|
}
|
|
616
625
|
|
|
@@ -674,7 +683,11 @@ server.on('upgrade', (req, socket, head) => {
|
|
|
674
683
|
await ensureStorage()
|
|
675
684
|
await initializeNetworkProxy()
|
|
676
685
|
installAiHttpLogger()
|
|
677
|
-
initializeChannels({
|
|
686
|
+
initializeChannels({
|
|
687
|
+
projectRoot,
|
|
688
|
+
channelEventsUrl: `http://127.0.0.1:${port}/api/channels/events`,
|
|
689
|
+
logsDir,
|
|
690
|
+
})
|
|
678
691
|
await resetStaleTaskStatuses()
|
|
679
692
|
await initializeActiveProject()
|
|
680
693
|
setActiveWorkspaceRootForFilesystem(getWorkspaceRoot())
|
|
@@ -80,13 +80,41 @@ async function writeLanAccessFile(config) {
|
|
|
80
80
|
await fs.writeFile(LAN_ACCESS_FILE, `${JSON.stringify(normalizeConfig(config), null, 2)}\n`, 'utf8')
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function normalizeText(value, maxLength) {
|
|
84
|
+
if (typeof value !== 'string') return undefined
|
|
85
|
+
const text = value.trim()
|
|
86
|
+
return text ? text.slice(0, maxLength) : undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeRemoteAddress(value) {
|
|
90
|
+
const address = normalizeText(value, 128)
|
|
91
|
+
return address?.startsWith('::ffff:') ? address.slice(7) : address
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function tokenRecordId(tokenRecord) {
|
|
95
|
+
const id = normalizeText(tokenRecord?.id, 128)
|
|
96
|
+
return id || `legacy_${sha256Base64Url(tokenRecord?.tokenHash || '').slice(0, 24)}`
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function publicTokenRecord(tokenRecord) {
|
|
100
|
+
return {
|
|
101
|
+
id: tokenRecordId(tokenRecord),
|
|
102
|
+
address: normalizeRemoteAddress(tokenRecord.remoteAddress),
|
|
103
|
+
userAgent: normalizeText(tokenRecord.userAgent, 300),
|
|
104
|
+
issuedAt: tokenRecord.issuedAt,
|
|
105
|
+
expiresAt: tokenRecord.expiresAt,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
83
109
|
function publicStatus(config) {
|
|
110
|
+
const activeTokens = pruneTokens(config.tokens)
|
|
84
111
|
return {
|
|
85
112
|
enabled: Boolean(config.enabled),
|
|
86
113
|
hasPassword: Boolean(config.passwordHash),
|
|
87
114
|
sessionTtlHours: config.sessionTtlHours,
|
|
88
115
|
authVersion: config.authVersion || 1,
|
|
89
|
-
activeTokenCount:
|
|
116
|
+
activeTokenCount: activeTokens.length,
|
|
117
|
+
activeDevices: activeTokens.map(publicTokenRecord),
|
|
90
118
|
updatedAt: config.updatedAt,
|
|
91
119
|
}
|
|
92
120
|
}
|
|
@@ -98,6 +126,12 @@ function pruneTokens(tokens, now = Date.now()) {
|
|
|
98
126
|
if (!tokenRecord.expiresAt) return true
|
|
99
127
|
return Date.parse(tokenRecord.expiresAt) > now
|
|
100
128
|
})
|
|
129
|
+
.map((tokenRecord) => ({
|
|
130
|
+
...tokenRecord,
|
|
131
|
+
id: tokenRecordId(tokenRecord),
|
|
132
|
+
remoteAddress: normalizeRemoteAddress(tokenRecord.remoteAddress),
|
|
133
|
+
userAgent: normalizeText(tokenRecord.userAgent, 300),
|
|
134
|
+
}))
|
|
101
135
|
.slice(-LAN_TOKEN_MAX_COUNT)
|
|
102
136
|
}
|
|
103
137
|
|
|
@@ -162,11 +196,58 @@ export async function revokeLanAccessTokens() {
|
|
|
162
196
|
})
|
|
163
197
|
}
|
|
164
198
|
|
|
199
|
+
export async function revokeLanAccessTokenById(id) {
|
|
200
|
+
const normalizedId = normalizeText(id, 128)
|
|
201
|
+
if (!normalizedId) {
|
|
202
|
+
const error = new Error('LAN access session id is required.')
|
|
203
|
+
error.statusCode = 400
|
|
204
|
+
throw error
|
|
205
|
+
}
|
|
206
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
207
|
+
const current = await readLanAccessFile()
|
|
208
|
+
const tokens = pruneTokens(current.tokens)
|
|
209
|
+
const nextTokens = tokens.filter((tokenRecord) => tokenRecordId(tokenRecord) !== normalizedId)
|
|
210
|
+
if (nextTokens.length === tokens.length) {
|
|
211
|
+
const error = new Error('LAN access session not found.')
|
|
212
|
+
error.statusCode = 404
|
|
213
|
+
throw error
|
|
214
|
+
}
|
|
215
|
+
const next = normalizeConfig({
|
|
216
|
+
...current,
|
|
217
|
+
tokens: nextTokens,
|
|
218
|
+
updatedAt: new Date().toISOString(),
|
|
219
|
+
})
|
|
220
|
+
await writeLanAccessFile(next)
|
|
221
|
+
return publicStatus(next)
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export async function revokeLanAccessToken(token) {
|
|
226
|
+
if (!token || typeof token !== 'string') return false
|
|
227
|
+
const [versionText, secret] = token.split('.')
|
|
228
|
+
if (!secret) return false
|
|
229
|
+
const actualHash = sha256Base64Url(secret)
|
|
230
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
231
|
+
const current = await readLanAccessFile()
|
|
232
|
+
if (Number(versionText) !== (current.authVersion || 1)) return false
|
|
233
|
+
const tokens = pruneTokens(current.tokens)
|
|
234
|
+
const nextTokens = tokens.filter((tokenRecord) => !safeHashEqual(tokenRecord.tokenHash, actualHash))
|
|
235
|
+
if (nextTokens.length === tokens.length) return false
|
|
236
|
+
const next = normalizeConfig({
|
|
237
|
+
...current,
|
|
238
|
+
tokens: nextTokens,
|
|
239
|
+
updatedAt: new Date().toISOString(),
|
|
240
|
+
})
|
|
241
|
+
await writeLanAccessFile(next)
|
|
242
|
+
return true
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
|
|
165
246
|
export function lanAccessCookieName() {
|
|
166
247
|
return 'qf_lan_access'
|
|
167
248
|
}
|
|
168
249
|
|
|
169
|
-
export async function issueLanAccessToken(password) {
|
|
250
|
+
export async function issueLanAccessToken(password, metadata = {}) {
|
|
170
251
|
return enqueueWrite(writeQueueName, async () => {
|
|
171
252
|
const current = await readLanAccessFile()
|
|
172
253
|
if (!current.enabled || !current.passwordHash) {
|
|
@@ -185,11 +266,20 @@ export async function issueLanAccessToken(password) {
|
|
|
185
266
|
const issuedAt = new Date().toISOString()
|
|
186
267
|
const ttlMs = normalizeSessionTtlHours(current.sessionTtlHours) * 60 * 60 * 1000
|
|
187
268
|
const expiresAt = new Date(Date.now() + ttlMs).toISOString()
|
|
269
|
+
const tokenRecord = {
|
|
270
|
+
id: createRandomToken(18),
|
|
271
|
+
tokenHash,
|
|
272
|
+
issuedAt,
|
|
273
|
+
expiresAt,
|
|
274
|
+
authVersion: current.authVersion || 1,
|
|
275
|
+
remoteAddress: normalizeRemoteAddress(metadata.remoteAddress),
|
|
276
|
+
userAgent: normalizeText(metadata.userAgent, 300),
|
|
277
|
+
}
|
|
188
278
|
const next = normalizeConfig({
|
|
189
279
|
...current,
|
|
190
280
|
tokens: [
|
|
191
281
|
...pruneTokens(current.tokens),
|
|
192
|
-
|
|
282
|
+
tokenRecord,
|
|
193
283
|
].slice(-LAN_TOKEN_MAX_COUNT),
|
|
194
284
|
updatedAt: issuedAt,
|
|
195
285
|
})
|
package/server/network-proxy.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { SocksClient } from 'socks'
|
|
|
4
4
|
import { atomicUpdate, readStore } from './storage.mjs'
|
|
5
5
|
|
|
6
6
|
const SETTINGS_KEY = 'network-proxy'
|
|
7
|
-
const VALID_MODES = new Set(['direct', 'system', 'manual'])
|
|
7
|
+
const VALID_MODES = new Set(['direct', 'system', 'manual', 'pac'])
|
|
8
8
|
const PROXY_ENV_KEYS = [
|
|
9
9
|
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY',
|
|
10
10
|
'http_proxy', 'https_proxy', 'all_proxy', 'no_proxy',
|
|
@@ -60,6 +60,40 @@ export function validateManualProxyUrl(value) {
|
|
|
60
60
|
return `${url.protocol}//${url.host}`
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export function validatePacUrl(value) {
|
|
64
|
+
let url
|
|
65
|
+
try {
|
|
66
|
+
url = new URL(String(value || '').trim())
|
|
67
|
+
} catch {
|
|
68
|
+
const error = new Error('PAC URL must be a valid HTTP or HTTPS URL')
|
|
69
|
+
error.statusCode = 400
|
|
70
|
+
throw error
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
74
|
+
const error = new Error('Only HTTP and HTTPS PAC URLs are supported')
|
|
75
|
+
error.statusCode = 400
|
|
76
|
+
throw error
|
|
77
|
+
}
|
|
78
|
+
if (!url.hostname) {
|
|
79
|
+
const error = new Error('PAC URL must include a host')
|
|
80
|
+
error.statusCode = 400
|
|
81
|
+
throw error
|
|
82
|
+
}
|
|
83
|
+
if (url.username || url.password) {
|
|
84
|
+
const error = new Error('Credentials are not supported in the PAC URL')
|
|
85
|
+
error.statusCode = 400
|
|
86
|
+
throw error
|
|
87
|
+
}
|
|
88
|
+
if (url.hash) {
|
|
89
|
+
const error = new Error('PAC URL cannot include a fragment')
|
|
90
|
+
error.statusCode = 400
|
|
91
|
+
throw error
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return url.href
|
|
95
|
+
}
|
|
96
|
+
|
|
63
97
|
function isLoopbackUrl(input) {
|
|
64
98
|
let url
|
|
65
99
|
try {
|
|
@@ -251,9 +285,27 @@ class SystemProxyDispatcher extends Dispatcher {
|
|
|
251
285
|
}
|
|
252
286
|
}
|
|
253
287
|
|
|
288
|
+
class UnsupportedProxyDispatcher extends Dispatcher {
|
|
289
|
+
constructor(message) {
|
|
290
|
+
super()
|
|
291
|
+
this.error = new Error(message)
|
|
292
|
+
this.error.code = 'QUICKFORGE_PROXY_UNSUPPORTED'
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
dispatch(_options, handler) {
|
|
296
|
+
queueMicrotask(() => handler.onResponseError?.(null, this.error))
|
|
297
|
+
return true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async close() {}
|
|
301
|
+
|
|
302
|
+
async destroy() {}
|
|
303
|
+
}
|
|
304
|
+
|
|
254
305
|
async function createNetworkDispatcher(config) {
|
|
255
306
|
if (config.mode === 'manual') return new ProxyAgent(config.proxyUrl)
|
|
256
307
|
if (config.mode === 'system') return new SystemProxyDispatcher(await getNativeResolver())
|
|
308
|
+
if (config.mode === 'pac') return new UnsupportedProxyDispatcher('Custom PAC URLs are not supported by this runtime')
|
|
257
309
|
return new Agent()
|
|
258
310
|
}
|
|
259
311
|
|
|
@@ -291,6 +343,7 @@ async function persistConfig(config) {
|
|
|
291
343
|
async function applyConfig(config) {
|
|
292
344
|
const normalized = normalizeNetworkProxyConfig(config)
|
|
293
345
|
if (normalized.mode === 'manual') normalized.proxyUrl = validateManualProxyUrl(normalized.proxyUrl)
|
|
346
|
+
if (normalized.mode === 'pac') normalized.proxyUrl = validatePacUrl(normalized.proxyUrl)
|
|
294
347
|
|
|
295
348
|
if (hostRuntime) await hostRuntime.apply(normalized)
|
|
296
349
|
else await replaceNetworkDispatcher(normalized)
|
|
@@ -310,6 +363,7 @@ async function nativeStatus() {
|
|
|
310
363
|
source,
|
|
311
364
|
features: {
|
|
312
365
|
pac: config.configuredPac?.state !== 'unsupported',
|
|
366
|
+
pacUrl: false,
|
|
313
367
|
wpad: config.wpadDns?.state !== 'unsupported' || config.wpadDhcp?.state !== 'unsupported',
|
|
314
368
|
httpProxy: true,
|
|
315
369
|
httpsProxy: true,
|
|
@@ -320,7 +374,7 @@ async function nativeStatus() {
|
|
|
320
374
|
return {
|
|
321
375
|
supported: false,
|
|
322
376
|
source: 'none',
|
|
323
|
-
features: { pac: false, wpad: false, httpProxy: false, httpsProxy: false, socks: false },
|
|
377
|
+
features: { pac: false, pacUrl: false, wpad: false, httpProxy: false, httpsProxy: false, socks: false },
|
|
324
378
|
error: error instanceof Error ? error.message : String(error),
|
|
325
379
|
}
|
|
326
380
|
}
|
|
@@ -331,7 +385,9 @@ export async function getNetworkProxyStatus() {
|
|
|
331
385
|
? await hostRuntime.getStatus()
|
|
332
386
|
: await nativeStatus()
|
|
333
387
|
const configuredMode = currentConfig.mode
|
|
334
|
-
const modeSupported = configuredMode
|
|
388
|
+
const modeSupported = configuredMode === 'system'
|
|
389
|
+
? runtimeStatus.supported
|
|
390
|
+
: configuredMode !== 'pac' || runtimeStatus.features?.pacUrl === true
|
|
335
391
|
return {
|
|
336
392
|
configuredMode,
|
|
337
393
|
effectiveMode: modeSupported ? configuredMode : 'unsupported',
|
|
@@ -361,6 +417,15 @@ export async function updateNetworkProxyConfig(value) {
|
|
|
361
417
|
const previous = currentConfig
|
|
362
418
|
const next = normalizeNetworkProxyConfig(value)
|
|
363
419
|
if (next.mode === 'manual') next.proxyUrl = validateManualProxyUrl(next.proxyUrl)
|
|
420
|
+
if (next.mode === 'pac') {
|
|
421
|
+
next.proxyUrl = validatePacUrl(next.proxyUrl)
|
|
422
|
+
const runtimeStatus = hostRuntime ? await hostRuntime.getStatus() : await nativeStatus()
|
|
423
|
+
if (runtimeStatus.features?.pacUrl !== true) {
|
|
424
|
+
const error = new Error('Custom PAC URLs are not supported by this runtime')
|
|
425
|
+
error.statusCode = 400
|
|
426
|
+
throw error
|
|
427
|
+
}
|
|
428
|
+
}
|
|
364
429
|
|
|
365
430
|
await applyConfig(next)
|
|
366
431
|
try {
|
|
@@ -16,6 +16,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
16
16
|
const bundledPluginDir = path.resolve(__dirname, '..', '..', 'plugins')
|
|
17
17
|
const globalPluginDir = path.join(dataDir, 'plugins')
|
|
18
18
|
const legacyGlobalPluginDir = path.join(os.homedir(), '.agents', 'plugins')
|
|
19
|
+
export const DEFAULT_PLUGIN_TOOL_TIMEOUT_MS = 2 * 60 * 1000
|
|
19
20
|
let cachedCatalog = null
|
|
20
21
|
let cachedCatalogKey = null
|
|
21
22
|
let catalogDirty = false
|
|
@@ -29,6 +30,56 @@ function isPlainObject(value) {
|
|
|
29
30
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value))
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function withTimeout(promise, timeoutMs, message, onTimeout, signal) {
|
|
34
|
+
let timer
|
|
35
|
+
let timedOut = false
|
|
36
|
+
let abortHandler
|
|
37
|
+
const guardedPromise = promise.catch((error) => {
|
|
38
|
+
if (timedOut) return new Promise(() => {})
|
|
39
|
+
throw error
|
|
40
|
+
})
|
|
41
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
42
|
+
const rejectForAbort = () => {
|
|
43
|
+
timedOut = true
|
|
44
|
+
reject(new Error('Plugin tool call aborted'))
|
|
45
|
+
}
|
|
46
|
+
if (signal?.aborted) {
|
|
47
|
+
rejectForAbort()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
if (signal) {
|
|
51
|
+
abortHandler = rejectForAbort
|
|
52
|
+
signal.addEventListener('abort', abortHandler, { once: true })
|
|
53
|
+
}
|
|
54
|
+
timer = setTimeout(() => {
|
|
55
|
+
timedOut = true
|
|
56
|
+
try {
|
|
57
|
+
onTimeout?.()
|
|
58
|
+
} finally {
|
|
59
|
+
reject(new Error(message))
|
|
60
|
+
}
|
|
61
|
+
}, timeoutMs)
|
|
62
|
+
})
|
|
63
|
+
return Promise.race([guardedPromise, timeoutPromise]).finally(() => {
|
|
64
|
+
clearTimeout(timer)
|
|
65
|
+
if (abortHandler) signal?.removeEventListener('abort', abortHandler)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function combineAbortSignals(parentSignal, timeoutSignal) {
|
|
70
|
+
if (!parentSignal) return timeoutSignal
|
|
71
|
+
if (typeof AbortSignal.any === 'function') return AbortSignal.any([parentSignal, timeoutSignal])
|
|
72
|
+
|
|
73
|
+
const controller = new AbortController()
|
|
74
|
+
const abort = () => controller.abort()
|
|
75
|
+
if (parentSignal.aborted || timeoutSignal.aborted) controller.abort()
|
|
76
|
+
else {
|
|
77
|
+
parentSignal.addEventListener('abort', abort, { once: true })
|
|
78
|
+
timeoutSignal.addEventListener('abort', abort, { once: true })
|
|
79
|
+
}
|
|
80
|
+
return controller.signal
|
|
81
|
+
}
|
|
82
|
+
|
|
32
83
|
async function exists(filePath) {
|
|
33
84
|
try {
|
|
34
85
|
await fs.access(filePath)
|
|
@@ -308,10 +359,19 @@ export async function callPluginTool(toolName, params = {}, toolContext = {}) {
|
|
|
308
359
|
throw error
|
|
309
360
|
}
|
|
310
361
|
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
362
|
+
const timeoutController = new AbortController()
|
|
363
|
+
const signal = combineAbortSignals(toolContext.signal, timeoutController.signal)
|
|
364
|
+
const result = await withTimeout(
|
|
365
|
+
Promise.resolve().then(() => handler.callTool(tool.name, params, {
|
|
366
|
+
...toolContext,
|
|
367
|
+
signal,
|
|
368
|
+
plugin: { name: plugin.name, dir: plugin.dir, config: plugin.config },
|
|
369
|
+
})),
|
|
370
|
+
DEFAULT_PLUGIN_TOOL_TIMEOUT_MS,
|
|
371
|
+
`Plugin tool ${toolName} timed out after ${DEFAULT_PLUGIN_TOOL_TIMEOUT_MS}ms`,
|
|
372
|
+
() => timeoutController.abort(),
|
|
373
|
+
toolContext.signal,
|
|
374
|
+
)
|
|
315
375
|
|
|
316
376
|
return {
|
|
317
377
|
isError: Boolean(result.isError),
|
package/server/public-api.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
|
+
import { createNodeProcessEnv } from './utils/process-env.mjs'
|
|
4
5
|
|
|
5
6
|
const __filename = fileURLToPath(import.meta.url)
|
|
6
7
|
const __dirname = path.dirname(__filename)
|
|
@@ -83,13 +84,16 @@ function buildEnv(options = {}) {
|
|
|
83
84
|
if (options.workspaceDir) env.QUICKFORGE_WORKSPACE_DIR = path.resolve(options.workspaceDir)
|
|
84
85
|
if (options.vitePort) env.QUICKFORGE_VITE_PORT = String(options.vitePort)
|
|
85
86
|
if (options.terminal === false) env.QUICKFORGE_TERMINAL = '0'
|
|
86
|
-
if (process.versions.electron) env.ELECTRON_RUN_AS_NODE = '1'
|
|
87
87
|
if (options.allowRemote || shareLan) env.QUICKFORGE_ALLOW_REMOTE = '1'
|
|
88
|
+
delete env.ELECTRON_RUN_AS_NODE
|
|
89
|
+
delete env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
|
88
90
|
|
|
89
91
|
return env
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
export function prepareQuickForgeEnv(options = {}) {
|
|
95
|
+
delete process.env.ELECTRON_RUN_AS_NODE
|
|
96
|
+
delete process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
|
93
97
|
Object.assign(process.env, buildEnv(options))
|
|
94
98
|
}
|
|
95
99
|
|
|
@@ -191,7 +195,7 @@ export async function startQuickForge(options = {}) {
|
|
|
191
195
|
stdio: options.stdio || 'ignore',
|
|
192
196
|
windowsHide: true,
|
|
193
197
|
shell: false,
|
|
194
|
-
env: buildEnv(options),
|
|
198
|
+
env: createNodeProcessEnv(buildEnv(options)),
|
|
195
199
|
})
|
|
196
200
|
|
|
197
201
|
let exitInfo = null
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { streamSimpleWithAiHttpLogging } from '../ai-http-logger.mjs'
|
|
2
2
|
import { DEFAULT_AI_MAX_RETRIES } from '../ai-provider-options.mjs'
|
|
3
3
|
import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
|
|
4
4
|
import { readStore } from '../storage.mjs'
|
|
@@ -90,7 +90,7 @@ Rules:
|
|
|
90
90
|
- If the user requests Chinese, write label, description, and systemPrompt in Chinese; otherwise match the user's language.`
|
|
91
91
|
|
|
92
92
|
try {
|
|
93
|
-
const stream =
|
|
93
|
+
const stream = streamSimpleWithAiHttpLogging(
|
|
94
94
|
model,
|
|
95
95
|
{
|
|
96
96
|
systemPrompt,
|