@shawnstack/quickforge 1.7.4 → 1.7.6
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/assets/AgentProfilesPage-Dl5Peqov.js +1 -0
- package/dist/assets/ChatPanelHost-D3987TUJ.js +307 -0
- package/dist/assets/CloudAccountSettingsPage-BG5Nn3bq.js +1 -0
- package/dist/assets/PluginsPage-BDKYfLna.js +1 -0
- package/dist/assets/ScheduledTasksPage--GAki3lB.js +2 -0
- package/dist/assets/SettingsWorkspacePage-DSy66Tuw.js +1696 -0
- package/dist/assets/ShareLinksSettingsPage-BQQMsvJo.js +1 -0
- package/dist/assets/SharedConversationPage-B9BUVCri.js +1 -0
- package/dist/assets/TerminalDock-CT1nYo7P.js +2 -0
- package/dist/assets/WorkspaceInspector-BnfDLf9a.js +36 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-Dy7veffk.js → abnfDiagram-VRR7QNED-CMhVoYUh.js} +1 -1
- package/dist/assets/{arc-Bo3p2lGy.js → arc-w21O0TLn.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-DMwFMHxq.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-Cwx8qOvu.js → architectureDiagram-ZJ3FMSHR-C52qL6Hg.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-BiuaAOFa.js → blockDiagram-677ZJIJ3-CM3XbLol.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-BTcGfhK2.js → c4Diagram-LMCZKHZV-D7Bh1ui1.js} +1 -1
- package/dist/assets/channel-Duu1CuH9.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-B6rHWc4S.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-B9PuF_RF.js → chunk-32BRIVSS-xgrtbT8e.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-3zGItfIM.js → chunk-52WLFC77-CDbaHw80.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-DQ66qlmt.js → chunk-5FCAYU7R-CcSvVbPc.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-CLqTO-VF.js → chunk-5HE753X5-erFpzzgx.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-Csxv1jr6.js → chunk-5JV3BV7I-rEhN9F6G.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-BCQttpBr.js → chunk-5TONJI2A-D8L-QTwD.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DEWJGSTb.js → chunk-5VM5RSS4-CjlfMBbM.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-DNorm9MM.js → chunk-7BUUIJ7U-CCcsI9XE.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-DgvY_BOT.js → chunk-BIQX33UG-FSqFbmb3.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-DMvn9IPs.js → chunk-C7G6YPKG-ZMLUvfLy.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-Dq2duUgr.js → chunk-CQNSW5MT-DZuBpk25.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-BovYyc4Y.js → chunk-CYSBUYHQ-CB26wsG0.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-BMDOpuYN.js → chunk-EMLP6XTP-BbsM8oRw.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-6eP2UPOf.js → chunk-EX3LRPZG-BcQhaqt7.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-D4q-wTSy.js → chunk-FWX5IMBZ-DrnrdrSu.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-XjIcuzH7.js → chunk-HOUHSVGY-tdc-Z9eW.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-YBXbanq9.js → chunk-ICXQ74PX-Cd7igVGt.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-CcN3uNXF.js → chunk-JG7HCLWE-iXXGW8nf.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-Cwg6AUAt.js → chunk-JWPE2WC7-BZ87mXEv.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-NNspcGIU.js → chunk-MOJQB5TN-CLTjV5ov.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-pMJ_jMos.js → chunk-MOZMSUNE-B3SCBrRH.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-CJy-3Ldv.js → chunk-OGEWGWER-TgvkXkFX.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-D7cLHHOA.js → chunk-OSBZ3O6U-B9E2E5v1.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-D5YBi1Pi.js → chunk-PUDLZKDR-C5GMVYup.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-DYmqQdIf.js → chunk-Q4XR5HBZ-fo5-HsqY.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-Bfcx00X0.js → chunk-QBLGF6JB-Dwfg8sKc.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-oXzadpZY.js → chunk-R7FJI6CG-DpBrv4ni.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-Dt3-8yUh.js → chunk-RYQCIY6F-DzzmTl11.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-B55eSvpx.js → chunk-U6XO7XAA-Dw6aviKa.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-Btk4JwCt.js → chunk-V7JOEXUC-CEDazV6w.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CWYjg8zE.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-DgaSiVOY.js → chunk-VR4S4FIN-C_-eEj-9.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-rRDZ_Tta.js → chunk-WYO6CB5R-NF-MsJJR.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-DzTwx8ZH.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-BnFxj5o4.js → chunk-YOTPTUD7-DqzqtdII.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-CrnlPlmJ.js → chunk-ZGVPDNZ5-CRrAYi4g.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-BYTiATIH.js → chunk-ZIRB5QZD-Dc1s5bLM.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Cc63fLWP.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-CoXU0qT6.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-BORKJRwF.js → cose-bilkent-JH36ORCC-cL247pHg.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-C8yCvH_o.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-BXKeaSl8.js → cynefinDiagram-TSTJHNR4-D4sZ7LYI.js} +1 -1
- package/dist/assets/{dagre-CrA0XVwJ.js → dagre-DmHb3Ctt.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-ouq_lA1a.js → dagre-VKFMJZFB-BlsnSh-Q.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BxR5kUiQ.js → diagram-FQU43EPY-DW1yQiRO.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-DWonk1sL.js → diagram-G47NLZAW-BTUAsFjb.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-fmV_wV5e.js → diagram-NH7WQ7WH-BcaomqSd.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-DvOSRRiV.js → diagram-OA4YK3LP-DDLX7Xwr.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-Bp-xb3uS.js → diagram-WEI45ONY-CIave0An.js} +1 -1
- package/dist/assets/{dist-DLKC_odd.js → dist-yDHbi5ux.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-BTlnwneA.js → ebnfDiagram-CCIWWBDH-Cr1Y6lPu.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-C3fFWGCm.js → erDiagram-Q63AITRT-D3EgiVVH.js} +1 -1
- package/dist/assets/{esm-oa69b5u4.js → esm-sdsK6gaB.js} +2 -2
- package/dist/assets/eventmodeling-45OFAUF4-DO1vRTM0.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-BS7KXcqn.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-DzAiojNi.js → ganttDiagram-NO4QXBWP-B0oyDIqK.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-m5F5ef09.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-Cv57oNEA.js → gitGraphDiagram-IHSO6WYX-uQtr6IqK.js} +1 -1
- package/dist/assets/icons-2D18YbSI.js +1 -0
- package/dist/assets/index-1ei6nhs-.css +3 -0
- package/dist/assets/index-BivLuZvX.js +65 -0
- package/dist/assets/info-DKCQHKI2-CZxPlQJ0.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-B5Z_O3Ro.js → infoDiagram-FWYZ7A6U-B_GzlLIM.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-CKTK_TKV.js → ishikawaDiagram-FXEZZL3T-CKQIsSdU.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-Cm9k4ywW.js → journeyDiagram-5HDEW3XC-DrrvzZL1.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-DzNteLwx.js → kanban-definition-HUTT4EX6-CJQRTdXz.js} +1 -1
- package/dist/assets/{line-BG78c_WJ.js → line-B4DxX7tC.js} +1 -1
- package/dist/assets/{linear-1X20_EGu.js → linear-U-nKUhyW.js} +1 -1
- package/dist/assets/{map-CCI8WZkS.js → map-BneTM-ri.js} +1 -1
- package/dist/assets/{mcp-servers-dialog-GXXD9K0U.js → mcp-servers-dialog-BEV4_TXC.js} +2 -2
- package/dist/assets/{mermaid-parser.core-Bb8HI_OS.js → mermaid-parser.core-BlKWrEdZ.js} +2 -2
- package/dist/assets/{mermaid-renderer-PIaNiXAz.js → mermaid-renderer-jEbqFaTe.js} +2 -2
- package/dist/assets/{mermaid.core-DUHOIwL5.js → mermaid.core-DWpShXCs.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-Tnbszo7b.js → mindmap-definition-LN4V7U3C-CEVwZD78.js} +1 -1
- package/dist/assets/{logger-8REuLPGK.js → model-display-label-C6-0eDxF.js} +1 -1
- package/dist/assets/model-identity-CgA6_Pbm.js +1 -0
- package/dist/assets/model-reference-o41fJ6jQ.js +1 -0
- package/dist/assets/{monaco-BVtCspYZ.js → monaco-DvXErxp2.js} +1 -1
- package/dist/assets/{ordinal-v4prQcuq.js → ordinal-B2r5L1v4.js} +1 -1
- package/dist/assets/{otel-D1LswXpM.js → otel-BFnphzjt.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-zIioIZVN.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-CX824Kd0.js → pegDiagram-2B236MQR-CBLkBdnQ.js} +1 -1
- package/dist/assets/{pi-ai-DAJlO9ZB.js → pi-ai-Cr2ee4a0.js} +2 -2
- package/dist/assets/{pi-web-ui-B-RJSxjR.js → pi-web-ui-DV5jKjSg.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-GGWijBvB.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-BT04VWPQ.js → pieDiagram-ENE6RG2P-8tCnwtWS.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BvOZxkBr.js → quadrantDiagram-ABIIQ3AL-CWd28cHF.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-yvwFH3RV.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BVadlswB.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-B7R-p_2-.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-BnbJxPug.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-Ci3DcEuQ.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-DnTU--BV.js → railroadDiagram-RFXS5EU6-CI4ouOBs.js} +1 -1
- package/dist/assets/{react-vendor-eWH0QhNU.js → react-vendor-3UaA-C9W.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE--MZX_yEv.js → requirementDiagram-TGXJPOKE-DtFz7sw4.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-SEZkHhYn.js → sankeyDiagram-HTMAVEWB--VeOwwk9.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BhC2OHiz.js → sequenceDiagram-DBY2YBRQ-B8AHk5Oe.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-Ci5LeToV.js → sizeCapture-X5ZJPWSS-vV_ArM8D.js} +1 -1
- package/dist/assets/{skills-dialog-DDh1h8eH.js → skills-dialog-BTJ7mzk_.js} +3 -3
- package/dist/assets/{src-CovulcRC.js → src-PYsDme0X.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-DKK5tFT4.js → stateDiagram-2N3HPSRC-etXnJ8X8.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-Dms3pICj.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-DxxGCk67.js → swimlanes-5IMT3BWC-pabIKAmn.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-B14aWjv6.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-BPXB3Sue.js → timeline-definition-FHXFAJF6-C55MKm3L.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-Cru9B2zv.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-DNOVHFaJ.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CxcBCKye.js → vennDiagram-L72KCM5P-CgNf1Q9W.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-E3m1BOJQ.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-DEL6Yes5.js → wardleyDiagram-EHGQE667-Drt5ytec.js} +1 -1
- package/dist/assets/{web-RgX7H-OH.js → web-B5qgZJRv.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-N8cPbcjC.js → xychartDiagram-FW5EYKEG-hhoEo_Ar.js} +1 -1
- package/dist/index.html +11 -10
- package/dist/sw.js +17 -0
- package/package.json +4 -1
- package/plugins/{openai-documents → documents}/plugin.json +2 -2
- package/plugins/{openai-presentations → presentations}/plugin.json +2 -2
- package/plugins/{openai-spreadsheets → spreadsheets}/plugin.json +2 -2
- package/runtime-assets/agent/darwin-arm64/qf-agent +0 -0
- package/runtime-assets/agent/darwin-x64/qf-agent +0 -0
- package/runtime-assets/agent/linux-arm64/qf-agent +0 -0
- package/runtime-assets/agent/linux-x64/qf-agent +0 -0
- package/runtime-assets/agent/win32-x64/qf-agent.exe +0 -0
- package/server/access-policy.mjs +11 -0
- package/server/acp/server.mjs +36 -26
- package/server/agent-manager.mjs +474 -71
- package/server/agent-profile-files.mjs +8 -4
- package/server/agent-profile-schema.mjs +47 -23
- package/server/agent-profiles.mjs +28 -9
- package/server/ai-http-logger.mjs +98 -12
- package/server/auto-compaction.mjs +35 -8
- package/server/cloud/chat-idempotency-store.mjs +96 -0
- package/server/cloud/client.mjs +99 -0
- package/server/cloud/config.mjs +54 -0
- package/server/cloud/credential-store.mjs +200 -0
- package/server/cloud/identity.mjs +312 -0
- package/server/cloud/index.mjs +7 -0
- package/server/cloud/models.mjs +80 -0
- package/server/cloud/qf-agent-process.mjs +571 -0
- package/server/cloud/runtime.mjs +135 -0
- package/server/cloud/service-config.mjs +81 -0
- package/server/index.mjs +124 -48
- package/server/mcp/registry.mjs +8 -7
- package/server/message-converters.mjs +40 -19
- package/server/model-catalog.mjs +269 -0
- package/server/opencode-acp-agent.mjs +1151 -0
- package/server/plugins/registry.mjs +29 -5
- package/server/public-api.mjs +11 -5
- package/server/routes/agent-profiles.mjs +46 -9
- package/server/routes/agent.mjs +60 -11
- package/server/routes/backup.mjs +8 -1
- package/server/routes/cloud.mjs +246 -0
- package/server/routes/models.mjs +7 -1
- package/server/routes/scheduled-tasks.mjs +38 -8
- package/server/routes/shared-conversation.mjs +62 -58
- package/server/routes/shares.mjs +75 -1
- package/server/routes/storage.mjs +9 -1
- package/server/routes/system.mjs +7 -4
- package/server/routes/workspace.mjs +6 -4
- package/server/share-store.mjs +168 -13
- package/server/skills.mjs +30 -3
- package/server/storage.mjs +7 -1
- package/server/subagents.mjs +3 -3
- package/server/tools/definitions.mjs +1 -1
- package/server/tools/index.mjs +2 -2
- package/server/utils/network.mjs +9 -0
- package/server/utils/process-tree.mjs +78 -0
- package/server/utils/response.mjs +8 -1
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/UPSTREAM.md +6 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/dist/assets/AgentProfilesPage-B84w-r9S.js +0 -1
- package/dist/assets/ChatPanelHost-CyO8fEoj.js +0 -288
- package/dist/assets/PluginsPage-D2862wul.js +0 -1
- package/dist/assets/ScheduledTasksPage-GqbiJDPX.js +0 -2
- package/dist/assets/SettingsWorkspacePage-B53_eDf2.js +0 -1628
- package/dist/assets/SharedConversationPage-Cw2QFnbC.js +0 -1
- package/dist/assets/TerminalDock-Dgg-_ZmU.js +0 -2
- package/dist/assets/WorkspaceInspector-3uLiu6Bv.js +0 -13
- package/dist/assets/architecture-TIHT7OUA-7FQo5GbP.js +0 -1
- package/dist/assets/channel-DToA8d-4.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-D_Wxpk-T.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-CIZseNLi.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-Do-XYxat.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Bu24Sy-k.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-DKhh5HSW.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-oyiAgYC4.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-YB_0MefX.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-Bc73xdBd.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-Bvp27L0n.js +0 -1
- package/dist/assets/icons-cTkqD_Gi.js +0 -1
- package/dist/assets/index-B48prgLL.js +0 -65
- package/dist/assets/index-DQhwHHer.css +0 -3
- package/dist/assets/info-DKCQHKI2-BsLOivgq.js +0 -1
- package/dist/assets/packet-7NZHBO7P-BKQkXRtk.js +0 -1
- package/dist/assets/pie-RZYD4A2V-BTiTN5Ge.js +0 -1
- package/dist/assets/radar-I7S5WNFK-BU-oVjKM.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-7H2hxNbL.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-Mx6wf6XQ.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-BgXSKhYy.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-DTEl2rAC.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-D8x3mkni.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-C4nOBNp8.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-DOE6NSdf.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-Ca7QD_b4.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-D9O4RKRm.js +0 -1
- /package/dist/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-s7Qq-R3i.js → chunk-KEIR6QF5-jrz1pRUS.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-BgISNSiw.js → chunk-Y2CYZVJY-D6N-CrJv.js} +0 -0
- /package/dist/assets/{cytoscape.esm-iekjSZ2n.js → cytoscape.esm-DyfqweF2.js} +0 -0
- /package/dist/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/dist/assets/{dist-zzfqk5hx.js → dist-CLg13NtV.js} +0 -0
- /package/dist/assets/{graphlib-CIz_CfxW.js → graphlib-DJkmx0Cq.js} +0 -0
- /package/dist/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
- /package/dist/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
- /package/dist/assets/{plugin-api-C8hMT5W6.js → plugin-api-Uu2xAB4d.js} +0 -0
- /package/dist/assets/{rough.esm-BMOtDaaS.js → rough.esm-Bud4g4fh.js} +0 -0
- /package/dist/assets/{xterm-BFzZ1FH4.js → xterm-Bm_IbozX.js} +0 -0
- /package/plugins/{openai-documents → documents}/commands/document.md +0 -0
- /package/plugins/{openai-documents → documents}/skills/documents/SKILL.md +0 -0
- /package/plugins/{openai-presentations → presentations}/commands/presentation.md +0 -0
- /package/plugins/{openai-presentations → presentations}/skills/presentations/SKILL.md +0 -0
- /package/plugins/{openai-spreadsheets → spreadsheets}/commands/spreadsheet.md +0 -0
- /package/plugins/{openai-spreadsheets → spreadsheets}/skills/spreadsheets/SKILL.md +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { atomicUpdate, readStore } from '../storage.mjs'
|
|
2
|
+
import { parseCloudBaseUrl } from './config.mjs'
|
|
3
|
+
|
|
4
|
+
export const CLOUD_SERVICE_SETTINGS_KEY = 'quickforge-cloud-service'
|
|
5
|
+
export const CLOUD_SERVICE_SCHEMA_VERSION = 1
|
|
6
|
+
export const CLOUD_SERVICE_TYPE = 'quickforge-cloud'
|
|
7
|
+
export const DEFAULT_CLOUD_URL = 'http://127.0.0.1:8082/'
|
|
8
|
+
|
|
9
|
+
function savedServiceRecord(settings) {
|
|
10
|
+
const value = settings?.[CLOUD_SERVICE_SETTINGS_KEY]
|
|
11
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function candidateFrom(settings, env) {
|
|
15
|
+
const saved = savedServiceRecord(settings)
|
|
16
|
+
if (saved && typeof saved.cloudUrl === 'string' && saved.cloudUrl.trim()) {
|
|
17
|
+
return { value: saved.cloudUrl, source: 'saved', saved: true, enabled: saved.enabled === true }
|
|
18
|
+
}
|
|
19
|
+
if (typeof env?.QUICKFORGE_CLOUD_URL === 'string' && env.QUICKFORGE_CLOUD_URL.trim()) {
|
|
20
|
+
return { value: env.QUICKFORGE_CLOUD_URL, source: 'env', saved: false, enabled: false }
|
|
21
|
+
}
|
|
22
|
+
return { value: DEFAULT_CLOUD_URL, source: 'default', saved: false, enabled: false }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function readCloudServiceConfig({ readSettings = readStore, env = process.env, strict = true } = {}) {
|
|
26
|
+
const settings = await readSettings('settings')
|
|
27
|
+
const candidate = candidateFrom(settings, env)
|
|
28
|
+
try {
|
|
29
|
+
const baseUrl = parseCloudBaseUrl(candidate.value)
|
|
30
|
+
return {
|
|
31
|
+
schemaVersion: CLOUD_SERVICE_SCHEMA_VERSION,
|
|
32
|
+
serviceType: CLOUD_SERVICE_TYPE,
|
|
33
|
+
enabled: candidate.enabled,
|
|
34
|
+
cloudUrl: baseUrl.href,
|
|
35
|
+
baseUrl,
|
|
36
|
+
source: candidate.source,
|
|
37
|
+
saved: candidate.saved,
|
|
38
|
+
valid: true,
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (strict) throw error
|
|
42
|
+
return {
|
|
43
|
+
schemaVersion: CLOUD_SERVICE_SCHEMA_VERSION,
|
|
44
|
+
serviceType: CLOUD_SERVICE_TYPE,
|
|
45
|
+
enabled: candidate.enabled,
|
|
46
|
+
cloudUrl: String(candidate.value || '').trim(),
|
|
47
|
+
source: candidate.source,
|
|
48
|
+
saved: candidate.saved,
|
|
49
|
+
valid: false,
|
|
50
|
+
configurationError: error instanceof Error ? error.message : String(error),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function saveCloudServiceConfig({ cloudUrl, enabled }, { updateSettings = atomicUpdate } = {}) {
|
|
56
|
+
const baseUrl = parseCloudBaseUrl(cloudUrl)
|
|
57
|
+
const record = {
|
|
58
|
+
schemaVersion: CLOUD_SERVICE_SCHEMA_VERSION,
|
|
59
|
+
serviceType: CLOUD_SERVICE_TYPE,
|
|
60
|
+
enabled: enabled === true,
|
|
61
|
+
cloudUrl: baseUrl.href,
|
|
62
|
+
}
|
|
63
|
+
await updateSettings('settings', (settings) => ({
|
|
64
|
+
...settings,
|
|
65
|
+
[CLOUD_SERVICE_SETTINGS_KEY]: record,
|
|
66
|
+
}))
|
|
67
|
+
return record
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function publicCloudServiceConfig(config) {
|
|
71
|
+
return {
|
|
72
|
+
schemaVersion: CLOUD_SERVICE_SCHEMA_VERSION,
|
|
73
|
+
serviceType: CLOUD_SERVICE_TYPE,
|
|
74
|
+
enabled: config.enabled === true,
|
|
75
|
+
cloudUrl: config.cloudUrl,
|
|
76
|
+
source: config.source,
|
|
77
|
+
saved: config.saved,
|
|
78
|
+
valid: config.valid,
|
|
79
|
+
configurationError: config.configurationError,
|
|
80
|
+
}
|
|
81
|
+
}
|
package/server/index.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { handleToolApi, handleGetTools } from './routes/tools.mjs'
|
|
|
17
17
|
import { handleInstructionsApi } from './routes/instructions.mjs'
|
|
18
18
|
import { handleMemoryApi } from './routes/memory.mjs'
|
|
19
19
|
import { handleSkillsApi } from './routes/skills.mjs'
|
|
20
|
+
import { ensureDefaultGlobalSkills } from './skills.mjs'
|
|
20
21
|
import { handleAgentApi } from './routes/agent.mjs'
|
|
21
22
|
import { handleAgentProfilesApi } from './routes/agent-profiles.mjs'
|
|
22
23
|
import { handleScheduledTasksApi, startScheduledTaskRunner, stopScheduledTaskRunner } from './routes/scheduled-tasks.mjs'
|
|
@@ -33,6 +34,9 @@ import { handleWorkspaceApi, handleGitApi } from './routes/workspace.mjs'
|
|
|
33
34
|
import { handleTerminalApi, handleTerminalUpgrade } from './routes/terminal.mjs'
|
|
34
35
|
import { handleChannelsApi } from './routes/channels.mjs'
|
|
35
36
|
import { handleModelsApi } from './routes/models.mjs'
|
|
37
|
+
import { handleCloudApi } from './routes/cloud.mjs'
|
|
38
|
+
import { readCloudServiceConfig } from './cloud/service-config.mjs'
|
|
39
|
+
import { startQfAgent, stopQfAgent, getQfAgentStatus } from './cloud/qf-agent-process.mjs'
|
|
36
40
|
import { serveStatic } from './routes/static.mjs'
|
|
37
41
|
import { logger, flushLogger } from './utils/logger.mjs'
|
|
38
42
|
import { getPackageInfo, checkForUpdates, checkDesktopRelease } from './utils/package-update.mjs'
|
|
@@ -45,6 +49,7 @@ import { initializeChannels, shutdownChannels } from './channels/registry.mjs'
|
|
|
45
49
|
import { shutdownMcpConnections } from './mcp/registry.mjs'
|
|
46
50
|
import { shutdownTerminalSessions } from './terminal/terminal-manager.mjs'
|
|
47
51
|
import { createNodeProcessEnv } from './utils/process-env.mjs'
|
|
52
|
+
import { isAuthenticatedAppClient } from './access-policy.mjs'
|
|
48
53
|
|
|
49
54
|
const __filename = fileURLToPath(import.meta.url)
|
|
50
55
|
const __dirname = path.dirname(__filename)
|
|
@@ -65,6 +70,10 @@ const port = Number(process.env.QUICKFORGE_PORT || (isDev ? 32176 : 5176))
|
|
|
65
70
|
const vitePort = Number(process.env.QUICKFORGE_VITE_PORT || 5176)
|
|
66
71
|
let restartInProgress = false
|
|
67
72
|
let updateInProgress = false
|
|
73
|
+
let shutdownPromise = null
|
|
74
|
+
let shutdownStarted = false
|
|
75
|
+
let qfAgentStartPromise = null
|
|
76
|
+
let boundServerPort = null
|
|
68
77
|
|
|
69
78
|
setDefaultWorkspaceRoot(process.env.QUICKFORGE_WORKSPACE_DIR || path.join(dataDir, 'workspace'))
|
|
70
79
|
|
|
@@ -72,11 +81,12 @@ function getRestartSupport() {
|
|
|
72
81
|
return { supported: true, reason: null }
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
async function getSystemStatus({ isLocalRequest = true } = {}) {
|
|
84
|
+
async function getSystemStatus({ isLocalRequest = true, remoteAuthorized = false } = {}) {
|
|
76
85
|
const config = await readProjectConfig()
|
|
77
86
|
const restartSupport = getRestartSupport()
|
|
78
87
|
const packageInfo = await getPackageInfo(projectRoot)
|
|
79
|
-
const
|
|
88
|
+
const restartAllowed = isAuthenticatedAppClient({ isLocalRequest, remoteAuthorized })
|
|
89
|
+
const restartSupported = restartAllowed && restartSupport.supported
|
|
80
90
|
return {
|
|
81
91
|
ok: true,
|
|
82
92
|
mode: isDev ? 'development' : 'production',
|
|
@@ -92,7 +102,7 @@ async function getSystemStatus({ isLocalRequest = true } = {}) {
|
|
|
92
102
|
terminal: isLocalRequest,
|
|
93
103
|
},
|
|
94
104
|
restartSupported,
|
|
95
|
-
restartUnsupportedReason:
|
|
105
|
+
restartUnsupportedReason: restartAllowed ? restartSupport.reason : 'Restart requires an authenticated client',
|
|
96
106
|
dataDir,
|
|
97
107
|
configDir,
|
|
98
108
|
storageDir,
|
|
@@ -149,11 +159,9 @@ function closeHttpServer() {
|
|
|
149
159
|
})
|
|
150
160
|
}
|
|
151
161
|
|
|
152
|
-
async function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
logger.info(`Restart supervisor started (PID ${supervisorPid}).`)
|
|
156
|
-
|
|
162
|
+
async function shutdownRuntime() {
|
|
163
|
+
shutdownStarted = true
|
|
164
|
+
await stopQfAgent()
|
|
157
165
|
stopScheduledTaskRunner()
|
|
158
166
|
stopAutoArchiveRunner()
|
|
159
167
|
stopVite()
|
|
@@ -162,6 +170,19 @@ async function performRestart() {
|
|
|
162
170
|
await shutdownChannels()
|
|
163
171
|
shutdownTerminalSessions()
|
|
164
172
|
await closeHttpServer()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function stopQuickForgeServer() {
|
|
176
|
+
if (!shutdownPromise) shutdownPromise = shutdownRuntime()
|
|
177
|
+
return shutdownPromise
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function performRestart() {
|
|
181
|
+
logger.info('Restart requested from settings UI.')
|
|
182
|
+
const supervisorPid = await spawnRestartSupervisor()
|
|
183
|
+
logger.info(`Restart supervisor started (PID ${supervisorPid}).`)
|
|
184
|
+
|
|
185
|
+
await stopQuickForgeServer()
|
|
165
186
|
process.exit(0)
|
|
166
187
|
}
|
|
167
188
|
|
|
@@ -228,14 +249,7 @@ function spawnUpdateSupervisor(update) {
|
|
|
228
249
|
|
|
229
250
|
async function shutdownForUpdate() {
|
|
230
251
|
logger.info('Shutting down QuickForge for external updater.')
|
|
231
|
-
|
|
232
|
-
stopAutoArchiveRunner()
|
|
233
|
-
stopVite()
|
|
234
|
-
await shutdownAgentManager()
|
|
235
|
-
await shutdownMcpConnections()
|
|
236
|
-
await shutdownChannels()
|
|
237
|
-
shutdownTerminalSessions()
|
|
238
|
-
await closeHttpServer()
|
|
252
|
+
await stopQuickForgeServer()
|
|
239
253
|
flushLogger()
|
|
240
254
|
process.exit(0)
|
|
241
255
|
}
|
|
@@ -282,6 +296,22 @@ async function updateQuickForge() {
|
|
|
282
296
|
}
|
|
283
297
|
}
|
|
284
298
|
|
|
299
|
+
async function applyCloudServiceConfig(cloudConfig, { urlChanged = false } = {}) {
|
|
300
|
+
if (shutdownStarted) return null
|
|
301
|
+
if (!cloudConfig.enabled || !cloudConfig.valid) {
|
|
302
|
+
if (!cloudConfig.valid) logger.warn(`QuickForge remote agent was not started: ${cloudConfig.configurationError || 'invalid Cloud configuration'}`)
|
|
303
|
+
return stopQfAgent({ disabled: true })
|
|
304
|
+
}
|
|
305
|
+
if (!boundServerPort) return null
|
|
306
|
+
const agentStatus = getQfAgentStatus()
|
|
307
|
+
if (urlChanged || agentStatus.status === 'disabled' || agentStatus.enabled === false) await stopQfAgent()
|
|
308
|
+
return startQfAgent({
|
|
309
|
+
serverUrl: `http://127.0.0.1:${boundServerPort}/`,
|
|
310
|
+
ownerPid: process.pid,
|
|
311
|
+
cloudUrl: cloudConfig.cloudUrl,
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
|
|
285
315
|
// --- Route dispatching ---
|
|
286
316
|
async function handleApi(req, res, url, requestContext = {}) {
|
|
287
317
|
const pathname = url.pathname
|
|
@@ -289,12 +319,12 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
289
319
|
|
|
290
320
|
// Conversation share routes (management + public LAN access)
|
|
291
321
|
if (pathname === '/api/shares' || pathname.startsWith('/api/shares/')) {
|
|
292
|
-
await handleSharesApi(req, res, url, { port })
|
|
322
|
+
await handleSharesApi(req, res, url, { ...requestContext, port })
|
|
293
323
|
return
|
|
294
324
|
}
|
|
295
325
|
|
|
296
326
|
if (pathname.startsWith('/api/shared/')) {
|
|
297
|
-
await handleSharedConversationApi(req, res, url)
|
|
327
|
+
await handleSharedConversationApi(req, res, url, requestContext)
|
|
298
328
|
return
|
|
299
329
|
}
|
|
300
330
|
|
|
@@ -336,13 +366,24 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
336
366
|
|
|
337
367
|
// Agent profiles
|
|
338
368
|
if (pathname === '/api/agent-profiles' || pathname.startsWith('/api/agent-profiles/')) {
|
|
339
|
-
await handleAgentProfilesApi(req, res, url)
|
|
369
|
+
await handleAgentProfilesApi(req, res, url, requestContext)
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// QuickForge Cloud account and managed models (local or authenticated Tailscale requests).
|
|
374
|
+
if (pathname === '/api/cloud' || pathname.startsWith('/api/cloud/')) {
|
|
375
|
+
await handleCloudApi(req, res, url, {
|
|
376
|
+
isLocalRequest: requestContext.isLocalRequest === true,
|
|
377
|
+
remoteAddress: requestContext.remoteAddress,
|
|
378
|
+
remoteAuthorized: requestContext.remoteAuthorized === true,
|
|
379
|
+
onCloudServiceConfigChanged: applyCloudServiceConfig,
|
|
380
|
+
})
|
|
340
381
|
return
|
|
341
382
|
}
|
|
342
383
|
|
|
343
384
|
// Custom model management (connection test)
|
|
344
|
-
if (pathname === '/api/models/test-connection') {
|
|
345
|
-
await handleModelsApi(req, res, url)
|
|
385
|
+
if (pathname === '/api/models/catalog' || pathname === '/api/models/test-connection') {
|
|
386
|
+
await handleModelsApi(req, res, url, requestContext)
|
|
346
387
|
return
|
|
347
388
|
}
|
|
348
389
|
|
|
@@ -387,7 +428,7 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
387
428
|
}
|
|
388
429
|
|
|
389
430
|
if (pathname.startsWith('/api/git/')) {
|
|
390
|
-
await handleGitApi(req, res, url)
|
|
431
|
+
await handleGitApi(req, res, url, requestContext)
|
|
391
432
|
return
|
|
392
433
|
}
|
|
393
434
|
|
|
@@ -411,19 +452,19 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
411
452
|
|
|
412
453
|
// Agent routes
|
|
413
454
|
if (parts[0] === 'api' && parts[1] === 'agents') {
|
|
414
|
-
await handleAgentApi(req, res, url)
|
|
455
|
+
await handleAgentApi(req, res, url, requestContext)
|
|
415
456
|
return
|
|
416
457
|
}
|
|
417
458
|
|
|
418
459
|
// Scheduled task routes
|
|
419
460
|
if (pathname === '/api/scheduled-tasks' || pathname.startsWith('/api/scheduled-tasks/')) {
|
|
420
|
-
await handleScheduledTasksApi(req, res, url)
|
|
461
|
+
await handleScheduledTasksApi(req, res, url, requestContext)
|
|
421
462
|
return
|
|
422
463
|
}
|
|
423
464
|
|
|
424
465
|
// Backup / import-export routes
|
|
425
466
|
if (pathname === '/api/backup/export' || pathname === '/api/backup/import' || pathname === '/api/backup/inspect' || pathname === '/api/backup/inspect-file') {
|
|
426
|
-
await handleBackupApi(req, res, url)
|
|
467
|
+
await handleBackupApi(req, res, url, requestContext)
|
|
427
468
|
return
|
|
428
469
|
}
|
|
429
470
|
|
|
@@ -437,6 +478,7 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
437
478
|
checkDesktopRelease: () => checkDesktopRelease(projectRoot),
|
|
438
479
|
updateQuickForge,
|
|
439
480
|
isLocalRequest: requestContext.isLocalRequest === true,
|
|
481
|
+
remoteAuthorized: requestContext.remoteAuthorized === true,
|
|
440
482
|
getTerminalShellSetting: readTerminalShellSetting,
|
|
441
483
|
updateTerminalShellSetting,
|
|
442
484
|
getTerminalShellConfig: readTerminalShellConfig,
|
|
@@ -461,7 +503,7 @@ async function handleApi(req, res, url, requestContext = {}) {
|
|
|
461
503
|
|
|
462
504
|
// Storage routes (catch-all)
|
|
463
505
|
if (parts[0] === 'api' && parts[1] === 'storage') {
|
|
464
|
-
await handleStorageApi(req, res, url)
|
|
506
|
+
await handleStorageApi(req, res, url, requestContext)
|
|
465
507
|
return
|
|
466
508
|
}
|
|
467
509
|
|
|
@@ -532,6 +574,24 @@ function isAllowedHostHeader(value) {
|
|
|
532
574
|
return allowedHosts.has(parsed.hostname) && hostPort === expectedPort
|
|
533
575
|
}
|
|
534
576
|
|
|
577
|
+
const trustedTunnelSocket = Symbol('trustedTunnelSocket')
|
|
578
|
+
|
|
579
|
+
function isTunnelClientRequest(req) {
|
|
580
|
+
if (
|
|
581
|
+
isLoopbackAddress(req.socket.remoteAddress)
|
|
582
|
+
&& req.headers['x-quickforge-tunnel'] === '1'
|
|
583
|
+
&& req.headers.host === '127.0.0.1:18080'
|
|
584
|
+
) {
|
|
585
|
+
req.socket[trustedTunnelSocket] = true
|
|
586
|
+
}
|
|
587
|
+
return req.socket[trustedTunnelSocket] === true
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function isAllowedRequestHost(req, isTunnelClient) {
|
|
591
|
+
return isAllowedHostHeader(req.headers.host)
|
|
592
|
+
|| (isTunnelClient && req.headers.host === '127.0.0.1:18080')
|
|
593
|
+
}
|
|
594
|
+
|
|
535
595
|
function isLanAccessBootstrapPath(pathname) {
|
|
536
596
|
return pathname === '/api/health'
|
|
537
597
|
|| pathname === '/api/lan-access/status'
|
|
@@ -571,7 +631,11 @@ const server = createServer(async (req, res) => {
|
|
|
571
631
|
reqLogger.info(`${req.method} ${req.url} ${res.statusCode}`, { method: req.method, url: req.url, status: res.statusCode, durationMs })
|
|
572
632
|
})
|
|
573
633
|
|
|
574
|
-
|
|
634
|
+
const remoteAddress = req.socket.remoteAddress
|
|
635
|
+
// 云远程访问(RemoteTunnel):agent 与 qf 同机时流量经 127.0.0.1 回环进入并携带
|
|
636
|
+
// X-QuickForge-Tunnel: 1。仅该可信隧道允许手机本地入口 127.0.0.1:18080 作为 Host。
|
|
637
|
+
const isTunnelClient = isTunnelClientRequest(req)
|
|
638
|
+
if (!isAllowedRequestHost(req, isTunnelClient)) {
|
|
575
639
|
res.writeHead(403, { 'content-type': 'application/json; charset=utf-8' })
|
|
576
640
|
res.end(JSON.stringify({ error: 'Forbidden host' }))
|
|
577
641
|
return
|
|
@@ -593,11 +657,11 @@ const server = createServer(async (req, res) => {
|
|
|
593
657
|
}
|
|
594
658
|
try {
|
|
595
659
|
const url = new URL(req.url || '/', `http://${req.headers.host || `${host}:${port}`}`)
|
|
596
|
-
|
|
597
|
-
const isRemoteRequest = !isLoopbackAddress(remoteAddress)
|
|
598
|
-
const remoteAuthorized = isRemoteRequest ? await isAuthorizedRemoteRequest(req) : true
|
|
660
|
+
// 隧道请求视为“已认证远程客户端”:认证通过,但本地能力按远程请求裁剪。
|
|
661
|
+
const isRemoteRequest = isTunnelClient || !isLoopbackAddress(remoteAddress)
|
|
662
|
+
const remoteAuthorized = isRemoteRequest ? (isTunnelClient ? true : await isAuthorizedRemoteRequest(req)) : true
|
|
599
663
|
|
|
600
|
-
if (isRemoteRequest &&
|
|
664
|
+
if (isRemoteRequest && !remoteAuthorized && !isLanAccessBootstrapPath(url.pathname) && !isSharePath(url.pathname) && !isStaticAssetPath(url.pathname)) {
|
|
601
665
|
if (url.pathname.startsWith('/api/')) {
|
|
602
666
|
sendLanAuthRequired(res)
|
|
603
667
|
} else {
|
|
@@ -609,7 +673,6 @@ const server = createServer(async (req, res) => {
|
|
|
609
673
|
if (
|
|
610
674
|
url.pathname.startsWith('/api/') &&
|
|
611
675
|
isRemoteRequest &&
|
|
612
|
-
shareLanEnabled &&
|
|
613
676
|
!remoteAuthorized &&
|
|
614
677
|
!(url.pathname.startsWith('/api/shared/') || url.pathname === '/api/health' || url.pathname.startsWith('/api/lan-access/'))
|
|
615
678
|
) {
|
|
@@ -619,7 +682,12 @@ const server = createServer(async (req, res) => {
|
|
|
619
682
|
}
|
|
620
683
|
|
|
621
684
|
if (url.pathname.startsWith('/api/')) {
|
|
622
|
-
await handleApi(req, res, url, {
|
|
685
|
+
await handleApi(req, res, url, {
|
|
686
|
+
isLocalRequest: !isRemoteRequest,
|
|
687
|
+
remoteAddress,
|
|
688
|
+
remoteAuthorized,
|
|
689
|
+
tunnelClient: isTunnelClient,
|
|
690
|
+
})
|
|
623
691
|
return
|
|
624
692
|
}
|
|
625
693
|
|
|
@@ -633,7 +701,7 @@ const server = createServer(async (req, res) => {
|
|
|
633
701
|
return
|
|
634
702
|
}
|
|
635
703
|
|
|
636
|
-
if (isRemoteRequest &&
|
|
704
|
+
if (isRemoteRequest && !remoteAuthorized) {
|
|
637
705
|
res.writeHead(403, { 'content-type': 'application/json; charset=utf-8' })
|
|
638
706
|
res.end(JSON.stringify({ error: 'Remote access is limited to shared conversation links.' }))
|
|
639
707
|
return
|
|
@@ -661,7 +729,8 @@ function writeAndDestroySocket(socket, statusLine) {
|
|
|
661
729
|
}
|
|
662
730
|
|
|
663
731
|
server.on('upgrade', (req, socket, head) => {
|
|
664
|
-
|
|
732
|
+
const isTunnelClient = isTunnelClientRequest(req)
|
|
733
|
+
if (!isAllowedRequestHost(req, isTunnelClient)) {
|
|
665
734
|
writeAndDestroySocket(socket, 'HTTP/1.1 403 Forbidden')
|
|
666
735
|
return
|
|
667
736
|
}
|
|
@@ -670,7 +739,8 @@ server.on('upgrade', (req, socket, head) => {
|
|
|
670
739
|
const url = new URL(req.url || '/', `http://${req.headers.host || `${host}:${port}`}`)
|
|
671
740
|
if (url.pathname.startsWith('/api/terminal/sessions/')) {
|
|
672
741
|
handleTerminalUpgrade(req, socket, head, url, {
|
|
673
|
-
|
|
742
|
+
// 隧道流量同样是远程客户端:终端 WebSocket 保持禁止。
|
|
743
|
+
isLocalRequest: isLoopbackAddress(req.socket.remoteAddress) && !isTunnelClient,
|
|
674
744
|
})
|
|
675
745
|
return
|
|
676
746
|
}
|
|
@@ -681,6 +751,7 @@ server.on('upgrade', (req, socket, head) => {
|
|
|
681
751
|
})
|
|
682
752
|
|
|
683
753
|
await ensureStorage()
|
|
754
|
+
await ensureDefaultGlobalSkills()
|
|
684
755
|
await initializeNetworkProxy()
|
|
685
756
|
installAiHttpLogger()
|
|
686
757
|
initializeChannels({
|
|
@@ -708,35 +779,40 @@ server.on('error', (error) => {
|
|
|
708
779
|
})
|
|
709
780
|
|
|
710
781
|
server.listen(port, host, () => {
|
|
711
|
-
|
|
782
|
+
const address = server.address()
|
|
783
|
+
const boundPort = typeof address === 'object' && address ? address.port : port
|
|
784
|
+
boundServerPort = boundPort
|
|
785
|
+
logger.info(`QuickForge local API: http://${host}:${boundPort}`)
|
|
712
786
|
if (shareLanEnabled) {
|
|
713
|
-
const lanUrls = getLanUrls(
|
|
714
|
-
logger.info(`QuickForge LAN sharing is enabled. Share pages are available at: ${lanUrls.length ? lanUrls.join(', ') : `http://<your-lan-ip>:${
|
|
787
|
+
const lanUrls = getLanUrls(boundPort)
|
|
788
|
+
logger.info(`QuickForge LAN sharing is enabled. Share pages are available at: ${lanUrls.length ? lanUrls.join(', ') : `http://<your-lan-ip>:${boundPort}`}`)
|
|
715
789
|
logger.info('Remote non-share API routes are restricted while QUICKFORGE_SHARE_LAN=1.')
|
|
716
790
|
}
|
|
717
791
|
logger.info(`QuickForge data dir: ${dataDir}`)
|
|
718
792
|
logger.info(`QuickForge project: ${getWorkspaceRoot()}`)
|
|
719
793
|
|
|
794
|
+
const pending = readCloudServiceConfig({ strict: false }).then((cloudConfig) => applyCloudServiceConfig(cloudConfig)).catch((error) => {
|
|
795
|
+
if (!shutdownStarted) logger.warn(`QuickForge remote agent failed to start: ${error?.message || error}`)
|
|
796
|
+
return null
|
|
797
|
+
}).finally(() => {
|
|
798
|
+
if (qfAgentStartPromise === pending) qfAgentStartPromise = null
|
|
799
|
+
})
|
|
800
|
+
qfAgentStartPromise = pending
|
|
801
|
+
|
|
720
802
|
if (isDev) {
|
|
721
803
|
startVite()
|
|
722
804
|
setTimeout(() => openBrowser(`http://localhost:${vitePort}`), 1000)
|
|
723
805
|
} else if (shareLanEnabled) {
|
|
724
|
-
openBrowser(`http://localhost:${
|
|
806
|
+
openBrowser(`http://localhost:${boundPort}`)
|
|
725
807
|
} else {
|
|
726
|
-
openBrowser(`http://localhost:${
|
|
808
|
+
openBrowser(`http://localhost:${boundPort}`)
|
|
727
809
|
}
|
|
728
810
|
})
|
|
729
811
|
|
|
730
812
|
// Graceful shutdown
|
|
731
813
|
async function gracefulShutdown(signal) {
|
|
732
814
|
logger.info(`Received ${signal}, shutting down gracefully...`)
|
|
733
|
-
|
|
734
|
-
stopAutoArchiveRunner()
|
|
735
|
-
stopVite()
|
|
736
|
-
await shutdownAgentManager()
|
|
737
|
-
await shutdownMcpConnections()
|
|
738
|
-
await shutdownChannels()
|
|
739
|
-
shutdownTerminalSessions()
|
|
815
|
+
await stopQuickForgeServer()
|
|
740
816
|
flushLogger()
|
|
741
817
|
process.exit(0)
|
|
742
818
|
}
|
package/server/mcp/registry.mjs
CHANGED
|
@@ -215,22 +215,21 @@ async function refreshConnections() {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
const refreshed = await Promise.all([...enabled.values()].map(async (config) => {
|
|
219
219
|
const existing = connections.get(config.name)
|
|
220
220
|
if (existing) {
|
|
221
221
|
if (existing.status === 'error' && Date.now() - (existing.lastAttemptAt || 0) >= RETRY_ERROR_AFTER_MS) {
|
|
222
222
|
connections.delete(config.name)
|
|
223
223
|
await closeConnection(existing)
|
|
224
224
|
} else {
|
|
225
|
-
|
|
225
|
+
return [config.name, existing]
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
try {
|
|
229
|
-
|
|
230
|
-
connections.set(config.name, connection)
|
|
229
|
+
return [config.name, await connectServer(config)]
|
|
231
230
|
} catch (error) {
|
|
232
231
|
logger.error(`Failed to connect MCP server ${config.name}:`, error)
|
|
233
|
-
|
|
232
|
+
return [config.name, {
|
|
234
233
|
config,
|
|
235
234
|
client: null,
|
|
236
235
|
transport: null,
|
|
@@ -240,10 +239,12 @@ async function refreshConnections() {
|
|
|
240
239
|
connectedAt: null,
|
|
241
240
|
stderr: '',
|
|
242
241
|
lastAttemptAt: Date.now(),
|
|
243
|
-
}
|
|
242
|
+
}]
|
|
244
243
|
}
|
|
245
|
-
}
|
|
244
|
+
}))
|
|
246
245
|
|
|
246
|
+
connections.clear()
|
|
247
|
+
for (const [name, connection] of refreshed) connections.set(name, connection)
|
|
247
248
|
return connections
|
|
248
249
|
}
|
|
249
250
|
|
|
@@ -21,30 +21,51 @@ export function omitDetailsForLlm(message) {
|
|
|
21
21
|
* Handles "user-with-attachments" → "user" with multi-modal content blocks.
|
|
22
22
|
* Without this the default pi-agent-core convertToLlm silently drops
|
|
23
23
|
* user-with-attachments messages, so the LLM never sees attachments.
|
|
24
|
+
*
|
|
25
|
+
* Also guards against orphaned toolResult messages (a tool result without a
|
|
26
|
+
* preceding assistant tool_call): OpenAI-compatible APIs reject such sequences
|
|
27
|
+
* with 400 "Messages with role 'tool' must be a response to a preceding
|
|
28
|
+
* message with 'tool_calls'". Assistant messages that pi-ai would skip
|
|
29
|
+
* (stopReason error/aborted) are not counted as valid tool_call ancestors,
|
|
30
|
+
* matching the upstream skip logic so their results are dropped too.
|
|
24
31
|
*/
|
|
25
32
|
export function serverConvertToLlm(messages) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const pendingToolCallIds = new Set()
|
|
34
|
+
const converted = []
|
|
35
|
+
for (const m of messages) {
|
|
36
|
+
if (!m || typeof m !== 'object') continue
|
|
37
|
+
if (m.role === 'artifact') continue
|
|
38
|
+
if (m.role === 'user-with-attachments') {
|
|
39
|
+
const textContent = typeof m.content === 'string'
|
|
40
|
+
? [{ type: 'text', text: m.content }]
|
|
41
|
+
: [...m.content]
|
|
42
|
+
if (Array.isArray(m.attachments)) {
|
|
43
|
+
for (const att of m.attachments) {
|
|
44
|
+
if (att.type === 'image' && att.content) {
|
|
45
|
+
textContent.push({ type: 'image', data: att.content, mimeType: att.mimeType })
|
|
46
|
+
} else if (att.type === 'document' && att.extractedText) {
|
|
47
|
+
textContent.push({ type: 'text', text: `\n\n[Document: ${att.fileName}]\n${att.extractedText}` })
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
|
-
return omitDetailsForLlm({ ...m, role: 'user', content: textContent })
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
51
|
+
converted.push(omitDetailsForLlm({ ...m, role: 'user', content: textContent }))
|
|
52
|
+
} else if (m.role === 'user') {
|
|
53
|
+
converted.push(omitDetailsForLlm(m))
|
|
54
|
+
} else if (m.role === 'assistant') {
|
|
55
|
+
if (m.stopReason !== 'error' && m.stopReason !== 'aborted' && Array.isArray(m.content)) {
|
|
56
|
+
for (const block of m.content) {
|
|
57
|
+
if (block?.type === 'toolCall' && block.id) pendingToolCallIds.add(block.id)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
converted.push(omitDetailsForLlm(m))
|
|
61
|
+
} else if (m.role === 'toolResult') {
|
|
62
|
+
// 孤儿 toolResult(无前导 assistant tool_calls)直接丢弃,避免上游 400
|
|
63
|
+
if (!m.toolCallId || !pendingToolCallIds.has(m.toolCallId)) continue
|
|
64
|
+
pendingToolCallIds.delete(m.toolCallId)
|
|
65
|
+
converted.push(omitDetailsForLlm(m))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return converted
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
/**
|