@shawnstack/quickforge 1.7.2 → 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 -0
- package/bin/quickforge.mjs +2 -21
- package/dist/assets/AgentProfilesPage-B84w-r9S.js +1 -0
- package/dist/assets/{ChatPanelHost-Cld9HcVz.js → ChatPanelHost-CyO8fEoj.js} +98 -98
- package/dist/assets/{PluginsPage-DEMqwhOA.js → PluginsPage-D2862wul.js} +1 -1
- package/dist/assets/{ScheduledTasksPage-DTP_gedp.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-Bp6kbIup.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-DiJAUfvW.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 +7 -1
- package/server/acp/server.mjs +14 -9
- package/server/agent-manager.mjs +215 -20
- package/server/ai-http-logger.mjs +69 -12
- package/server/ai-provider-options.mjs +11 -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 +43 -25
- package/server/lan-access-store.mjs +93 -3
- package/server/network-proxy.mjs +449 -0
- package/server/plugins/registry.mjs +64 -4
- package/server/public-api.mjs +10 -2
- package/server/routes/agent-profiles.mjs +4 -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 +7 -3
- package/server/routes/project.mjs +19 -1
- package/server/routes/scheduled-tasks.mjs +4 -2
- package/server/routes/static.mjs +26 -1
- package/server/routes/system.mjs +37 -0
- package/server/routes/terminal.mjs +7 -2
- package/server/routes/workspace.mjs +62 -9
- package/server/session-utils.mjs +4 -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-BFgyFa5e.js +0 -1
- package/dist/assets/SettingsWorkspacePage-Dr9zT5Bs.js +0 -1495
- package/dist/assets/SharedConversationPage-op3DRwcw.js +0 -1
- package/dist/assets/TerminalDock-BL_UovwU.js +0 -2
- package/dist/assets/WorkspaceInspector-73dhAime.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-Cu2bBHLv.css +0 -3
- package/dist/assets/index-yV1wtqTr.js +0 -66
- 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
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import tls from 'node:tls'
|
|
2
|
+
import { Agent, DecoratorHandler, Dispatcher, ProxyAgent } from 'undici'
|
|
3
|
+
import { SocksClient } from 'socks'
|
|
4
|
+
import { atomicUpdate, readStore } from './storage.mjs'
|
|
5
|
+
|
|
6
|
+
const SETTINGS_KEY = 'network-proxy'
|
|
7
|
+
const VALID_MODES = new Set(['direct', 'system', 'manual', 'pac'])
|
|
8
|
+
const PROXY_ENV_KEYS = [
|
|
9
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY',
|
|
10
|
+
'http_proxy', 'https_proxy', 'all_proxy', 'no_proxy',
|
|
11
|
+
]
|
|
12
|
+
const ORIGINAL_FETCH = globalThis.fetch.bind(globalThis)
|
|
13
|
+
const FETCH_PATCH_MARKER = Symbol.for('quickforge.networkProxy.fetchPatched')
|
|
14
|
+
|
|
15
|
+
let currentConfig = { mode: 'direct', proxyUrl: '' }
|
|
16
|
+
let hostRuntime = null
|
|
17
|
+
let nativeResolver = null
|
|
18
|
+
let nativeResolverError = null
|
|
19
|
+
let networkDispatcher = null
|
|
20
|
+
let initialized = false
|
|
21
|
+
let lastAppliedAt = null
|
|
22
|
+
|
|
23
|
+
export function normalizeNetworkProxyConfig(value) {
|
|
24
|
+
const mode = VALID_MODES.has(value?.mode) ? value.mode : 'direct'
|
|
25
|
+
const proxyUrl = typeof value?.proxyUrl === 'string' ? value.proxyUrl.trim() : ''
|
|
26
|
+
return { mode, proxyUrl }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function validateManualProxyUrl(value) {
|
|
30
|
+
let url
|
|
31
|
+
try {
|
|
32
|
+
url = new URL(String(value || '').trim())
|
|
33
|
+
} catch {
|
|
34
|
+
const error = new Error('Proxy address must be a valid HTTP or HTTPS URL')
|
|
35
|
+
error.statusCode = 400
|
|
36
|
+
throw error
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
40
|
+
const error = new Error('Only HTTP and HTTPS proxy addresses are supported')
|
|
41
|
+
error.statusCode = 400
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
44
|
+
if (!url.hostname || !url.port) {
|
|
45
|
+
const error = new Error('Proxy address must include a host and port')
|
|
46
|
+
error.statusCode = 400
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
if (url.username || url.password) {
|
|
50
|
+
const error = new Error('Proxy credentials are not supported in the proxy address')
|
|
51
|
+
error.statusCode = 400
|
|
52
|
+
throw error
|
|
53
|
+
}
|
|
54
|
+
if ((url.pathname && url.pathname !== '/') || url.search || url.hash) {
|
|
55
|
+
const error = new Error('Proxy address cannot include a path, query, or fragment')
|
|
56
|
+
error.statusCode = 400
|
|
57
|
+
throw error
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return `${url.protocol}//${url.host}`
|
|
61
|
+
}
|
|
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
|
+
|
|
97
|
+
function isLoopbackUrl(input) {
|
|
98
|
+
let url
|
|
99
|
+
try {
|
|
100
|
+
if (typeof Request !== 'undefined' && input instanceof Request) url = new URL(input.url)
|
|
101
|
+
else url = new URL(input instanceof URL ? input.href : String(input))
|
|
102
|
+
} catch {
|
|
103
|
+
return false
|
|
104
|
+
}
|
|
105
|
+
return ['localhost', '127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(url.hostname)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function withoutProxyEnvironment(callback) {
|
|
109
|
+
const previous = new Map()
|
|
110
|
+
for (const key of PROXY_ENV_KEYS) {
|
|
111
|
+
previous.set(key, process.env[key])
|
|
112
|
+
delete process.env[key]
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
return callback()
|
|
116
|
+
} finally {
|
|
117
|
+
for (const [key, value] of previous) {
|
|
118
|
+
if (value === undefined) delete process.env[key]
|
|
119
|
+
else process.env[key] = value
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function getNativeResolver() {
|
|
125
|
+
if (nativeResolver) return nativeResolver
|
|
126
|
+
if (nativeResolverError) throw nativeResolverError
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const { ProxyResolver } = await import('@vscode/os-proxy-resolver')
|
|
130
|
+
nativeResolver = withoutProxyEnvironment(() => new ProxyResolver())
|
|
131
|
+
return nativeResolver
|
|
132
|
+
} catch (error) {
|
|
133
|
+
nativeResolverError = error instanceof Error ? error : new Error(String(error))
|
|
134
|
+
throw nativeResolverError
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function parseProxyHost(value) {
|
|
139
|
+
const raw = String(value || '').trim()
|
|
140
|
+
const url = new URL(raw.includes('://') ? raw : `http://${raw}`)
|
|
141
|
+
return {
|
|
142
|
+
host: url.hostname,
|
|
143
|
+
port: Number(url.port || (url.protocol === 'https:' ? 443 : 80)),
|
|
144
|
+
secure: url.protocol === 'https:',
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function createSocksConnector(proxyHost) {
|
|
149
|
+
const proxy = parseProxyHost(proxyHost)
|
|
150
|
+
return (options, callback) => {
|
|
151
|
+
const destinationHost = options.hostname || options.host
|
|
152
|
+
const destinationPort = Number(options.port || (options.protocol === 'https:' ? 443 : 80))
|
|
153
|
+
let settled = false
|
|
154
|
+
|
|
155
|
+
const complete = (error, socket) => {
|
|
156
|
+
if (settled) return
|
|
157
|
+
settled = true
|
|
158
|
+
callback(error, socket)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
SocksClient.createConnection({
|
|
162
|
+
proxy: {
|
|
163
|
+
host: proxy.host,
|
|
164
|
+
port: proxy.port,
|
|
165
|
+
type: 5,
|
|
166
|
+
},
|
|
167
|
+
command: 'connect',
|
|
168
|
+
destination: {
|
|
169
|
+
host: destinationHost,
|
|
170
|
+
port: destinationPort,
|
|
171
|
+
},
|
|
172
|
+
timeout: Number(options.timeout || 10000),
|
|
173
|
+
}).then(({ socket }) => {
|
|
174
|
+
if (options.protocol !== 'https:') {
|
|
175
|
+
complete(null, socket)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
const secureSocket = tls.connect({
|
|
179
|
+
socket,
|
|
180
|
+
servername: options.servername || destinationHost,
|
|
181
|
+
ALPNProtocols: ['http/1.1'],
|
|
182
|
+
})
|
|
183
|
+
secureSocket.once('secureConnect', () => complete(null, secureSocket))
|
|
184
|
+
secureSocket.once('error', (error) => complete(error))
|
|
185
|
+
}).catch((error) => complete(error))
|
|
186
|
+
|
|
187
|
+
return () => {
|
|
188
|
+
settled = true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class RetryHandler extends DecoratorHandler {
|
|
194
|
+
constructor(dispatcher, options, handler, retry) {
|
|
195
|
+
super(handler)
|
|
196
|
+
this.dispatcher = dispatcher
|
|
197
|
+
this.options = options
|
|
198
|
+
this.retry = retry
|
|
199
|
+
this.receivedResponse = false
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
onResponseStart(controller, statusCode, headers, statusMessage) {
|
|
203
|
+
this.receivedResponse = true
|
|
204
|
+
return super.onResponseStart(controller, statusCode, headers, statusMessage)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
onResponseError(controller, error) {
|
|
208
|
+
if (!this.receivedResponse) {
|
|
209
|
+
void this.retry(error)
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
return super.onResponseError(controller, error)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
class SystemProxyDispatcher extends Dispatcher {
|
|
217
|
+
constructor(resolver) {
|
|
218
|
+
super()
|
|
219
|
+
this.resolver = resolver
|
|
220
|
+
this.directAgent = new Agent()
|
|
221
|
+
this.dispatchers = new Map()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
dispatcherFor(route) {
|
|
225
|
+
if (!route || route.kind === 'direct') return this.directAgent
|
|
226
|
+
const key = `${route.kind}:${route.host}`
|
|
227
|
+
if (this.dispatchers.has(key)) return this.dispatchers.get(key)
|
|
228
|
+
|
|
229
|
+
let dispatcher
|
|
230
|
+
if (route.kind === 'http') {
|
|
231
|
+
const proxy = parseProxyHost(route.host)
|
|
232
|
+
dispatcher = new ProxyAgent({ uri: `${proxy.secure ? 'https' : 'http'}://${proxy.host}:${proxy.port}` })
|
|
233
|
+
} else if (route.kind === 'socks') {
|
|
234
|
+
dispatcher = new Agent({ connect: createSocksConnector(route.host) })
|
|
235
|
+
} else {
|
|
236
|
+
dispatcher = this.directAgent
|
|
237
|
+
}
|
|
238
|
+
this.dispatchers.set(key, dispatcher)
|
|
239
|
+
return dispatcher
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
dispatch(options, handler) {
|
|
243
|
+
const url = `${options.origin}${options.path}`
|
|
244
|
+
let routes = []
|
|
245
|
+
let index = 0
|
|
246
|
+
|
|
247
|
+
const tryNext = async (lastError) => {
|
|
248
|
+
if (index >= routes.length) {
|
|
249
|
+
handler.onResponseError?.(null, lastError || new Error('No usable system proxy route'))
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
const route = routes[index]
|
|
253
|
+
index += 1
|
|
254
|
+
const dispatcher = this.dispatcherFor(route)
|
|
255
|
+
const retryHandler = new RetryHandler(dispatcher, options, handler, async (error) => {
|
|
256
|
+
if (route?.kind !== 'direct') this.resolver.reportProxyFailed(route)
|
|
257
|
+
await tryNext(error)
|
|
258
|
+
})
|
|
259
|
+
try {
|
|
260
|
+
dispatcher.dispatch(options, retryHandler)
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (route?.kind !== 'direct') this.resolver.reportProxyFailed(route)
|
|
263
|
+
await tryNext(error)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
void this.resolver.resolve(url).then((resolvedRoutes) => {
|
|
268
|
+
routes = Array.isArray(resolvedRoutes) && resolvedRoutes.length ? resolvedRoutes : [{ kind: 'direct' }]
|
|
269
|
+
return tryNext()
|
|
270
|
+
}).catch((error) => handler.onResponseError?.(null, error))
|
|
271
|
+
|
|
272
|
+
return true
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async close() {
|
|
276
|
+
const dispatchers = [this.directAgent, ...this.dispatchers.values()]
|
|
277
|
+
await Promise.allSettled(dispatchers.map((dispatcher) => dispatcher.close()))
|
|
278
|
+
this.dispatchers.clear()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async destroy(error) {
|
|
282
|
+
const dispatchers = [this.directAgent, ...this.dispatchers.values()]
|
|
283
|
+
await Promise.allSettled(dispatchers.map((dispatcher) => dispatcher.destroy(error)))
|
|
284
|
+
this.dispatchers.clear()
|
|
285
|
+
}
|
|
286
|
+
}
|
|
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
|
+
|
|
305
|
+
async function createNetworkDispatcher(config) {
|
|
306
|
+
if (config.mode === 'manual') return new ProxyAgent(config.proxyUrl)
|
|
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')
|
|
309
|
+
return new Agent()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function replaceNetworkDispatcher(config) {
|
|
313
|
+
const previous = networkDispatcher
|
|
314
|
+
networkDispatcher = await createNetworkDispatcher(config)
|
|
315
|
+
if (previous) void previous.close().catch(() => {})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function installNetworkFetch() {
|
|
319
|
+
if (globalThis[FETCH_PATCH_MARKER]) return
|
|
320
|
+
globalThis.fetch = async (input, init = {}) => {
|
|
321
|
+
if (isLoopbackUrl(input)) return ORIGINAL_FETCH(input, init)
|
|
322
|
+
if (hostRuntime) {
|
|
323
|
+
if (currentConfig.mode === 'direct') return ORIGINAL_FETCH(input, init)
|
|
324
|
+
return hostRuntime.fetch(input, init)
|
|
325
|
+
}
|
|
326
|
+
if (!networkDispatcher) return ORIGINAL_FETCH(input, init)
|
|
327
|
+
return ORIGINAL_FETCH(input, { ...init, dispatcher: networkDispatcher })
|
|
328
|
+
}
|
|
329
|
+
globalThis[FETCH_PATCH_MARKER] = true
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function registerHostNetworkRuntime(runtime) {
|
|
333
|
+
hostRuntime = runtime || null
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function persistConfig(config) {
|
|
337
|
+
await atomicUpdate('settings', (settings) => ({
|
|
338
|
+
...settings,
|
|
339
|
+
[SETTINGS_KEY]: config,
|
|
340
|
+
}))
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function applyConfig(config) {
|
|
344
|
+
const normalized = normalizeNetworkProxyConfig(config)
|
|
345
|
+
if (normalized.mode === 'manual') normalized.proxyUrl = validateManualProxyUrl(normalized.proxyUrl)
|
|
346
|
+
if (normalized.mode === 'pac') normalized.proxyUrl = validatePacUrl(normalized.proxyUrl)
|
|
347
|
+
|
|
348
|
+
if (hostRuntime) await hostRuntime.apply(normalized)
|
|
349
|
+
else await replaceNetworkDispatcher(normalized)
|
|
350
|
+
|
|
351
|
+
currentConfig = normalized
|
|
352
|
+
lastAppliedAt = new Date().toISOString()
|
|
353
|
+
return currentConfig
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function nativeStatus() {
|
|
357
|
+
try {
|
|
358
|
+
const resolver = await getNativeResolver()
|
|
359
|
+
const config = await resolver.readProxyConfig()
|
|
360
|
+
const source = config.platform?.kind || process.platform
|
|
361
|
+
return {
|
|
362
|
+
supported: true,
|
|
363
|
+
source,
|
|
364
|
+
features: {
|
|
365
|
+
pac: config.configuredPac?.state !== 'unsupported',
|
|
366
|
+
pacUrl: false,
|
|
367
|
+
wpad: config.wpadDns?.state !== 'unsupported' || config.wpadDhcp?.state !== 'unsupported',
|
|
368
|
+
httpProxy: true,
|
|
369
|
+
httpsProxy: true,
|
|
370
|
+
socks: true,
|
|
371
|
+
},
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
return {
|
|
375
|
+
supported: false,
|
|
376
|
+
source: 'none',
|
|
377
|
+
features: { pac: false, pacUrl: false, wpad: false, httpProxy: false, httpsProxy: false, socks: false },
|
|
378
|
+
error: error instanceof Error ? error.message : String(error),
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export async function getNetworkProxyStatus() {
|
|
384
|
+
const runtimeStatus = hostRuntime
|
|
385
|
+
? await hostRuntime.getStatus()
|
|
386
|
+
: await nativeStatus()
|
|
387
|
+
const configuredMode = currentConfig.mode
|
|
388
|
+
const modeSupported = configuredMode === 'system'
|
|
389
|
+
? runtimeStatus.supported
|
|
390
|
+
: configuredMode !== 'pac' || runtimeStatus.features?.pacUrl === true
|
|
391
|
+
return {
|
|
392
|
+
configuredMode,
|
|
393
|
+
effectiveMode: modeSupported ? configuredMode : 'unsupported',
|
|
394
|
+
proxyUrl: currentConfig.proxyUrl,
|
|
395
|
+
runtimeKind: hostRuntime ? 'electron-inline' : (process.versions.electron ? 'electron-node' : 'node'),
|
|
396
|
+
lastAppliedAt,
|
|
397
|
+
...runtimeStatus,
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export async function initializeNetworkProxy() {
|
|
402
|
+
if (initialized) return { config: currentConfig, status: await getNetworkProxyStatus() }
|
|
403
|
+
installNetworkFetch()
|
|
404
|
+
const settings = await readStore('settings')
|
|
405
|
+
await applyConfig(settings?.[SETTINGS_KEY])
|
|
406
|
+
initialized = true
|
|
407
|
+
return { config: currentConfig, status: await getNetworkProxyStatus() }
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export async function getNetworkProxyConfig() {
|
|
411
|
+
if (!initialized) await initializeNetworkProxy()
|
|
412
|
+
return { config: currentConfig, status: await getNetworkProxyStatus() }
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export async function updateNetworkProxyConfig(value) {
|
|
416
|
+
if (!initialized) await initializeNetworkProxy()
|
|
417
|
+
const previous = currentConfig
|
|
418
|
+
const next = normalizeNetworkProxyConfig(value)
|
|
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
|
+
}
|
|
429
|
+
|
|
430
|
+
await applyConfig(next)
|
|
431
|
+
try {
|
|
432
|
+
await persistConfig(currentConfig)
|
|
433
|
+
} catch (error) {
|
|
434
|
+
await applyConfig(previous)
|
|
435
|
+
throw error
|
|
436
|
+
}
|
|
437
|
+
return { config: currentConfig, status: await getNetworkProxyStatus() }
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export async function refreshSystemProxy() {
|
|
441
|
+
if (!initialized) await initializeNetworkProxy()
|
|
442
|
+
nativeResolver?.close?.()
|
|
443
|
+
nativeResolver = null
|
|
444
|
+
nativeResolverError = null
|
|
445
|
+
if (hostRuntime) await hostRuntime.refresh()
|
|
446
|
+
else await replaceNetworkDispatcher(currentConfig)
|
|
447
|
+
lastAppliedAt = new Date().toISOString()
|
|
448
|
+
return { config: currentConfig, status: await getNetworkProxyStatus() }
|
|
449
|
+
}
|
|
@@ -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
|
|
|
@@ -140,6 +144,10 @@ async function waitForQuickForge(options = {}) {
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
export async function startQuickForge(options = {}) {
|
|
147
|
+
if (options.networkRuntime) {
|
|
148
|
+
const { registerHostNetworkRuntime } = await import('./network-proxy.mjs')
|
|
149
|
+
registerHostNetworkRuntime(options.networkRuntime)
|
|
150
|
+
}
|
|
143
151
|
const existingHealth = options.reuseExisting === false ? null : await checkQuickForgeHealth(options)
|
|
144
152
|
const url = getQuickForgeUrl(options)
|
|
145
153
|
const healthUrl = getQuickForgeHealthUrl(options)
|
|
@@ -187,7 +195,7 @@ export async function startQuickForge(options = {}) {
|
|
|
187
195
|
stdio: options.stdio || 'ignore',
|
|
188
196
|
windowsHide: true,
|
|
189
197
|
shell: false,
|
|
190
|
-
env: buildEnv(options),
|
|
198
|
+
env: createNodeProcessEnv(buildEnv(options)),
|
|
191
199
|
})
|
|
192
200
|
|
|
193
201
|
let exitInfo = null
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { streamSimpleWithAiHttpLogging } from '../ai-http-logger.mjs'
|
|
2
|
+
import { DEFAULT_AI_MAX_RETRIES } from '../ai-provider-options.mjs'
|
|
2
3
|
import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
|
|
3
4
|
import { readStore } from '../storage.mjs'
|
|
4
5
|
import { logger } from '../utils/logger.mjs'
|
|
@@ -89,7 +90,7 @@ Rules:
|
|
|
89
90
|
- If the user requests Chinese, write label, description, and systemPrompt in Chinese; otherwise match the user's language.`
|
|
90
91
|
|
|
91
92
|
try {
|
|
92
|
-
const stream =
|
|
93
|
+
const stream = streamSimpleWithAiHttpLogging(
|
|
93
94
|
model,
|
|
94
95
|
{
|
|
95
96
|
systemPrompt,
|
|
@@ -101,6 +102,7 @@ Rules:
|
|
|
101
102
|
maxTokens: 1600,
|
|
102
103
|
temperature: 0,
|
|
103
104
|
reasoning: thinkingLevel === 'off' ? undefined : thinkingLevel,
|
|
105
|
+
maxRetries: DEFAULT_AI_MAX_RETRIES,
|
|
104
106
|
maxRetryDelayMs: 60000,
|
|
105
107
|
},
|
|
106
108
|
)
|
package/server/routes/agent.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
|
|
2
|
+
import { logger } from '../utils/logger.mjs'
|
|
2
3
|
import {
|
|
3
4
|
createAgent,
|
|
4
5
|
runPrompt,
|
|
@@ -96,6 +97,30 @@ export async function handleAgentApi(req, res, url) {
|
|
|
96
97
|
return
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
// POST /api/agents/:sessionId/restore — restore once and return the authoritative state
|
|
101
|
+
if (req.method === 'POST' && subPath === 'restore') {
|
|
102
|
+
const startedAt = performance.now()
|
|
103
|
+
const session = await restoreAgent(sessionId)
|
|
104
|
+
if (!session) {
|
|
105
|
+
const error = new Error('Session not found')
|
|
106
|
+
error.statusCode = 404
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
109
|
+
const state = getSessionState(sessionId)
|
|
110
|
+
if (!state) {
|
|
111
|
+
const error = new Error('Session not found')
|
|
112
|
+
error.statusCode = 404
|
|
113
|
+
throw error
|
|
114
|
+
}
|
|
115
|
+
logger.debug(`Restored session ${sessionId} for client`, {
|
|
116
|
+
sessionId,
|
|
117
|
+
messageCount: state.messages?.length ?? 0,
|
|
118
|
+
durationMs: Math.round((performance.now() - startedAt) * 10) / 10,
|
|
119
|
+
})
|
|
120
|
+
sendJson(res, 200, state)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
99
124
|
// GET /api/agents/:sessionId/state — get session state
|
|
100
125
|
if (req.method === 'GET' && subPath === 'state') {
|
|
101
126
|
let state = getSessionState(sessionId)
|
|
@@ -141,6 +166,9 @@ export async function handleAgentApi(req, res, url) {
|
|
|
141
166
|
status: session.status,
|
|
142
167
|
scope: session.scope,
|
|
143
168
|
title: session.title,
|
|
169
|
+
source: session.source || undefined,
|
|
170
|
+
channelId: session.channelId || undefined,
|
|
171
|
+
channelName: session.channelName || undefined,
|
|
144
172
|
accessMode: session.accessMode,
|
|
145
173
|
yoloMode: session.yoloMode,
|
|
146
174
|
})
|