@shawnstack/quickforge 1.7.3 → 1.7.5
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-XNjuqJs7.js +1 -0
- package/dist/assets/ChatPanelHost-CuF-PZpc.js +292 -0
- package/dist/assets/CloudAccountSettingsPage-GJSoi6sO.js +1 -0
- package/dist/assets/PluginsPage-BQgp0qN-.js +1 -0
- package/dist/assets/ScheduledTasksPage-DdekATzq.js +2 -0
- package/dist/assets/SettingsWorkspacePage-Cb54Ic91.js +1639 -0
- package/dist/assets/ShareLinksSettingsPage-C75WJpaF.js +1 -0
- package/dist/assets/SharedConversationPage-CjMtSQTW.js +1 -0
- package/dist/assets/TerminalDock-D4zJBJc4.js +2 -0
- package/dist/assets/WorkspaceInspector-D9kdnLDd.js +36 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-Dl_Fvlqg.js → abnfDiagram-VRR7QNED-CMhVoYUh.js} +1 -1
- package/dist/assets/{arc-Bhg4c0ht.js → arc-w21O0TLn.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-DMwFMHxq.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BuojN27B.js → architectureDiagram-ZJ3FMSHR-C52qL6Hg.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-alDLbrR1.js → blockDiagram-677ZJIJ3-CM3XbLol.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-D1lKa4VT.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-CwWLNjMZ.js → chunk-32BRIVSS-xgrtbT8e.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-Be569m7d.js → chunk-52WLFC77-CDbaHw80.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-BSvoiWV1.js → chunk-5FCAYU7R-CcSvVbPc.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-CRgsy8WS.js → chunk-5HE753X5-erFpzzgx.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CdW477UR.js → chunk-5JV3BV7I-rEhN9F6G.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-CdtKzSmp.js → chunk-5TONJI2A-D8L-QTwD.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BKjjssLZ.js → chunk-5VM5RSS4-CjlfMBbM.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-ByAXiXdn.js → chunk-7BUUIJ7U-CCcsI9XE.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-DeG8SCT1.js → chunk-BIQX33UG-FSqFbmb3.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-D_zHT68Q.js → chunk-C7G6YPKG-ZMLUvfLy.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-CHpHoUSu.js → chunk-CQNSW5MT-DZuBpk25.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-CRP50Vov.js → chunk-CYSBUYHQ-CB26wsG0.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-DX6uQQ2v.js → chunk-EMLP6XTP-BbsM8oRw.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DAJRNkHR.js → chunk-EX3LRPZG-BcQhaqt7.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-XbM5yomC.js → chunk-FWX5IMBZ-DrnrdrSu.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Ci0ljnb1.js → chunk-HOUHSVGY-tdc-Z9eW.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-Dy2WJ71z.js → chunk-ICXQ74PX-Cd7igVGt.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-2PrkzHkt.js → chunk-JG7HCLWE-iXXGW8nf.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-D32uacSM.js → chunk-JWPE2WC7-BZ87mXEv.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DIAFLtiS.js → chunk-MOJQB5TN-CLTjV5ov.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-BbscOaEz.js → chunk-MOZMSUNE-B3SCBrRH.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-ivBVxzKe.js → chunk-OGEWGWER-TgvkXkFX.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-frD0hkdx.js → chunk-OSBZ3O6U-B9E2E5v1.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-B-37Rxae.js → chunk-PUDLZKDR-C5GMVYup.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Y90m2RTe.js → chunk-Q4XR5HBZ-fo5-HsqY.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-DixBqslB.js → chunk-QBLGF6JB-Dwfg8sKc.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-DXS1UXn_.js → chunk-R7FJI6CG-DpBrv4ni.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-n4JxhNd0.js → chunk-RYQCIY6F-DzzmTl11.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-yHlgxMmn.js → chunk-U6XO7XAA-Dw6aviKa.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-Z6kuZfbl.js → chunk-V7JOEXUC-CEDazV6w.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CWYjg8zE.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-Cs_SIQma.js → chunk-VR4S4FIN-C_-eEj-9.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-CGExYbV-.js → chunk-WYO6CB5R-NF-MsJJR.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-DzTwx8ZH.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-DopPXWSl.js → chunk-YOTPTUD7-DqzqtdII.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-CJHOeqMb.js → chunk-ZGVPDNZ5-CRrAYi4g.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-BBVc5mf6.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-DLjV-dTm.js → cose-bilkent-JH36ORCC-cL247pHg.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-C8yCvH_o.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-BfH3zVS-.js → cynefinDiagram-TSTJHNR4-D4sZ7LYI.js} +1 -1
- package/dist/assets/{dagre-CY3D84Cl.js → dagre-DmHb3Ctt.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-BIhwZCIZ.js → dagre-VKFMJZFB-BlsnSh-Q.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BHnzNM2w.js → diagram-FQU43EPY-DW1yQiRO.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C7-ZlmFT.js → diagram-G47NLZAW-BTUAsFjb.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-C-P9Y7IR.js → diagram-NH7WQ7WH-BcaomqSd.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-Ci5lZ_T5.js → diagram-OA4YK3LP-DDLX7Xwr.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-C1xuK2bs.js → diagram-WEI45ONY-CIave0An.js} +1 -1
- package/dist/assets/dist-DLKC_odd.js +1 -0
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DKQ5NWMD.js → ebnfDiagram-CCIWWBDH-Cr1Y6lPu.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-HAWySadP.js → erDiagram-Q63AITRT-D3EgiVVH.js} +1 -1
- package/dist/assets/esm-Df4edDpj.js +2 -0
- package/dist/assets/eventmodeling-45OFAUF4-DO1vRTM0.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-BS7KXcqn.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-C1hwIcOA.js → ganttDiagram-NO4QXBWP-B0oyDIqK.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-m5F5ef09.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-DYUYxs1R.js → gitGraphDiagram-IHSO6WYX-uQtr6IqK.js} +1 -1
- package/dist/assets/icons-CXPP1fFT.js +1 -0
- package/dist/assets/index-_ZhK6bvp.js +65 -0
- package/dist/assets/index-iVV2bP_f.css +3 -0
- package/dist/assets/info-DKCQHKI2-CZxPlQJ0.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-CnIL6tv4.js → infoDiagram-FWYZ7A6U-B_GzlLIM.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-B7WXNycQ.js → ishikawaDiagram-FXEZZL3T-CKQIsSdU.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-D4HvioyY.js → journeyDiagram-5HDEW3XC-DrrvzZL1.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-CDs763eS.js → kanban-definition-HUTT4EX6-CJQRTdXz.js} +1 -1
- package/dist/assets/{line-ikKkQiFp.js → line-B4DxX7tC.js} +1 -1
- package/dist/assets/{linear-GqPyxQkN.js → linear-U-nKUhyW.js} +1 -1
- package/dist/assets/{map-CJwjCvwr.js → map-BneTM-ri.js} +1 -1
- package/dist/assets/{mcp-servers-dialog-BQbFywVL.js → mcp-servers-dialog-BUSPvaqI.js} +2 -2
- package/dist/assets/{mermaid-parser.core-CoE8cccr.js → mermaid-parser.core-BlKWrEdZ.js} +2 -2
- package/dist/assets/{mermaid-renderer-BmbSAmpG.js → mermaid-renderer-jEbqFaTe.js} +2 -2
- package/dist/assets/{mermaid.core-DlFSL90j.js → mermaid.core-DWpShXCs.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-DLdq0K9C.js → mindmap-definition-LN4V7U3C-CEVwZD78.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-BTsVCDWS.js → monaco-D6OhXgjg.js} +1 -1
- package/dist/assets/{ordinal-CibZU3PM.js → ordinal-B2r5L1v4.js} +1 -1
- package/dist/assets/{otel-BNGM50C-.js → otel-BFnphzjt.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-zIioIZVN.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-Cm1kPRC2.js → pegDiagram-2B236MQR-CBLkBdnQ.js} +1 -1
- package/dist/assets/{pi-ai-Cr3rpCXT.js → pi-ai-Cr2ee4a0.js} +2 -2
- package/dist/assets/{pi-web-ui-DoRGtREc.js → pi-web-ui-DV5jKjSg.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-GGWijBvB.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-Df05wjjo.js → pieDiagram-ENE6RG2P-8tCnwtWS.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BjCwGxJC.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-CTUvpKkv.js → railroadDiagram-RFXS5EU6-CI4ouOBs.js} +1 -1
- package/dist/assets/{react-vendor-Dr5xvL-e.js → react-vendor-1KC5PyRx.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-BU1kvEku.js → requirementDiagram-TGXJPOKE-DtFz7sw4.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-BHmvxVqH.js → sankeyDiagram-HTMAVEWB--VeOwwk9.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-CTpwICgg.js → sequenceDiagram-DBY2YBRQ-B8AHk5Oe.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-BbLhGaa4.js → sizeCapture-X5ZJPWSS-vV_ArM8D.js} +1 -1
- package/dist/assets/{skills-dialog-REBeTFSH.js → skills-dialog-BThnIUBs.js} +3 -1
- package/dist/assets/{src-B4Tvg3gT.js → src-PYsDme0X.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-BxuDwIgT.js → stateDiagram-2N3HPSRC-etXnJ8X8.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-Dms3pICj.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-EkZsiylV.js → swimlanes-5IMT3BWC-pabIKAmn.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-B14aWjv6.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-C9ewOCTa.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-CR49Y4Lp.js → vennDiagram-L72KCM5P-CgNf1Q9W.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-E3m1BOJQ.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-Cn0GgwWi.js → wardleyDiagram-EHGQE667-Drt5ytec.js} +1 -1
- package/dist/assets/web-DoV8MSMn.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-DlmPNAvd.js → xychartDiagram-FW5EYKEG-hhoEo_Ar.js} +1 -1
- package/dist/index.html +13 -11
- package/dist/mobile-error.html +24 -0
- package/package.json +6 -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/server/acp/server.mjs +50 -35
- package/server/agent-manager.mjs +343 -46
- package/server/agent-profile-files.mjs +8 -4
- package/server/agent-profile-schema.mjs +47 -23
- package/server/agent-profiles.mjs +1 -1
- package/server/ai-http-logger.mjs +156 -13
- 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/cloud/chat-idempotency-store.mjs +96 -0
- package/server/cloud/client.mjs +102 -0
- package/server/cloud/config.mjs +53 -0
- package/server/cloud/credential-store.mjs +193 -0
- package/server/cloud/identity.mjs +355 -0
- package/server/cloud/index.mjs +7 -0
- package/server/cloud/models.mjs +80 -0
- package/server/cloud/runtime.mjs +129 -0
- package/server/cloud/service-config.mjs +77 -0
- package/server/index.mjs +63 -33
- package/server/lan-access-store.mjs +93 -3
- package/server/model-catalog.mjs +262 -0
- package/server/network-proxy.mjs +68 -3
- package/server/plugins/registry.mjs +93 -9
- package/server/public-api.mjs +6 -2
- package/server/routes/agent-profiles.mjs +43 -9
- package/server/routes/agent.mjs +48 -11
- package/server/routes/backup.mjs +7 -1
- package/server/routes/channels.mjs +47 -0
- package/server/routes/cloud.mjs +227 -0
- package/server/routes/lan-access.mjs +246 -21
- package/server/routes/models.mjs +13 -4
- package/server/routes/project.mjs +19 -1
- package/server/routes/scheduled-tasks.mjs +40 -10
- package/server/routes/shared-conversation.mjs +62 -58
- package/server/routes/shares.mjs +76 -1
- package/server/routes/static.mjs +26 -1
- package/server/routes/storage.mjs +24 -1
- package/server/routes/system.mjs +11 -0
- package/server/routes/terminal.mjs +7 -2
- package/server/routes/workspace.mjs +66 -13
- package/server/session-utils.mjs +2 -2
- package/server/share-store.mjs +168 -13
- package/server/skills.mjs +30 -3
- package/server/storage.mjs +7 -1
- package/server/subagents.mjs +4 -4
- package/server/terminal/terminal-manager.mjs +2 -4
- package/server/tools/definitions.mjs +3 -3
- package/server/tools/index.mjs +7 -5
- package/server/utils/network.mjs +9 -0
- package/server/utils/package-update.mjs +70 -47
- package/server/utils/process-env.mjs +7 -0
- package/server/utils/response.mjs +10 -3
- 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-D7pbg8nm.js +0 -1
- package/dist/assets/ChatPanelHost-wfGCaCzD.js +0 -288
- package/dist/assets/PluginsPage-NAGroBm3.js +0 -1
- package/dist/assets/ScheduledTasksPage-Bp0MNfQD.js +0 -2
- 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-B9UHiPd-.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-77hsebd7.js → chunk-KEIR6QF5-jrz1pRUS.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-CQltPwYk.js → chunk-Y2CYZVJY-D6N-CrJv.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-DyfqweF2.js} +0 -0
- /package/dist/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/dist/assets/{dist-DelZBZpK.js → dist-CLg13NtV.js} +0 -0
- /package/dist/assets/{graphlib-CAISHZh4.js → graphlib-DJkmx0Cq.js} +0 -0
- /package/dist/assets/{init-Rr1s_RiX.js → init-DlZdxViB.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-6uRLdFF7.js} +0 -0
- /package/dist/assets/{plugin-api-BbUkYm3Y.js → plugin-api-Uu2xAB4d.js} +0 -0
- /package/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-Bud4g4fh.js} +0 -0
- /package/dist/assets/{xterm-CGiQzDiR.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
|
@@ -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 { readJsonBody, sendJson, decodeSegment } from '../utils/response.mjs'
|
|
4
4
|
import { readStore, atomicUpdate } from '../storage.mjs'
|
|
@@ -6,6 +6,7 @@ import { createAgent, getSessionEventBus, agentEvents, persistSessionState, abor
|
|
|
6
6
|
import { agentProfileSnapshot, getAgentProfile } from '../agent-profiles.mjs'
|
|
7
7
|
import { projectContextFromId, readProjectConfig } from '../project-config.mjs'
|
|
8
8
|
import { logger } from '../utils/logger.mjs'
|
|
9
|
+
import { resolveModelBinding } from '../model-catalog.mjs'
|
|
9
10
|
import {
|
|
10
11
|
dayMs,
|
|
11
12
|
formatLocalDateTime,
|
|
@@ -133,10 +134,14 @@ async function getApiKey(provider) {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
async function parseScheduledTaskInstructionWithAi(instruction,
|
|
137
|
+
async function parseScheduledTaskInstructionWithAi(instruction, modelInput, thinkingLevel = 'off', context = {}) {
|
|
137
138
|
const text = String(instruction || '').trim()
|
|
138
139
|
if (!text) return { needMoreInfo: true, question: '请输入要创建的定时任务。' }
|
|
139
|
-
if (!
|
|
140
|
+
if (!modelInput) return { needMoreInfo: true, question: '请先选择用于解析任务的大模型。' }
|
|
141
|
+
const { model } = await resolveModelBinding(
|
|
142
|
+
modelInput?.modelRef || modelInput?.model ? modelInput : { model: modelInput },
|
|
143
|
+
{ context, legacySnapshot: modelInput?.model || modelInput },
|
|
144
|
+
)
|
|
140
145
|
|
|
141
146
|
const now = new Date()
|
|
142
147
|
const systemPrompt = `你是定时任务解析器。把用户的中文自然语言定时任务解析为 JSON。
|
|
@@ -154,7 +159,7 @@ async function parseScheduledTaskInstructionWithAi(instruction, model, thinkingL
|
|
|
154
159
|
示例输出:{"title":"生成日报","instruction":"生成销售日报","cronExpression":"0 9 * * *","scheduleRule":"每天 09:00","question":""}`
|
|
155
160
|
|
|
156
161
|
try {
|
|
157
|
-
const stream =
|
|
162
|
+
const stream = streamSimpleWithAiHttpLogging(
|
|
158
163
|
model,
|
|
159
164
|
{
|
|
160
165
|
systemPrompt,
|
|
@@ -529,6 +534,18 @@ async function executeTask(task, trigger = 'schedule', onStarted) {
|
|
|
529
534
|
|
|
530
535
|
try {
|
|
531
536
|
const executionProject = await resolveExecutionProject(task)
|
|
537
|
+
const binding = task.modelRef || task.model
|
|
538
|
+
? await resolveModelBinding(
|
|
539
|
+
task.modelRef ? { modelRef: task.modelRef } : { model: task.model },
|
|
540
|
+
{
|
|
541
|
+
context: { source: 'scheduled', allowCloud: true },
|
|
542
|
+
currentModel: task.model,
|
|
543
|
+
allowCurrentHidden: true,
|
|
544
|
+
forExecution: true,
|
|
545
|
+
legacySnapshot: task.model,
|
|
546
|
+
},
|
|
547
|
+
)
|
|
548
|
+
: null
|
|
532
549
|
if (task.agentId) {
|
|
533
550
|
executionAgent = await getAgentProfile(task.agentId, { projectId: executionProject?.id || null, workspaceRoot: executionProject?.path })
|
|
534
551
|
if (!executionAgent) agentWarning = `Configured agent not found: ${task.agentId}`
|
|
@@ -541,7 +558,9 @@ async function executeTask(task, trigger = 'schedule', onStarted) {
|
|
|
541
558
|
scope: executionProject ? 'project' : 'global',
|
|
542
559
|
projectId: executionProject?.id || null,
|
|
543
560
|
yoloMode,
|
|
544
|
-
model: task.model,
|
|
561
|
+
model: binding?.model || task.model,
|
|
562
|
+
modelRef: binding?.modelRef || task.modelRef || null,
|
|
563
|
+
modelAccessContext: { source: 'scheduled', allowCloud: true },
|
|
545
564
|
thinkingLevel: task.thinkingLevel,
|
|
546
565
|
title: `[定时任务] ${task.title}`,
|
|
547
566
|
agentProfile: executionAgent,
|
|
@@ -577,7 +596,7 @@ async function executeTask(task, trigger = 'schedule', onStarted) {
|
|
|
577
596
|
onStarted?.({ taskId: task.id, runId, sessionId })
|
|
578
597
|
|
|
579
598
|
const eventBus = getSessionEventBus(sessionId)
|
|
580
|
-
const runtimeLimitMs = Math.max(1000, Math.min(Number(executionAgent?.maxRuntimeMs ||
|
|
599
|
+
const runtimeLimitMs = Math.max(1000, Math.min(Number(executionAgent?.maxRuntimeMs || 60 * 60 * 1000), 60 * 60 * 1000))
|
|
581
600
|
let timeout = null
|
|
582
601
|
let handler = null
|
|
583
602
|
let timedOut = false
|
|
@@ -770,12 +789,12 @@ export function stopScheduledTaskRunner() {
|
|
|
770
789
|
runningTaskRunIds.clear()
|
|
771
790
|
}
|
|
772
791
|
|
|
773
|
-
export async function handleScheduledTasksApi(req, res, url) {
|
|
792
|
+
export async function handleScheduledTasksApi(req, res, url, context = {}) {
|
|
774
793
|
const parts = url.pathname.split('/').filter(Boolean)
|
|
775
794
|
|
|
776
795
|
if (req.method === 'POST' && url.pathname === '/api/scheduled-tasks/parse') {
|
|
777
796
|
const body = await readJsonBody(req)
|
|
778
|
-
sendJson(res, 200, await parseScheduledTaskInstructionWithAi(body?.instruction, body?.model, body?.thinkingLevel))
|
|
797
|
+
sendJson(res, 200, await parseScheduledTaskInstructionWithAi(body?.instruction, body?.modelRef ? body : body?.model, body?.thinkingLevel, context))
|
|
779
798
|
return
|
|
780
799
|
}
|
|
781
800
|
|
|
@@ -796,12 +815,14 @@ export async function handleScheduledTasksApi(req, res, url) {
|
|
|
796
815
|
const now = new Date().toISOString()
|
|
797
816
|
const normalized = normalizeTaskInput(parsed)
|
|
798
817
|
const enabled = parsed.enabled !== false && parsed.status !== 'paused'
|
|
818
|
+
const binding = await resolveModelBinding(body, { context, legacySnapshot: body?.model })
|
|
799
819
|
const task = {
|
|
800
820
|
id: createId(),
|
|
801
821
|
...normalized,
|
|
802
822
|
scheduleRule: normalized.scheduleRule || scheduleRuleFor(normalized),
|
|
803
823
|
executionMode: normalized.executionMode || 'serial',
|
|
804
|
-
model:
|
|
824
|
+
model: binding.model,
|
|
825
|
+
modelRef: binding.modelRef,
|
|
805
826
|
thinkingLevel: body?.thinkingLevel || (body?.model?.reasoning ? 'medium' : 'off'),
|
|
806
827
|
projectId: body?.projectId || null,
|
|
807
828
|
projectName: body?.projectName || null,
|
|
@@ -830,6 +851,14 @@ export async function handleScheduledTasksApi(req, res, url) {
|
|
|
830
851
|
const parsed = body?.task
|
|
831
852
|
if (!parsed) throw requestError('Missing task')
|
|
832
853
|
const normalized = normalizeTaskInput(parsed, existing)
|
|
854
|
+
const binding = body?.modelRef || body?.model
|
|
855
|
+
? await resolveModelBinding(body, {
|
|
856
|
+
context,
|
|
857
|
+
currentModel: existing.model,
|
|
858
|
+
allowCurrentHidden: true,
|
|
859
|
+
legacySnapshot: body?.model,
|
|
860
|
+
})
|
|
861
|
+
: null
|
|
833
862
|
const now = new Date().toISOString()
|
|
834
863
|
const hasProject = Object.prototype.hasOwnProperty.call(body, 'projectId')
|
|
835
864
|
const task = await updateTask(taskId, (current) => ({
|
|
@@ -837,7 +866,8 @@ export async function handleScheduledTasksApi(req, res, url) {
|
|
|
837
866
|
...normalized,
|
|
838
867
|
scheduleRule: normalized.scheduleRule || scheduleRuleFor(normalized),
|
|
839
868
|
executionMode: normalized.executionMode || 'serial',
|
|
840
|
-
model:
|
|
869
|
+
model: binding?.model ?? current.model,
|
|
870
|
+
modelRef: binding?.modelRef ?? current.modelRef,
|
|
841
871
|
thinkingLevel: body?.thinkingLevel ?? current.thinkingLevel,
|
|
842
872
|
projectId: hasProject ? (body.projectId || null) : current.projectId,
|
|
843
873
|
projectName: hasProject ? (body.projectName || null) : current.projectName,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
|
|
2
|
-
import { readSessionValue
|
|
2
|
+
import { readSessionValue } from '../storage.mjs'
|
|
3
|
+
import { listModelCatalog, resolveModelBinding } from '../model-catalog.mjs'
|
|
3
4
|
import { sendSessionAsset } from './session-assets.mjs'
|
|
4
5
|
import { abortRun, restoreAgent, runPrompt, getSessionState, getSessionEventBus, updateSessionModel, updateSessionThinkingLevel } from '../agent-manager.mjs'
|
|
5
6
|
import {
|
|
6
7
|
assertShareActive,
|
|
7
8
|
issueConversationShareToken,
|
|
9
|
+
onConversationShareInvalidated,
|
|
8
10
|
parseCookies,
|
|
9
11
|
readConversationShare,
|
|
10
12
|
rollbackSharedSessionMessages,
|
|
@@ -49,6 +51,7 @@ function publicSharePayload(record) {
|
|
|
49
51
|
scope: record.scope,
|
|
50
52
|
projectId: record.projectId,
|
|
51
53
|
hasPassword: Boolean(record.passwordHash),
|
|
54
|
+
allowCloudUsage: record.allowCloudUsage === true,
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
|
|
@@ -101,13 +104,6 @@ function sanitizeModel(model) {
|
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
function sanitizeIncomingModel(model) {
|
|
105
|
-
if (!model || typeof model !== 'object') return null
|
|
106
|
-
const sanitized = sanitizeModel(model)
|
|
107
|
-
if (!sanitized.id || !sanitized.provider) return null
|
|
108
|
-
return sanitized
|
|
109
|
-
}
|
|
110
|
-
|
|
111
107
|
function sanitizeEvent(event) {
|
|
112
108
|
if (!event || typeof event !== 'object') return event
|
|
113
109
|
const next = { ...event }
|
|
@@ -147,6 +143,20 @@ async function handleSharedEvents(req, res, record) {
|
|
|
147
143
|
|
|
148
144
|
writeSseEvent(res, 'state', await sharedSessionPayload(record))
|
|
149
145
|
|
|
146
|
+
let cleanedUp = false
|
|
147
|
+
let expirationTimeout
|
|
148
|
+
let removeShareInvalidationListener = () => undefined
|
|
149
|
+
|
|
150
|
+
const cleanup = () => {
|
|
151
|
+
if (cleanedUp) return
|
|
152
|
+
cleanedUp = true
|
|
153
|
+
clearInterval(keepAlive)
|
|
154
|
+
if (expirationTimeout) clearTimeout(expirationTimeout)
|
|
155
|
+
removeShareInvalidationListener()
|
|
156
|
+
eventBus.removeListener('agent_event', onAgentEvent)
|
|
157
|
+
if (!res.writableEnded) res.end()
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
const keepAlive = setInterval(() => {
|
|
151
161
|
try {
|
|
152
162
|
res.write(': ping\n\n')
|
|
@@ -164,11 +174,19 @@ async function handleSharedEvents(req, res, record) {
|
|
|
164
174
|
}
|
|
165
175
|
}
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
removeShareInvalidationListener = onConversationShareInvalidated(({ shareId }) => {
|
|
178
|
+
if (shareId === record.id) cleanup()
|
|
179
|
+
})
|
|
180
|
+
const scheduleExpiration = () => {
|
|
181
|
+
if (!record.expiresAt || cleanedUp) return
|
|
182
|
+
const delay = Date.parse(record.expiresAt) - Date.now()
|
|
183
|
+
if (delay <= 0) {
|
|
184
|
+
cleanup()
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
expirationTimeout = setTimeout(scheduleExpiration, Math.min(delay, 2_147_483_647))
|
|
171
188
|
}
|
|
189
|
+
scheduleExpiration()
|
|
172
190
|
|
|
173
191
|
eventBus.on('agent_event', onAgentEvent)
|
|
174
192
|
req.on('close', cleanup)
|
|
@@ -269,42 +287,7 @@ async function sharedSessionPayload(record) {
|
|
|
269
287
|
return sanitizeSession(session, record)
|
|
270
288
|
}
|
|
271
289
|
|
|
272
|
-
function
|
|
273
|
-
if (!provider || typeof provider !== 'object') return null
|
|
274
|
-
const models = Array.isArray(provider.models)
|
|
275
|
-
? provider.models.map(sanitizeModel).filter((model) => model?.id && model?.provider && model?.api)
|
|
276
|
-
: []
|
|
277
|
-
if (!models.length) return null
|
|
278
|
-
return {
|
|
279
|
-
id: provider.id || provider.name || models[0].provider,
|
|
280
|
-
name: provider.name || models[0].provider,
|
|
281
|
-
type: provider.type || models[0].api,
|
|
282
|
-
baseUrl: provider.baseUrl,
|
|
283
|
-
models,
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function listSharedModelProviders() {
|
|
288
|
-
const providers = await readStore('custom-providers')
|
|
289
|
-
return Object.values(providers || {}).map(sanitizeProvider).filter(Boolean)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async function readConfiguredModel(model) {
|
|
293
|
-
if (!model || typeof model !== 'object') return null
|
|
294
|
-
const providers = await readStore('custom-providers')
|
|
295
|
-
for (const provider of Object.values(providers || {})) {
|
|
296
|
-
if (!provider || typeof provider !== 'object') continue
|
|
297
|
-
const matched = Array.isArray(provider.models)
|
|
298
|
-
? provider.models.find((candidate) => {
|
|
299
|
-
return candidate?.id === model.id && candidate?.provider === model.provider && candidate?.api === model.api
|
|
300
|
-
})
|
|
301
|
-
: undefined
|
|
302
|
-
if (matched) return matched
|
|
303
|
-
}
|
|
304
|
-
return null
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export async function handleSharedConversationApi(req, res, url) {
|
|
290
|
+
export async function handleSharedConversationApi(req, res, url, context = {}) {
|
|
308
291
|
const parts = url.pathname.split('/').filter(Boolean)
|
|
309
292
|
const shareId = decodeSegment(parts[2])
|
|
310
293
|
const action = parts[3]
|
|
@@ -360,7 +343,21 @@ export async function handleSharedConversationApi(req, res, url) {
|
|
|
360
343
|
|
|
361
344
|
if (req.method === 'GET' && action === 'models') {
|
|
362
345
|
assertOperate(record)
|
|
363
|
-
|
|
346
|
+
const session = await sharedSessionPayload(record)
|
|
347
|
+
const models = await listModelCatalog({
|
|
348
|
+
context: { ...context, allowCloud: record.allowCloudUsage === true },
|
|
349
|
+
currentModel: session.model,
|
|
350
|
+
})
|
|
351
|
+
const providers = []
|
|
352
|
+
for (const model of models) {
|
|
353
|
+
let provider = providers.find((item) => item.id === model.provider)
|
|
354
|
+
if (!provider) {
|
|
355
|
+
provider = { id: model.provider, name: model.provider, type: model.api, baseUrl: model.baseUrl, models: [] }
|
|
356
|
+
providers.push(provider)
|
|
357
|
+
}
|
|
358
|
+
provider.models.push(sanitizeModel(model))
|
|
359
|
+
}
|
|
360
|
+
sendJson(res, 200, { providers })
|
|
364
361
|
return
|
|
365
362
|
}
|
|
366
363
|
|
|
@@ -373,7 +370,14 @@ export async function handleSharedConversationApi(req, res, url) {
|
|
|
373
370
|
assertOperate(record)
|
|
374
371
|
const body = await readJsonBody(req)
|
|
375
372
|
await restoreAgent(record.sessionId)
|
|
376
|
-
const result = await runPrompt(
|
|
373
|
+
const result = await runPrompt(
|
|
374
|
+
record.sessionId,
|
|
375
|
+
messageFromBody(body, record, req),
|
|
376
|
+
[],
|
|
377
|
+
body?.command,
|
|
378
|
+
null,
|
|
379
|
+
{ ...context, allowCloud: record.allowCloudUsage === true, source: 'shared' },
|
|
380
|
+
)
|
|
377
381
|
sendJson(res, 200, result)
|
|
378
382
|
return
|
|
379
383
|
}
|
|
@@ -381,15 +385,15 @@ export async function handleSharedConversationApi(req, res, url) {
|
|
|
381
385
|
if (req.method === 'POST' && action === 'model') {
|
|
382
386
|
assertOperate(record)
|
|
383
387
|
const body = await readJsonBody(req)
|
|
384
|
-
const model = sanitizeIncomingModel(body?.model)
|
|
385
|
-
if (!model) {
|
|
386
|
-
const error = new Error('Missing model in request body')
|
|
387
|
-
error.statusCode = 400
|
|
388
|
-
throw error
|
|
389
|
-
}
|
|
390
388
|
await restoreAgent(record.sessionId)
|
|
391
|
-
const
|
|
392
|
-
|
|
389
|
+
const currentModel = getSessionState(record.sessionId)?.model
|
|
390
|
+
const binding = await resolveModelBinding(body, {
|
|
391
|
+
context: { ...context, allowCloud: record.allowCloudUsage === true, source: 'shared' },
|
|
392
|
+
currentModel,
|
|
393
|
+
allowCurrentHidden: true,
|
|
394
|
+
legacySnapshot: body?.model,
|
|
395
|
+
})
|
|
396
|
+
sendJson(res, 200, updateSessionModel(record.sessionId, sanitizeModel(binding.model), binding.modelRef))
|
|
393
397
|
return
|
|
394
398
|
}
|
|
395
399
|
|
package/server/routes/shares.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
|
|
2
2
|
import { readSessionValue } from '../storage.mjs'
|
|
3
|
-
import { getLanUrls } from '../utils/network.mjs'
|
|
3
|
+
import { getLanUrls, isTailscaleAddress } from '../utils/network.mjs'
|
|
4
4
|
import {
|
|
5
5
|
createConversationShare,
|
|
6
|
+
deleteConversationShare,
|
|
6
7
|
listConversationShares,
|
|
8
|
+
restoreConversationShare,
|
|
7
9
|
revokeConversationShare,
|
|
10
|
+
updateConversationShare,
|
|
11
|
+
updateConversationShareExpiration,
|
|
8
12
|
} from '../share-store.mjs'
|
|
9
13
|
|
|
10
14
|
function localBaseUrl(req, port) {
|
|
@@ -46,6 +50,13 @@ export async function handleSharesApi(req, res, url, context = {}) {
|
|
|
46
50
|
const passwordProvided = typeof body?.password === 'string'
|
|
47
51
|
const password = passwordProvided ? body.password.trim() : undefined
|
|
48
52
|
const expiresAt = typeof body?.expiresAt === 'string' && body.expiresAt ? body.expiresAt : undefined
|
|
53
|
+
const allowCloudUsage = permission === 'operate' && body?.allowCloudUsage === true
|
|
54
|
+
if (allowCloudUsage && context.isLocalRequest === false
|
|
55
|
+
&& !(context.remoteAuthorized === true && isTailscaleAddress(context.remoteAddress))) {
|
|
56
|
+
const error = new Error('QuickForge Cloud sharing can only be enabled locally or from an authorized Tailscale client.')
|
|
57
|
+
error.statusCode = 403
|
|
58
|
+
throw error
|
|
59
|
+
}
|
|
49
60
|
|
|
50
61
|
const session = sessionId ? await readSessionValue(sessionId) : null
|
|
51
62
|
if (!session) {
|
|
@@ -59,6 +70,7 @@ export async function handleSharesApi(req, res, url, context = {}) {
|
|
|
59
70
|
permission,
|
|
60
71
|
password: passwordProvided ? password : undefined,
|
|
61
72
|
expiresAt,
|
|
73
|
+
allowCloudUsage,
|
|
62
74
|
titleSnapshot: session.title,
|
|
63
75
|
scope: session.scope,
|
|
64
76
|
projectId: session.projectId,
|
|
@@ -77,6 +89,69 @@ export async function handleSharesApi(req, res, url, context = {}) {
|
|
|
77
89
|
return
|
|
78
90
|
}
|
|
79
91
|
|
|
92
|
+
if (parts.length === 4 && parts[0] === 'api' && parts[1] === 'shares') {
|
|
93
|
+
const shareId = decodeSegment(parts[2])
|
|
94
|
+
const action = parts[3]
|
|
95
|
+
|
|
96
|
+
if (req.method === 'POST' && action === 'disable') {
|
|
97
|
+
const share = await revokeConversationShare(shareId)
|
|
98
|
+
sendJson(res, 200, { ok: true, share })
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (req.method === 'POST' && action === 'restore') {
|
|
103
|
+
const body = await readJsonBody(req)
|
|
104
|
+
const expiresAt = typeof body?.expiresAt === 'string' && body.expiresAt ? body.expiresAt : undefined
|
|
105
|
+
const currentShares = await listConversationShares()
|
|
106
|
+
const current = currentShares.find((share) => share.id === shareId)
|
|
107
|
+
if (!current) {
|
|
108
|
+
const error = new Error('Share not found')
|
|
109
|
+
error.statusCode = 404
|
|
110
|
+
throw error
|
|
111
|
+
}
|
|
112
|
+
const session = current.sessionId ? await readSessionValue(current.sessionId) : null
|
|
113
|
+
if (!session) {
|
|
114
|
+
const error = new Error('Session not found')
|
|
115
|
+
error.statusCode = 404
|
|
116
|
+
throw error
|
|
117
|
+
}
|
|
118
|
+
const share = await restoreConversationShare(shareId, expiresAt)
|
|
119
|
+
sendJson(res, 200, { ok: true, share })
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (req.method === 'POST' && action === 'expiration') {
|
|
124
|
+
const body = await readJsonBody(req)
|
|
125
|
+
const expiresAt = typeof body?.expiresAt === 'string' && body.expiresAt ? body.expiresAt : undefined
|
|
126
|
+
const share = await updateConversationShareExpiration(shareId, expiresAt)
|
|
127
|
+
sendJson(res, 200, { ok: true, share })
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (req.method === 'POST' && action === 'update') {
|
|
132
|
+
const body = await readJsonBody(req)
|
|
133
|
+
const permission = body?.permission
|
|
134
|
+
const password = typeof body?.password === 'string' ? body.password : undefined
|
|
135
|
+
const expiresAt = typeof body?.expiresAt === 'string' && body.expiresAt ? body.expiresAt : undefined
|
|
136
|
+
const allowCloudUsage = body?.allowCloudUsage
|
|
137
|
+
if (allowCloudUsage === true && context.isLocalRequest === false
|
|
138
|
+
&& !(context.remoteAuthorized === true && isTailscaleAddress(context.remoteAddress))) {
|
|
139
|
+
const error = new Error('QuickForge Cloud sharing can only be enabled locally or from an authorized Tailscale client.')
|
|
140
|
+
error.statusCode = 403
|
|
141
|
+
throw error
|
|
142
|
+
}
|
|
143
|
+
const share = await updateConversationShare(shareId, { permission, password, expiresAt, allowCloudUsage })
|
|
144
|
+
sendJson(res, 200, { ok: true, share })
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (req.method === 'DELETE' && action === 'permanent') {
|
|
149
|
+
const share = await deleteConversationShare(shareId)
|
|
150
|
+
sendJson(res, 200, { ok: true, share })
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
80
155
|
if (req.method === 'DELETE' && parts.length === 3 && parts[0] === 'api' && parts[1] === 'shares') {
|
|
81
156
|
const shareId = decodeSegment(parts[2])
|
|
82
157
|
const share = await revokeConversationShare(shareId)
|
package/server/routes/static.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
4
4
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url)
|
|
6
6
|
const projectRoot = path.resolve(path.dirname(__filename), '../..')
|
|
7
|
+
const androidApkPath = path.join(projectRoot, 'android', 'app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk')
|
|
7
8
|
|
|
8
9
|
function getContentType(filePath) {
|
|
9
10
|
const extension = path.extname(filePath).toLowerCase()
|
|
@@ -45,7 +46,31 @@ function shouldFallbackToIndex(url, pathname) {
|
|
|
45
46
|
return !path.extname(pathname)
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function cacheControlFor(filePath) {
|
|
50
|
+
if (filePath.endsWith('index.html')) return 'no-cache'
|
|
51
|
+
if (path.extname(filePath).toLowerCase() === '.apk') return 'no-cache'
|
|
52
|
+
return 'public, max-age=31536000, immutable'
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
export async function serveStatic(req, res, url) {
|
|
56
|
+
if (url.pathname === '/downloads/quickforge-android.apk') {
|
|
57
|
+
try {
|
|
58
|
+
const data = await fs.readFile(androidApkPath)
|
|
59
|
+
res.writeHead(200, {
|
|
60
|
+
'content-type': 'application/vnd.android.package-archive',
|
|
61
|
+
'content-length': data.length,
|
|
62
|
+
'content-disposition': 'attachment; filename="quickforge-android.apk"',
|
|
63
|
+
'cache-control': 'no-cache',
|
|
64
|
+
'x-content-type-options': 'nosniff',
|
|
65
|
+
})
|
|
66
|
+
res.end(data)
|
|
67
|
+
} catch {
|
|
68
|
+
res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' })
|
|
69
|
+
res.end('Android APK not found. Run npm run android:build first.')
|
|
70
|
+
}
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
const distDir = path.join(projectRoot, 'dist')
|
|
50
75
|
const requested = decodeURIComponent(requestPathname(url))
|
|
51
76
|
const normalized = path.normalize(requested).replace(/^([.][.][/])+/, '').replace(/^[/\\]+/, '')
|
|
@@ -74,7 +99,7 @@ export async function serveStatic(req, res, url) {
|
|
|
74
99
|
const data = await fs.readFile(filePath)
|
|
75
100
|
res.writeHead(200, {
|
|
76
101
|
'content-type': getContentType(filePath),
|
|
77
|
-
'cache-control': filePath
|
|
102
|
+
'cache-control': cacheControlFor(filePath),
|
|
78
103
|
})
|
|
79
104
|
res.end(data)
|
|
80
105
|
} catch {
|
|
@@ -94,7 +94,7 @@ async function readIndexedValues(store, indexName, direction, scope, projectId,
|
|
|
94
94
|
return values
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export async function handleStorageApi(req, res, url) {
|
|
97
|
+
export async function handleStorageApi(req, res, url, context = {}) {
|
|
98
98
|
const parts = url.pathname.split('/').filter(Boolean)
|
|
99
99
|
|
|
100
100
|
if (req.method === 'GET' && url.pathname === '/api/storage/quota') {
|
|
@@ -116,6 +116,29 @@ export async function handleStorageApi(req, res, url) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const store = decodeSegment(parts[2])
|
|
119
|
+
const remotelySensitiveStores = new Set([
|
|
120
|
+
'provider-keys',
|
|
121
|
+
'custom-providers',
|
|
122
|
+
'mcp',
|
|
123
|
+
'plugins',
|
|
124
|
+
'scheduled-tasks',
|
|
125
|
+
'agent-profile-overrides',
|
|
126
|
+
'custom-agents',
|
|
127
|
+
])
|
|
128
|
+
if (context.isLocalRequest === false && remotelySensitiveStores.has(store)) {
|
|
129
|
+
const error = new Error('This storage area is available only on this computer.')
|
|
130
|
+
error.statusCode = 403
|
|
131
|
+
error.errorCode = 'storage_local_only'
|
|
132
|
+
throw error
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const remotelyReadOnlyStores = new Set(['sessions', 'sessions-metadata'])
|
|
136
|
+
if (context.isLocalRequest === false && remotelyReadOnlyStores.has(store) && req.method !== 'GET') {
|
|
137
|
+
const error = new Error('Remote clients must modify conversations through the Agent API.')
|
|
138
|
+
error.statusCode = 403
|
|
139
|
+
error.errorCode = 'storage_remote_read_only'
|
|
140
|
+
throw error
|
|
141
|
+
}
|
|
119
142
|
|
|
120
143
|
if (req.method === 'GET' && parts[3] === 'keys') {
|
|
121
144
|
const prefix = url.searchParams.get('prefix') || ''
|
package/server/routes/system.mjs
CHANGED
|
@@ -36,6 +36,12 @@ export async function handleSystemApi(req, res, url, context) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (req.method === 'POST' && url.pathname === '/api/system/restart') {
|
|
39
|
+
if (!context.isLocalRequest) {
|
|
40
|
+
const error = new Error('Restart is only allowed from this computer')
|
|
41
|
+
error.statusCode = 403
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
if (req.headers['x-quickforge-action'] !== 'restart') {
|
|
40
46
|
const error = new Error('Forbidden action')
|
|
41
47
|
error.statusCode = 403
|
|
@@ -59,6 +65,11 @@ export async function handleSystemApi(req, res, url, context) {
|
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
if (req.method === 'PUT') {
|
|
68
|
+
if (!context.isLocalRequest) {
|
|
69
|
+
const error = new Error('Terminal shell settings can only be changed from this computer')
|
|
70
|
+
error.statusCode = 403
|
|
71
|
+
throw error
|
|
72
|
+
}
|
|
62
73
|
const body = await readJsonBody(req, 64 * 1024) || {}
|
|
63
74
|
if (Array.isArray(body.profiles) || typeof body.defaultProfileId === 'string') {
|
|
64
75
|
sendJson(res, 200, await context.updateTerminalShellConfig(body))
|
|
@@ -59,7 +59,7 @@ export async function handleTerminalApi(req, res, url, options = {}) {
|
|
|
59
59
|
await assertLocalTerminalRequest(req, options.isLocalRequest)
|
|
60
60
|
|
|
61
61
|
if (req.method === 'GET' && url.pathname === '/api/terminal/capabilities') {
|
|
62
|
-
sendJson(res, 200, { ...await terminalCapabilities(), ...await platformInfo() })
|
|
62
|
+
sendJson(res, 200, { ...await terminalCapabilities(), ...await platformInfo() }, 'private, max-age=300')
|
|
63
63
|
return
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -137,7 +137,12 @@ async function handleTerminalUpgradeAsync(req, socket, head, url, options = {})
|
|
|
137
137
|
attachTerminalClient(sessionId, ws)
|
|
138
138
|
} catch (err) {
|
|
139
139
|
try {
|
|
140
|
-
ws.send(JSON.stringify({
|
|
140
|
+
ws.send(JSON.stringify({
|
|
141
|
+
type: 'error',
|
|
142
|
+
code: err?.statusCode === 404 ? 'SESSION_NOT_FOUND' : 'TERMINAL_CONNECTION_FAILED',
|
|
143
|
+
message: err?.message || 'Terminal connection failed',
|
|
144
|
+
retryable: err?.statusCode !== 404,
|
|
145
|
+
}), () => ws.close())
|
|
141
146
|
} catch {
|
|
142
147
|
ws.close()
|
|
143
148
|
}
|