@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,7 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { spawn } from 'node:child_process'
|
|
4
|
-
import {
|
|
4
|
+
import { streamSimpleWithAiHttpLogging } from '../ai-http-logger.mjs'
|
|
5
|
+
import { resolveModelBinding } from '../model-catalog.mjs'
|
|
5
6
|
import { DEFAULT_AI_MAX_RETRIES } from '../ai-provider-options.mjs'
|
|
6
7
|
import { sendJson, readJsonBody } from '../utils/response.mjs'
|
|
7
8
|
import { projectContextFromId } from '../project-config.mjs'
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
|
|
18
19
|
const MAX_PREVIEW_BYTES = 50 * 1024 * 1024
|
|
19
20
|
const MAX_STATIC_PREVIEW_BYTES = 50 * 1024 * 1024
|
|
21
|
+
const DEFAULT_GIT_TIMEOUT_MS = 2 * 60 * 1000
|
|
20
22
|
const MAX_GIT_LINE_COUNT_FILES = 100
|
|
21
23
|
const MAX_GIT_LINE_COUNT_FILE_BYTES = 1024 * 1024
|
|
22
24
|
const MAX_GIT_LINE_COUNT_TOTAL_BYTES = 10 * 1024 * 1024
|
|
@@ -29,7 +31,7 @@ const extensionLanguageMap = new Map([
|
|
|
29
31
|
['ts', 'typescript'], ['tsx', 'typescript'], ['js', 'javascript'], ['jsx', 'javascript'],
|
|
30
32
|
['mjs', 'javascript'], ['cjs', 'javascript'], ['json', 'json'], ['jsonc', 'json'],
|
|
31
33
|
['css', 'css'], ['scss', 'scss'], ['less', 'less'], ['html', 'html'], ['htm', 'html'],
|
|
32
|
-
['md', 'markdown'], ['markdown', 'markdown'], ['py', 'python'], ['rb', 'ruby'], ['go', 'go'],
|
|
34
|
+
['md', 'markdown'], ['mdx', 'markdown'], ['markdown', 'markdown'], ['py', 'python'], ['rb', 'ruby'], ['go', 'go'],
|
|
33
35
|
['rs', 'rust'], ['java', 'java'], ['c', 'c'], ['h', 'c'], ['cpp', 'cpp'], ['cc', 'cpp'],
|
|
34
36
|
['cxx', 'cpp'], ['hpp', 'cpp'], ['cs', 'csharp'], ['php', 'php'], ['swift', 'swift'],
|
|
35
37
|
['kt', 'kotlin'], ['kts', 'kotlin'], ['sh', 'shell'], ['bash', 'shell'], ['zsh', 'shell'],
|
|
@@ -76,20 +78,65 @@ async function projectContextFromUrl(url) {
|
|
|
76
78
|
return projectContextFromId(projectId)
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
function
|
|
81
|
+
function killProcessTree(child) {
|
|
82
|
+
if (!child?.pid) return
|
|
83
|
+
if (process.platform === 'win32') {
|
|
84
|
+
const killer = spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], {
|
|
85
|
+
stdio: 'ignore',
|
|
86
|
+
windowsHide: true,
|
|
87
|
+
})
|
|
88
|
+
killer.on('error', () => {
|
|
89
|
+
try { child.kill('SIGKILL') } catch { /* ignore */ }
|
|
90
|
+
})
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
process.kill(-child.pid, 'SIGKILL')
|
|
95
|
+
} catch {
|
|
96
|
+
try { child.kill('SIGKILL') } catch { /* ignore */ }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function git(args, cwd, options = {}) {
|
|
80
101
|
return new Promise((resolve, reject) => {
|
|
102
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(1, options.timeoutMs) : DEFAULT_GIT_TIMEOUT_MS
|
|
81
103
|
const child = spawn('git', args, {
|
|
82
104
|
cwd,
|
|
83
105
|
shell: false,
|
|
84
106
|
windowsHide: true,
|
|
85
|
-
|
|
107
|
+
detached: process.platform !== 'win32',
|
|
108
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
109
|
+
env: {
|
|
110
|
+
...process.env,
|
|
111
|
+
GIT_OPTIONAL_LOCKS: '0',
|
|
112
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
113
|
+
GCM_INTERACTIVE: 'Never',
|
|
114
|
+
},
|
|
86
115
|
})
|
|
87
116
|
const stdout = []
|
|
88
117
|
const stderr = []
|
|
118
|
+
let settled = false
|
|
119
|
+
const finish = (callback) => {
|
|
120
|
+
if (settled) return
|
|
121
|
+
settled = true
|
|
122
|
+
clearTimeout(timeout)
|
|
123
|
+
callback()
|
|
124
|
+
}
|
|
125
|
+
const timeout = setTimeout(() => {
|
|
126
|
+
killProcessTree(child)
|
|
127
|
+
finish(() => {
|
|
128
|
+
const error = new Error(`git ${args.join(' ')} timed out after ${timeoutMs}ms`)
|
|
129
|
+
error.statusCode = 504
|
|
130
|
+
error.code = 'GIT_TIMEOUT'
|
|
131
|
+
reject(error)
|
|
132
|
+
})
|
|
133
|
+
}, timeoutMs)
|
|
134
|
+
timeout.unref?.()
|
|
135
|
+
|
|
89
136
|
child.stdout.on('data', (chunk) => stdout.push(chunk))
|
|
90
137
|
child.stderr.on('data', (chunk) => stderr.push(chunk))
|
|
91
|
-
child.once('error', reject)
|
|
92
|
-
child.once('close', (code) => {
|
|
138
|
+
child.once('error', (error) => finish(() => reject(error)))
|
|
139
|
+
child.once('close', (code) => finish(() => {
|
|
93
140
|
const out = Buffer.concat(stdout)
|
|
94
141
|
const err = Buffer.concat(stderr).toString('utf8').trim()
|
|
95
142
|
if (code === 0 || options.allowFailure) {
|
|
@@ -99,7 +146,7 @@ function git(args, cwd, options = {}) {
|
|
|
99
146
|
error.statusCode = 400
|
|
100
147
|
reject(error)
|
|
101
148
|
}
|
|
102
|
-
})
|
|
149
|
+
}))
|
|
103
150
|
})
|
|
104
151
|
}
|
|
105
152
|
|
|
@@ -619,7 +666,7 @@ Unstaged diff:
|
|
|
619
666
|
${trimForPrompt(worktreeDiff)}`
|
|
620
667
|
|
|
621
668
|
try {
|
|
622
|
-
const stream =
|
|
669
|
+
const stream = streamSimpleWithAiHttpLogging(
|
|
623
670
|
model,
|
|
624
671
|
{
|
|
625
672
|
systemPrompt,
|
|
@@ -1034,9 +1081,10 @@ async function contextFromGitBody(req) {
|
|
|
1034
1081
|
return { context: await projectContextFromId(projectId), body }
|
|
1035
1082
|
}
|
|
1036
1083
|
|
|
1037
|
-
async function handleGitGenerateCommitMessage(req, res) {
|
|
1084
|
+
async function handleGitGenerateCommitMessage(req, res, requestContext = {}) {
|
|
1038
1085
|
const { context, body } = await contextFromGitBody(req)
|
|
1039
|
-
const
|
|
1086
|
+
const binding = await resolveModelBinding(body, { context: requestContext, legacySnapshot: body?.model })
|
|
1087
|
+
const message = await generateGitCommitMessage(context, binding.model, body?.thinkingLevel, Boolean(body?.includeUnstaged))
|
|
1040
1088
|
sendJson(res, 200, { message })
|
|
1041
1089
|
}
|
|
1042
1090
|
|
|
@@ -1100,7 +1148,7 @@ async function handleGitCommitAndPush(req, res) {
|
|
|
1100
1148
|
sendJson(res, 200, await commitAndPushGitChanges(context, body?.message, Boolean(body?.includeUnstaged)))
|
|
1101
1149
|
}
|
|
1102
1150
|
|
|
1103
|
-
export async function handleWorkspaceApi(req, res, url) {
|
|
1151
|
+
export async function handleWorkspaceApi(req, res, url, requestContext = {}) {
|
|
1104
1152
|
if (req.method === 'GET' && url.pathname === '/api/workspace/tree') {
|
|
1105
1153
|
await handleWorkspaceTree(req, res, url)
|
|
1106
1154
|
return
|
|
@@ -1118,6 +1166,11 @@ export async function handleWorkspaceApi(req, res, url) {
|
|
|
1118
1166
|
return
|
|
1119
1167
|
}
|
|
1120
1168
|
if (req.method === 'POST' && url.pathname === '/api/workspace/open-external') {
|
|
1169
|
+
if (requestContext.isLocalRequest !== true) {
|
|
1170
|
+
const error = new Error('Opening applications is only allowed from this computer')
|
|
1171
|
+
error.statusCode = 403
|
|
1172
|
+
throw error
|
|
1173
|
+
}
|
|
1121
1174
|
await handleWorkspaceOpenExternal(req, res)
|
|
1122
1175
|
return
|
|
1123
1176
|
}
|
|
@@ -1127,7 +1180,7 @@ export async function handleWorkspaceApi(req, res, url) {
|
|
|
1127
1180
|
throw error
|
|
1128
1181
|
}
|
|
1129
1182
|
|
|
1130
|
-
export async function handleGitApi(req, res, url) {
|
|
1183
|
+
export async function handleGitApi(req, res, url, requestContext = {}) {
|
|
1131
1184
|
if (req.method === 'GET' && url.pathname === '/api/git/status') {
|
|
1132
1185
|
await handleGitStatus(req, res, url)
|
|
1133
1186
|
return
|
|
@@ -1153,7 +1206,7 @@ export async function handleGitApi(req, res, url) {
|
|
|
1153
1206
|
return
|
|
1154
1207
|
}
|
|
1155
1208
|
if (req.method === 'POST' && url.pathname === '/api/git/generate-commit-message') {
|
|
1156
|
-
await handleGitGenerateCommitMessage(req, res)
|
|
1209
|
+
await handleGitGenerateCommitMessage(req, res, requestContext)
|
|
1157
1210
|
return
|
|
1158
1211
|
}
|
|
1159
1212
|
if (req.method === 'POST' && url.pathname === '/api/git/stage') {
|
package/server/session-utils.mjs
CHANGED
|
@@ -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 { buildInstructionsPayload, projectContextFromId } from './project-config.mjs'
|
|
4
4
|
import { composeSystemPrompt } from './system-prompt.mjs'
|
|
@@ -88,7 +88,7 @@ export async function generateAiTitle(messages, model, thinkingLevel, getApiKey)
|
|
|
88
88
|
|
|
89
89
|
try {
|
|
90
90
|
const apiKey = getApiKey ? await getApiKey(model.provider) : undefined
|
|
91
|
-
const stream =
|
|
91
|
+
const stream = streamSimpleWithAiHttpLogging(
|
|
92
92
|
model,
|
|
93
93
|
{
|
|
94
94
|
systemPrompt: '你是对话标题生成器。请用和用户相同的语言,根据对话主题生成 3 到 5 个词的短标题。只输出标题,不要解释,不要标点。',
|
package/server/share-store.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
|
+
import { EventEmitter } from 'node:events'
|
|
2
3
|
import { promises as fs } from 'node:fs'
|
|
3
4
|
import path from 'node:path'
|
|
4
5
|
import { ensureStorage, storageDir } from './storage.mjs'
|
|
@@ -10,6 +11,25 @@ const SHARES_DIR = path.join(storageDir, 'shares')
|
|
|
10
11
|
const SHARES_FILE = path.join(SHARES_DIR, 'conversation-shares.json')
|
|
11
12
|
const writeQueueName = 'conversation-shares'
|
|
12
13
|
const writeQueues = new Map()
|
|
14
|
+
const shareLifecycleEvents = new EventEmitter()
|
|
15
|
+
shareLifecycleEvents.setMaxListeners(0)
|
|
16
|
+
|
|
17
|
+
export function onConversationShareInvalidated(listener) {
|
|
18
|
+
shareLifecycleEvents.on('invalidated', listener)
|
|
19
|
+
return () => shareLifecycleEvents.removeListener('invalidated', listener)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function emitConversationShareInvalidated(shareId, reason) {
|
|
23
|
+
shareLifecycleEvents.emit('invalidated', { shareId, reason })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function clearShareTokens(record) {
|
|
27
|
+
record.authVersion = (record.authVersion || 1) + 1
|
|
28
|
+
record.tokens = []
|
|
29
|
+
record.tokenHash = undefined
|
|
30
|
+
record.tokenIssuedAt = undefined
|
|
31
|
+
record.tokenExpiresAt = undefined
|
|
32
|
+
}
|
|
13
33
|
|
|
14
34
|
function enqueueWrite(queueName, operation) {
|
|
15
35
|
const previous = writeQueues.get(queueName) || Promise.resolve()
|
|
@@ -64,6 +84,7 @@ function publicShareRecord(record) {
|
|
|
64
84
|
accessCount: record.accessCount || 0,
|
|
65
85
|
lastAccessedAt: record.lastAccessedAt,
|
|
66
86
|
hasPassword: Boolean(record.passwordHash),
|
|
87
|
+
allowCloudUsage: record.allowCloudUsage === true,
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
|
|
@@ -195,6 +216,7 @@ export async function createConversationShare({
|
|
|
195
216
|
permission,
|
|
196
217
|
password,
|
|
197
218
|
expiresAt,
|
|
219
|
+
allowCloudUsage = false,
|
|
198
220
|
titleSnapshot,
|
|
199
221
|
scope,
|
|
200
222
|
projectId,
|
|
@@ -233,14 +255,13 @@ export async function createConversationShare({
|
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
for (const stale of existingRecords.filter((record) => record?.id !== existing?.id)) {
|
|
258
|
+
const newlySuperseded = !stale.supersededAt
|
|
236
259
|
stale.supersededAt = stale.supersededAt || now
|
|
237
260
|
stale.revokedAt = stale.revokedAt || now
|
|
238
261
|
stale.updatedAt = now
|
|
239
|
-
stale
|
|
240
|
-
stale.tokenHash = undefined
|
|
241
|
-
stale.tokenIssuedAt = undefined
|
|
242
|
-
stale.tokenExpiresAt = undefined
|
|
262
|
+
clearShareTokens(stale)
|
|
243
263
|
data[stale.id] = stale
|
|
264
|
+
if (newlySuperseded) emitConversationShareInvalidated(stale.id, 'superseded')
|
|
244
265
|
}
|
|
245
266
|
|
|
246
267
|
if (existing?.id) {
|
|
@@ -251,6 +272,7 @@ export async function createConversationShare({
|
|
|
251
272
|
updatedAt: now,
|
|
252
273
|
supersededAt: undefined,
|
|
253
274
|
expiresAt: expiresAt || undefined,
|
|
275
|
+
allowCloudUsage: permission === 'operate' && allowCloudUsage === true,
|
|
254
276
|
revokedAt: undefined,
|
|
255
277
|
titleSnapshot: titleSnapshot || existing.titleSnapshot || 'New chat',
|
|
256
278
|
scope: scope === 'project' ? 'project' : 'global',
|
|
@@ -264,19 +286,20 @@ export async function createConversationShare({
|
|
|
264
286
|
tokenExpiresAt: existing.tokenExpiresAt,
|
|
265
287
|
}
|
|
266
288
|
if (passwordProvided) {
|
|
267
|
-
record
|
|
268
|
-
record.tokens = []
|
|
269
|
-
record.tokenHash = undefined
|
|
270
|
-
record.tokenIssuedAt = undefined
|
|
271
|
-
record.tokenExpiresAt = undefined
|
|
289
|
+
clearShareTokens(record)
|
|
272
290
|
}
|
|
273
291
|
if (passwordProvided && !passwordInfo.passwordHash) {
|
|
274
292
|
record.passwordHash = undefined
|
|
275
293
|
record.passwordSalt = undefined
|
|
276
294
|
record.passwordVersion = undefined
|
|
277
295
|
}
|
|
296
|
+
const lifecycleChanged = Boolean(existing.revokedAt)
|
|
297
|
+
|| String(existing.expiresAt || '') !== String(record.expiresAt || '')
|
|
298
|
+
|| existing.allowCloudUsage === true !== (record.allowCloudUsage === true)
|
|
299
|
+
|| (passwordProvided && existing.authVersion !== record.authVersion)
|
|
278
300
|
data[record.id] = record
|
|
279
301
|
await writeShareStoreFile(data)
|
|
302
|
+
if (lifecycleChanged) emitConversationShareInvalidated(record.id, 'updated')
|
|
280
303
|
return publicShareRecord(record)
|
|
281
304
|
}
|
|
282
305
|
|
|
@@ -292,6 +315,7 @@ export async function createConversationShare({
|
|
|
292
315
|
updatedAt: now,
|
|
293
316
|
supersededAt: undefined,
|
|
294
317
|
expiresAt: expiresAt || undefined,
|
|
318
|
+
allowCloudUsage: permission === 'operate' && allowCloudUsage === true,
|
|
295
319
|
revokedAt: undefined,
|
|
296
320
|
titleSnapshot: titleSnapshot || 'New chat',
|
|
297
321
|
scope: scope === 'project' ? 'project' : 'global',
|
|
@@ -335,18 +359,149 @@ export async function revokeConversationShare(shareId) {
|
|
|
335
359
|
error.statusCode = 404
|
|
336
360
|
throw error
|
|
337
361
|
}
|
|
362
|
+
const wasActive = !record.revokedAt
|
|
338
363
|
record.revokedAt = record.revokedAt || new Date().toISOString()
|
|
339
364
|
record.updatedAt = record.revokedAt
|
|
340
|
-
record
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
365
|
+
clearShareTokens(record)
|
|
366
|
+
data[shareId] = record
|
|
367
|
+
await writeShareStoreFile(data)
|
|
368
|
+
if (wasActive) emitConversationShareInvalidated(shareId, 'revoked')
|
|
369
|
+
return publicShareRecord(record)
|
|
370
|
+
})
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export async function restoreConversationShare(shareId, expiresAt) {
|
|
374
|
+
assertSafeShareId(shareId)
|
|
375
|
+
if (expiresAt && (Number.isNaN(Date.parse(expiresAt)) || Date.parse(expiresAt) <= Date.now())) {
|
|
376
|
+
const error = new Error('Expiration time must be in the future')
|
|
377
|
+
error.statusCode = 400
|
|
378
|
+
throw error
|
|
379
|
+
}
|
|
380
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
381
|
+
const data = await readShareStoreFile()
|
|
382
|
+
const record = data[shareId]
|
|
383
|
+
if (!record) {
|
|
384
|
+
const error = new Error('Share not found')
|
|
385
|
+
error.statusCode = 404
|
|
386
|
+
throw error
|
|
387
|
+
}
|
|
388
|
+
if (record.supersededAt) {
|
|
389
|
+
const error = new Error('Superseded shares cannot be restored')
|
|
390
|
+
error.statusCode = 409
|
|
391
|
+
throw error
|
|
392
|
+
}
|
|
393
|
+
record.revokedAt = undefined
|
|
394
|
+
record.expiresAt = expiresAt || undefined
|
|
395
|
+
record.updatedAt = new Date().toISOString()
|
|
396
|
+
clearShareTokens(record)
|
|
344
397
|
data[shareId] = record
|
|
345
398
|
await writeShareStoreFile(data)
|
|
346
399
|
return publicShareRecord(record)
|
|
347
400
|
})
|
|
348
401
|
}
|
|
349
402
|
|
|
403
|
+
export async function updateConversationShareExpiration(shareId, expiresAt) {
|
|
404
|
+
assertSafeShareId(shareId)
|
|
405
|
+
if (expiresAt && (Number.isNaN(Date.parse(expiresAt)) || Date.parse(expiresAt) <= Date.now())) {
|
|
406
|
+
const error = new Error('Expiration time must be in the future')
|
|
407
|
+
error.statusCode = 400
|
|
408
|
+
throw error
|
|
409
|
+
}
|
|
410
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
411
|
+
const data = await readShareStoreFile()
|
|
412
|
+
const record = data[shareId]
|
|
413
|
+
if (!record) {
|
|
414
|
+
const error = new Error('Share not found')
|
|
415
|
+
error.statusCode = 404
|
|
416
|
+
throw error
|
|
417
|
+
}
|
|
418
|
+
if (record.supersededAt) {
|
|
419
|
+
const error = new Error('Superseded shares cannot be updated')
|
|
420
|
+
error.statusCode = 409
|
|
421
|
+
throw error
|
|
422
|
+
}
|
|
423
|
+
assertShareActive(record)
|
|
424
|
+
record.expiresAt = expiresAt || undefined
|
|
425
|
+
record.updatedAt = new Date().toISOString()
|
|
426
|
+
data[shareId] = record
|
|
427
|
+
await writeShareStoreFile(data)
|
|
428
|
+
emitConversationShareInvalidated(shareId, 'updated')
|
|
429
|
+
return publicShareRecord(record)
|
|
430
|
+
})
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export async function updateConversationShare(shareId, { permission, password, expiresAt, allowCloudUsage } = {}) {
|
|
434
|
+
assertSafeShareId(shareId)
|
|
435
|
+
const passwordProvided = typeof password === 'string'
|
|
436
|
+
const normalizedPassword = passwordProvided ? password.trim() : undefined
|
|
437
|
+
if (expiresAt && (Number.isNaN(Date.parse(expiresAt)) || Date.parse(expiresAt) <= Date.now())) {
|
|
438
|
+
const error = new Error('Expiration time must be in the future')
|
|
439
|
+
error.statusCode = 400
|
|
440
|
+
throw error
|
|
441
|
+
}
|
|
442
|
+
const passwordInfo = passwordProvided ? await hashSharePassword(normalizedPassword) : {}
|
|
443
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
444
|
+
const data = await readShareStoreFile()
|
|
445
|
+
const record = data[shareId]
|
|
446
|
+
if (!record) {
|
|
447
|
+
const error = new Error('Share not found')
|
|
448
|
+
error.statusCode = 404
|
|
449
|
+
throw error
|
|
450
|
+
}
|
|
451
|
+
if (record.supersededAt) {
|
|
452
|
+
const error = new Error('Superseded shares cannot be updated')
|
|
453
|
+
error.statusCode = 409
|
|
454
|
+
throw error
|
|
455
|
+
}
|
|
456
|
+
assertShareActive(record)
|
|
457
|
+
const nextPermission = permission === undefined ? record.permission : permission
|
|
458
|
+
assertValidPermission(nextPermission)
|
|
459
|
+
const willHavePassword = passwordProvided ? Boolean(normalizedPassword) : Boolean(record.passwordHash)
|
|
460
|
+
if (nextPermission === 'operate' && !willHavePassword) {
|
|
461
|
+
const error = new Error('Editable shares require a non-empty password')
|
|
462
|
+
error.statusCode = 400
|
|
463
|
+
throw error
|
|
464
|
+
}
|
|
465
|
+
const nextAllowCloudUsage = nextPermission === 'operate'
|
|
466
|
+
&& (allowCloudUsage === undefined ? record.allowCloudUsage === true : allowCloudUsage === true)
|
|
467
|
+
const lifecycleChanged = nextPermission !== record.permission
|
|
468
|
+
|| nextAllowCloudUsage !== (record.allowCloudUsage === true)
|
|
469
|
+
|| (expiresAt !== undefined && String(record.expiresAt || '') !== String(expiresAt || ''))
|
|
470
|
+
|| passwordProvided
|
|
471
|
+
if (passwordProvided) {
|
|
472
|
+
record.passwordHash = passwordInfo.passwordHash
|
|
473
|
+
record.passwordSalt = passwordInfo.passwordSalt
|
|
474
|
+
record.passwordVersion = passwordInfo.passwordVersion
|
|
475
|
+
clearShareTokens(record)
|
|
476
|
+
}
|
|
477
|
+
record.permission = nextPermission
|
|
478
|
+
record.allowCloudUsage = nextAllowCloudUsage
|
|
479
|
+
if (expiresAt !== undefined) record.expiresAt = expiresAt || undefined
|
|
480
|
+
record.updatedAt = new Date().toISOString()
|
|
481
|
+
data[shareId] = record
|
|
482
|
+
await writeShareStoreFile(data)
|
|
483
|
+
if (lifecycleChanged) emitConversationShareInvalidated(shareId, 'updated')
|
|
484
|
+
return publicShareRecord(record)
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export async function deleteConversationShare(shareId) {
|
|
489
|
+
assertSafeShareId(shareId)
|
|
490
|
+
return enqueueWrite(writeQueueName, async () => {
|
|
491
|
+
const data = await readShareStoreFile()
|
|
492
|
+
const record = data[shareId]
|
|
493
|
+
if (!record) {
|
|
494
|
+
const error = new Error('Share not found')
|
|
495
|
+
error.statusCode = 404
|
|
496
|
+
throw error
|
|
497
|
+
}
|
|
498
|
+
delete data[shareId]
|
|
499
|
+
await writeShareStoreFile(data)
|
|
500
|
+
emitConversationShareInvalidated(shareId, 'deleted')
|
|
501
|
+
return publicShareRecord(record)
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
|
|
350
505
|
export async function issueConversationShareToken(shareId) {
|
|
351
506
|
assertSafeShareId(shareId)
|
|
352
507
|
return enqueueWrite(writeQueueName, async () => {
|
package/server/skills.mjs
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { existsSync, promises as fs } from 'node:fs'
|
|
2
2
|
import os from 'node:os'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
-
import {
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { atomicProjectConfigUpdate, configDir, dataDir } from './storage.mjs'
|
|
5
6
|
import { getEnabledPluginSkillSources } from './plugins/registry.mjs'
|
|
6
7
|
import { logger } from './utils/logger.mjs'
|
|
7
8
|
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const bundledSkillsDir = path.resolve(__dirname, '..', 'skills')
|
|
8
11
|
const userSkillsDir = path.join(dataDir, 'skills')
|
|
9
12
|
const sharedUserSkillsDir = path.join(os.homedir(), '.agents', 'skills')
|
|
10
13
|
const claudeUserSkillsDir = path.join(os.homedir(), '.claude', 'skills')
|
|
11
14
|
const opencodeUserSkillsDir = path.join(os.homedir(), '.opencode', 'skills')
|
|
15
|
+
const defaultSkillsMarkerFile = path.join(configDir, '.default-skills-v1')
|
|
16
|
+
const defaultGlobalSkillNames = ['skill-creator']
|
|
12
17
|
const defaultEntry = 'SKILL.md'
|
|
13
|
-
const resourceDirs = ['scripts', 'references', 'assets']
|
|
18
|
+
const resourceDirs = ['scripts', 'references', 'assets', 'agents', 'eval-viewer']
|
|
14
19
|
const maxResourceFiles = 200
|
|
15
20
|
|
|
16
21
|
function isValidSkillName(value) {
|
|
@@ -421,7 +426,7 @@ export function mergeSkills(...skillLists) {
|
|
|
421
426
|
}
|
|
422
427
|
|
|
423
428
|
export const skillSearchPaths = {
|
|
424
|
-
global: searchDirsForList([claudeUserSkillsDir, opencodeUserSkillsDir, sharedUserSkillsDir, userSkillsDir]),
|
|
429
|
+
global: searchDirsForList([bundledSkillsDir, claudeUserSkillsDir, opencodeUserSkillsDir, sharedUserSkillsDir, userSkillsDir]),
|
|
425
430
|
project: ['<project>/.claude/skills', '<project>/.opencode/skills', '<project>/.agents/skills', '<project>/.quickforge/skills'],
|
|
426
431
|
}
|
|
427
432
|
|
|
@@ -444,6 +449,7 @@ async function loadPluginSkills(workspaceRoot) {
|
|
|
444
449
|
|
|
445
450
|
export async function loadGlobalSkills() {
|
|
446
451
|
return loadSkillsFromSources([
|
|
452
|
+
{ dir: bundledSkillsDir, name: 'builtin' },
|
|
447
453
|
{ dir: claudeUserSkillsDir, name: 'user-claude' },
|
|
448
454
|
{ dir: opencodeUserSkillsDir, name: 'user-opencode' },
|
|
449
455
|
{ dir: sharedUserSkillsDir, name: 'user-shared' },
|
|
@@ -451,6 +457,27 @@ export async function loadGlobalSkills() {
|
|
|
451
457
|
])
|
|
452
458
|
}
|
|
453
459
|
|
|
460
|
+
export async function ensureDefaultGlobalSkills() {
|
|
461
|
+
if (existsSync(defaultSkillsMarkerFile)) return false
|
|
462
|
+
|
|
463
|
+
const known = new Set((await loadGlobalSkills()).map((skill) => skill.name))
|
|
464
|
+
const defaults = defaultGlobalSkillNames.filter((name) => known.has(name))
|
|
465
|
+
if (defaults.length === 0) return false
|
|
466
|
+
|
|
467
|
+
await atomicProjectConfigUpdate((config) => {
|
|
468
|
+
config.globalSkills = normalizeSkillNames([
|
|
469
|
+
...(Array.isArray(config.globalSkills) ? config.globalSkills : []),
|
|
470
|
+
...defaults,
|
|
471
|
+
])
|
|
472
|
+
return config
|
|
473
|
+
})
|
|
474
|
+
await fs.mkdir(configDir, { recursive: true })
|
|
475
|
+
await fs.writeFile(defaultSkillsMarkerFile, `${new Date().toISOString()}\n`, { flag: 'wx' }).catch((error) => {
|
|
476
|
+
if (error?.code !== 'EEXIST') throw error
|
|
477
|
+
})
|
|
478
|
+
return true
|
|
479
|
+
}
|
|
480
|
+
|
|
454
481
|
export async function loadProjectSkills(workspaceRoot) {
|
|
455
482
|
const pluginSkills = await loadPluginSkills(workspaceRoot)
|
|
456
483
|
const projectSkills = await loadSkillsFromSources([
|
package/server/storage.mjs
CHANGED
|
@@ -618,10 +618,16 @@ export async function atomicSessionValueUpdate(sessionId, updateFn) {
|
|
|
618
618
|
export async function deleteSessionValue(sessionId) {
|
|
619
619
|
return enqueueWrite('sessions', async () => {
|
|
620
620
|
const bucket = await findSessionBucket(sessionId)
|
|
621
|
-
if (!bucket)
|
|
621
|
+
if (!bucket) {
|
|
622
|
+
const { deleteCloudChatIdempotencySession } = await import('./cloud/chat-idempotency-store.mjs')
|
|
623
|
+
await deleteCloudChatIdempotencySession(sessionId)
|
|
624
|
+
return
|
|
625
|
+
}
|
|
622
626
|
await fs.rm(sessionDataFile(sessionId, bucket), { force: true })
|
|
623
627
|
const { deleteSessionAssets } = await import('./session-assets.mjs')
|
|
624
628
|
await deleteSessionAssets(bucket, sessionId)
|
|
629
|
+
const { deleteCloudChatIdempotencySession } = await import('./cloud/chat-idempotency-store.mjs')
|
|
630
|
+
await deleteCloudChatIdempotencySession(sessionId)
|
|
625
631
|
sessionBucketIndex.delete(sessionId)
|
|
626
632
|
})
|
|
627
633
|
}
|
package/server/subagents.mjs
CHANGED
|
@@ -32,7 +32,7 @@ export const subagentDefinitions = [
|
|
|
32
32
|
model: { mode: 'inherit' },
|
|
33
33
|
lifecycle: 'builtin',
|
|
34
34
|
allowFileMutations: true,
|
|
35
|
-
maxRuntimeMs:
|
|
35
|
+
maxRuntimeMs: 60 * 60 * 1000,
|
|
36
36
|
maxToolCalls: 300,
|
|
37
37
|
systemPrompt: `You are General, a general-purpose subagent for bounded complex multi-step implementation tasks and broader independent work. You may inspect, edit, write files, and run commands using the built-in workspace tools when needed. You do not have MCP tools or Agent Skills. Prefer Explore for focused read-only repository discovery, source search, call-chain lookup, tests/docs discovery, and impact analysis. Make focused, minimal changes that satisfy the delegated task, and verify your changes when appropriate.`,
|
|
38
38
|
},
|
|
@@ -46,9 +46,9 @@ export const subagentDefinitions = [
|
|
|
46
46
|
model: { mode: 'inherit' },
|
|
47
47
|
lifecycle: 'builtin',
|
|
48
48
|
allowFileMutations: false,
|
|
49
|
-
maxRuntimeMs:
|
|
49
|
+
maxRuntimeMs: 60 * 60 * 1000,
|
|
50
50
|
maxToolCalls: 300,
|
|
51
|
-
systemPrompt: `You are Explore, the preferred read-only repository
|
|
51
|
+
systemPrompt: `You are Explore, the preferred read-only repository research subagent. Investigate the delegated question using read_file, grep_files, and safe read-only run_command calls, and report only findings grounded in repository evidence. Do not design solutions or propose implementation, architecture, refactoring, or code changes. Cite relevant file paths, symbols, and line ranges when practical. Clearly separate verified facts, reasonable inferences, and unknowns, and never present an inference as a verified fact. Unless the parent Agent explicitly requests them, do not provide an implementation plan, recommendations, or suggested next steps. You cannot modify files.`,
|
|
52
52
|
},
|
|
53
53
|
]
|
|
54
54
|
|
|
@@ -82,7 +82,7 @@ capabilityPolicy: ${definition.capabilityPolicy || 'review-only'}
|
|
|
82
82
|
tools: ${definition.allowedTools.join(', ')}
|
|
83
83
|
model:
|
|
84
84
|
mode: inherit
|
|
85
|
-
max-runtime-ms: ${definition.maxRuntimeMs ||
|
|
85
|
+
max-runtime-ms: ${definition.maxRuntimeMs || 60 * 60 * 1000}
|
|
86
86
|
max-tool-calls: ${definition.maxToolCalls || 300}
|
|
87
87
|
---
|
|
88
88
|
${body}
|
|
@@ -7,8 +7,7 @@ import { logger } from '../utils/logger.mjs'
|
|
|
7
7
|
const require = createRequire(import.meta.url)
|
|
8
8
|
const MAX_SESSIONS = Math.max(1, Number(process.env.QUICKFORGE_MAX_TERMINALS || 6))
|
|
9
9
|
const TERMINAL_DISABLED = process.env.QUICKFORGE_TERMINAL === '0'
|
|
10
|
-
const RECONNECT_GRACE_MS = Number(process.env.QUICKFORGE_TERMINAL_RECONNECT_MS ||
|
|
11
|
-
const IDLE_TIMEOUT_MS = Number(process.env.QUICKFORGE_TERMINAL_IDLE_MS || 30 * 60 * 1000)
|
|
10
|
+
const RECONNECT_GRACE_MS = Number(process.env.QUICKFORGE_TERMINAL_RECONNECT_MS || 30 * 60 * 1000)
|
|
12
11
|
|
|
13
12
|
const sessions = new Map()
|
|
14
13
|
let cleanupTimer = null
|
|
@@ -85,8 +84,7 @@ function scheduleCleanup() {
|
|
|
85
84
|
const now = Date.now()
|
|
86
85
|
for (const session of sessions.values()) {
|
|
87
86
|
const disconnectedTooLong = session.clients.size === 0 && now - session.disconnectedAt > RECONNECT_GRACE_MS
|
|
88
|
-
|
|
89
|
-
if (session.exited || disconnectedTooLong || idleTooLong) {
|
|
87
|
+
if (session.exited || disconnectedTooLong) {
|
|
90
88
|
destroyTerminalSession(session.id)
|
|
91
89
|
}
|
|
92
90
|
}
|
|
@@ -111,7 +111,7 @@ export const workspaceTools = [
|
|
|
111
111
|
description: 'Run a shell command in the project bound to this chat. Use this for lint, build, tests, git status, and diagnostics.',
|
|
112
112
|
parameters: Type.Object({
|
|
113
113
|
command: Type.String({ description: 'Command to execute in the workspace.' }),
|
|
114
|
-
timeoutMs: Type.Optional(Type.Number({ description: 'Command timeout in milliseconds. Defaults to
|
|
114
|
+
timeoutMs: Type.Optional(Type.Number({ description: 'Command timeout in milliseconds. Defaults to 1 hour and is clamped to the supported range.', default: 3600000 })),
|
|
115
115
|
description: Type.Optional(Type.String({ description: 'Short explanation of why this command is being run.' })),
|
|
116
116
|
}),
|
|
117
117
|
executionMode: 'sequential',
|
|
@@ -129,7 +129,7 @@ export const workspaceTools = [
|
|
|
129
129
|
{
|
|
130
130
|
name: 'present_files',
|
|
131
131
|
label: 'Present files',
|
|
132
|
-
description: '
|
|
132
|
+
description: 'Present one or more AI-produced artifact files only when the user is likely to benefit from inspecting the actual deliverable directly. Use it for visual outputs, reports, documents, generated assets, or when the user explicitly asks to view or review a file. Source code and configuration files should only be presented when they are themselves the requested deliverable or the user asks to inspect them. Do not present routine implementation changes, test files, supporting code, or large sets of modified files merely because they were edited. Prefer a small, relevant selection. HTML and supported images open in Browser; Markdown, code, configuration, and text files open in Reader. Unsupported files remain available in the artifact list.',
|
|
133
133
|
parameters: Type.Object({
|
|
134
134
|
files: Type.Array(Type.Union([
|
|
135
135
|
Type.String({ description: 'File path relative to the workspace root.' }),
|
|
@@ -138,7 +138,7 @@ export const workspaceTools = [
|
|
|
138
138
|
title: Type.Optional(Type.String({ description: 'Optional display title.' })),
|
|
139
139
|
description: Type.Optional(Type.String({ description: 'Optional short description shown in artifact lists.' })),
|
|
140
140
|
kind: Type.Optional(Type.String({ description: 'Optional file kind hint, such as html, image, markdown, or code.' })),
|
|
141
|
-
preview: Type.Optional(Type.Boolean({ description: 'Whether this file
|
|
141
|
+
preview: Type.Optional(Type.Boolean({ description: 'Whether this file may be opened automatically. Set false to list it without opening it.' })),
|
|
142
142
|
}),
|
|
143
143
|
]), { description: 'Artifact files to present.' }),
|
|
144
144
|
defaultPreview: Type.Optional(Type.String({ description: 'File path to open as the default preview when multiple files are presented.' })),
|
package/server/tools/index.mjs
CHANGED
|
@@ -624,10 +624,12 @@ export async function toolReadSkillResource(params, context) {
|
|
|
624
624
|
// --- present_files ---
|
|
625
625
|
function inferPresentedFileKind(relativePath) {
|
|
626
626
|
const lower = String(relativePath || '').toLowerCase()
|
|
627
|
+
const fileName = lower.replace(/\\/g, '/').split('/').pop() || lower
|
|
627
628
|
if (lower.endsWith('.html') || lower.endsWith('.htm')) return 'html'
|
|
628
|
-
if (/\.(svg|png|jpe?g|webp|gif|
|
|
629
|
-
if (
|
|
630
|
-
if (
|
|
629
|
+
if (/\.(svg|png|jpe?g|webp|gif|ico)$/.test(lower)) return 'image'
|
|
630
|
+
if (/\.(md|mdx|markdown)$/.test(lower)) return 'markdown'
|
|
631
|
+
if (fileName === 'dockerfile' || fileName.endsWith('.dockerfile') || fileName === 'makefile') return 'code'
|
|
632
|
+
if (/\.(ts|tsx|js|jsx|mjs|cjs|css|scss|less|json|jsonc|txt|csv|tsv|log|sql|xml|yml|yaml|toml|ini|py|rb|go|rs|java|swift|kt|kts|c|h|cpp|hpp|cs|php|sh|bash|zsh|ps1)$/.test(lower)) return 'code'
|
|
631
633
|
return 'unknown'
|
|
632
634
|
}
|
|
633
635
|
|
|
@@ -707,9 +709,9 @@ export async function toolPresentFiles(params, context) {
|
|
|
707
709
|
}
|
|
708
710
|
|
|
709
711
|
// --- run_command ---
|
|
710
|
-
const DEFAULT_RUN_COMMAND_TIMEOUT_MS =
|
|
712
|
+
const DEFAULT_RUN_COMMAND_TIMEOUT_MS = 60 * 60 * 1000
|
|
711
713
|
const MIN_RUN_COMMAND_TIMEOUT_MS = 1000
|
|
712
|
-
const MAX_RUN_COMMAND_TIMEOUT_MS =
|
|
714
|
+
const MAX_RUN_COMMAND_TIMEOUT_MS = 60 * 60 * 1000
|
|
713
715
|
const COMMAND_PREVIEW_LINES = 200
|
|
714
716
|
const COMMAND_PREVIEW_TOTAL_CHARS = 10000
|
|
715
717
|
|
package/server/utils/network.mjs
CHANGED
|
@@ -8,6 +8,15 @@ export function isPrivateIpv4(hostname) {
|
|
|
8
8
|
return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export function isTailscaleAddress(address) {
|
|
12
|
+
if (!address) return false
|
|
13
|
+
const normalized = String(address).replace(/^::ffff:/i, '')
|
|
14
|
+
if (!/^\d{1,3}(?:\.\d{1,3}){3}$/.test(normalized)) return false
|
|
15
|
+
const parts = normalized.split('.').map((part) => Number(part))
|
|
16
|
+
if (parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return false
|
|
17
|
+
return parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
export function isLoopbackAddress(address) {
|
|
12
21
|
if (!address) return false
|
|
13
22
|
const normalized = address.replace(/^::ffff:/, '')
|