@qwen-code/qwen-code 0.19.11 → 0.19.12-preview.0
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/bundled/batch/SKILL.md +6 -0
- package/bundled/qc-helper/docs/configuration/model-providers.md +11 -0
- package/bundled/qc-helper/docs/configuration/settings.md +11 -10
- package/bundled/qc-helper/docs/features/channels/overview.md +66 -11
- package/bundled/qc-helper/docs/features/sub-agents.md +8 -8
- package/bundled/qc-helper/docs/features/worktree.md +2 -2
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +42 -7
- package/bundled/qc-helper/docs/qwen-serve.md +102 -40
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/bundled/review/SKILL.md +60 -49
- package/bundled/simplify/SKILL.md +1 -1
- package/chunks/{MaxSizedBox-L6TGYDM3.js → MaxSizedBox-3KIWAU6Z.js} +31 -32
- package/chunks/{StandaloneSessionPicker-AKEQHU5P.js → StandaloneSessionPicker-TF626NO7.js} +45 -46
- package/chunks/acp-startup-profiler-N3RHHXJ3.js +25 -0
- package/chunks/{acpAgent-PTNBGJDN.js → acpAgent-JDVJXVBO.js} +1863 -507
- package/chunks/{agent-AJOMOZPJ.js → agent-BONWC4VY.js} +26 -27
- package/chunks/{agent-headless-CYKOMAXS.js → agent-headless-H7HKMGLZ.js} +26 -27
- package/chunks/{anthropicContentGenerator-HBRXJSP4.js → anthropicContentGenerator-ZZRGFEES.js} +35 -12
- package/chunks/{artifact-tool-MIZLXBLF.js → artifact-tool-PHO5XEGZ.js} +5 -5
- package/chunks/{askUserQuestion-XMJVPNWM.js → askUserQuestion-CEPBT47X.js} +2 -47
- package/chunks/{bridge-QKISWPCI.js → bridge-ATN2TLWJ.js} +31 -32
- package/chunks/{ca-CODEN7TD.js → ca-2SL2BXTK.js} +3 -2
- package/chunks/{channel-worker-group-SOUWTFHC.js → channel-worker-group-S3H5QHTI.js} +4 -4
- package/chunks/{channel-worker-manager-LOVSV3WW.js → channel-worker-manager-ZJ4HTMO2.js} +4 -4
- package/chunks/{channel-worker-supervisor-XW6MHJBQ.js → channel-worker-supervisor-JA6VZ5V5.js} +3 -3
- package/chunks/{chunk-ZIXOTAPZ.js → chunk-2DZ7E27L.js} +92 -3
- package/chunks/{chunk-L5U3ELZX.js → chunk-335ECZTA.js} +1 -1
- package/chunks/{chunk-2FO5GFKZ.js → chunk-3DJY4IFO.js} +1 -1
- package/chunks/{chunk-4AGTGMCQ.js → chunk-3PE7OY2G.js} +12 -12
- package/chunks/{chunk-QOTQH4CB.js → chunk-4AGBO6RB.js} +27 -10
- package/chunks/{chunk-Y66VOBRP.js → chunk-4CA4WUMS.js} +2 -2
- package/chunks/{chunk-LQIDP6K5.js → chunk-4CHDYDG3.js} +1 -1
- package/chunks/{chunk-574PTLMU.js → chunk-4OVECYBM.js} +407 -92
- package/chunks/{chunk-TV6K2FB4.js → chunk-4RPARQ5D.js} +2 -2
- package/chunks/{chunk-TL4R5TRK.js → chunk-4W55BHOC.js} +2 -2
- package/chunks/{chunk-RCEEXIOO.js → chunk-4XCVGEEX.js} +3 -3
- package/chunks/{chunk-3DWIFU7L.js → chunk-53UMEONK.js} +1 -1
- package/chunks/{chunk-AU3MFJPL.js → chunk-54KPEDM2.js} +8 -8
- package/chunks/{chunk-T76DGHGG.js → chunk-66F4IJ4L.js} +5 -5
- package/chunks/{chunk-YLSSEUAM.js → chunk-675SVDMD.js} +354 -278
- package/chunks/{chunk-NVTQZXR7.js → chunk-6QGZVKOO.js} +91 -52
- package/chunks/{chunk-NJOFRXTM.js → chunk-6TDA4EVC.js} +4200 -3450
- package/chunks/{chunk-BYH2JRK3.js → chunk-6WZZHJSZ.js} +5 -5
- package/chunks/{chunk-7X7TEQNX.js → chunk-76BFY7AF.js} +13 -0
- package/chunks/chunk-7KS72E2N.js +883 -0
- package/chunks/{chunk-CF5VJRGC.js → chunk-7LMAKIWE.js} +1 -1
- package/chunks/{chunk-WMNXXRGL.js → chunk-7O7WZH7N.js} +31 -31
- package/chunks/{chunk-7ZYHLN67.js → chunk-7OZGMFHC.js} +26 -75
- package/chunks/{chunk-6WPJRYLZ.js → chunk-7SX6WZNO.js} +3 -3
- package/chunks/{chunk-JGUT3LWZ.js → chunk-ANRANWVE.js} +4 -1
- package/chunks/{chunk-JD44PNGJ.js → chunk-AWOU24F6.js} +10 -5
- package/chunks/{chunk-6WGZDD67.js → chunk-BEFQGC6H.js} +2 -2
- package/chunks/{chunk-KXYJPRT5.js → chunk-BEZPRKD3.js} +28 -6
- package/chunks/{chunk-FYQ22K62.js → chunk-CHVH4DVN.js} +6 -5
- package/chunks/{chunk-LS56TX3M.js → chunk-CMHJEUVB.js} +1 -1
- package/chunks/{chunk-FXKOAEXP.js → chunk-CXWUXRD5.js} +14 -14
- package/chunks/{chunk-CAHY5ABC.js → chunk-DULSE2SH.js} +1 -1
- package/chunks/{chunk-SQ5BK3GO.js → chunk-DYUPBU7H.js} +1 -1
- package/chunks/{chunk-FNX3LGAK.js → chunk-E655RDRL.js} +1 -1
- package/chunks/{chunk-N5VIZTND.js → chunk-EWPLL4OT.js} +1 -1
- package/chunks/{chunk-FSA7ERJ2.js → chunk-F2RR4HO2.js} +1 -1
- package/chunks/{chunk-S63Z5V42.js → chunk-F2YCCUXI.js} +4 -4
- package/chunks/{chunk-3AYLDB6B.js → chunk-FEZXQXW2.js} +1 -1
- package/chunks/{chunk-OWT2CAEQ.js → chunk-GEHSF7MS.js} +105 -12
- package/chunks/{chunk-LXLTBIDP.js → chunk-HCCR43AY.js} +1 -1
- package/chunks/{chunk-7NMU22Q3.js → chunk-HQ7MDVBO.js} +18 -7
- package/chunks/{chunk-KSZKSE23.js → chunk-HVWPSGDY.js} +1 -1
- package/chunks/{chunk-YFVO2HOR.js → chunk-HZXU6YZW.js} +5 -5
- package/chunks/{chunk-HASK4LMI.js → chunk-IKLWU6YS.js} +13 -6
- package/chunks/{chunk-GNBBAQ6J.js → chunk-ILQ42UG6.js} +111 -40
- package/chunks/{chunk-THXDQSTA.js → chunk-IQRQVPGN.js} +49 -154
- package/chunks/{chunk-SPJQO2CE.js → chunk-J2MKYJKZ.js} +2 -2
- package/chunks/{chunk-SUARMD4Q.js → chunk-J6NT5YTN.js} +28 -2
- package/chunks/{chunk-TYLOFD2U.js → chunk-JCJPZMJV.js} +1 -1
- package/chunks/{chunk-BYB3GF2T.js → chunk-JCYMQU5F.js} +133 -6
- package/chunks/{chunk-6EDDYPI3.js → chunk-JUNVFQVY.js} +4 -4
- package/chunks/{chunk-CH2XVB47.js → chunk-K7P34F2J.js} +3 -3
- package/chunks/{chunk-5VWYKIMW.js → chunk-KANTXY7B.js} +2 -2
- package/chunks/{chunk-K7VD4XLT.js → chunk-LHEQDKAR.js} +1 -1
- package/chunks/{chunk-A2HWMJVO.js → chunk-LKR7QQ63.js} +3 -3
- package/chunks/{chunk-4HUDDSO5.js → chunk-LLIGYAQU.js} +4 -4
- package/chunks/{chunk-5Z452MMD.js → chunk-LODZMRXC.js} +154 -83
- package/chunks/{chunk-FBU7WRZI.js → chunk-LPHGXXZX.js} +6 -1
- package/chunks/{chunk-CPFIQVZJ.js → chunk-LYFDJTXE.js} +1 -1
- package/chunks/{chunk-TIOE2OPE.js → chunk-MCYZTKBV.js} +2 -2
- package/chunks/{chunk-ZQIYGQCG.js → chunk-MHEZV3OY.js} +506 -143
- package/chunks/{chunk-JWICTOXS.js → chunk-NGTQA57Y.js} +2 -2
- package/chunks/{chunk-5CNYZ7TN.js → chunk-NJQPQ4IO.js} +7 -7
- package/chunks/chunk-NSZJQACR.js +45 -0
- package/chunks/{chunk-DOPUYNXO.js → chunk-NUHSLBJZ.js} +7 -7
- package/chunks/{chunk-CITWNIXJ.js → chunk-NVUGS23Y.js} +5 -5
- package/chunks/{chunk-2Z5UX4KI.js → chunk-OSSDHJDU.js} +1 -1
- package/chunks/{chunk-YUZI3WAC.js → chunk-OWAK7LMW.js} +7 -0
- package/chunks/{chunk-Y3KFTWVX.js → chunk-OZWJAFFE.js} +6 -6
- package/chunks/{chunk-YGYSMY2R.js → chunk-PSISZNIT.js} +1629 -1142
- package/chunks/{chunk-4ROQY23C.js → chunk-PTXVHS2O.js} +7 -7
- package/chunks/{chunk-OKAIGAYW.js → chunk-QGONRHUS.js} +3 -3
- package/chunks/{chunk-PZL23GTM.js → chunk-QR6U5QPP.js} +8 -0
- package/chunks/chunk-QSG4ZFG4.js +257 -0
- package/chunks/{chunk-BXBDC476.js → chunk-R32BTEZY.js} +5 -5
- package/chunks/{chunk-AVEOWVOM.js → chunk-R3VWUU2E.js} +1 -1
- package/chunks/{chunk-RP5R7MB6.js → chunk-R4RIMOJL.js} +12 -12
- package/chunks/{chunk-ARKANCNX.js → chunk-SGPFJMRP.js} +1 -1
- package/chunks/{chunk-3M75MSVA.js → chunk-SMASS3CY.js} +1 -1
- package/chunks/{chunk-4IJMTYCB.js → chunk-SNONG66P.js} +818 -29
- package/chunks/chunk-TDKQP3M6.js +201 -0
- package/chunks/{chunk-VFX7EZIR.js → chunk-TFV3XVAZ.js} +8 -1
- package/chunks/{chunk-4LD5APFL.js → chunk-TYQMGACW.js} +1 -0
- package/chunks/{chunk-7NFB7LBQ.js → chunk-UE5TZ55T.js} +8 -0
- package/chunks/{chunk-F333XEAU.js → chunk-UOA2IHRI.js} +4 -3
- package/chunks/{chunk-VRR65QYW.js → chunk-V4PCIS25.js} +5 -1
- package/chunks/{chunk-WIEO4CWB.js → chunk-VAJS2E2D.js} +1 -1
- package/chunks/{chunk-GLNHPRZT.js → chunk-W4DFAUAO.js} +1 -1
- package/chunks/{chunk-STZQCNN3.js → chunk-W7H223ZZ.js} +1 -1
- package/chunks/{chunk-6Y6ORE2G.js → chunk-WACI4VES.js} +288 -20
- package/chunks/{chunk-WTNZ6DXB.js → chunk-WEPXQKGP.js} +1 -1
- package/chunks/{chunk-4BJVSIS3.js → chunk-WHQNBBWW.js} +2 -2
- package/chunks/{chunk-QDJ2R4CM.js → chunk-WTHRMVKB.js} +3 -3
- package/chunks/{chunk-L4Y55KIS.js → chunk-X6YQWCIJ.js} +1 -1
- package/chunks/{chunk-Q6TUALBE.js → chunk-XAAEDML2.js} +1 -1
- package/chunks/{chunk-BUAXJVNP.js → chunk-XACBIZJB.js} +3 -3
- package/chunks/{chunk-2OG646IN.js → chunk-XFJU7SVP.js} +4 -4
- package/chunks/{chunk-KKRTYONM.js → chunk-XSPFQQRE.js} +3 -3
- package/chunks/{chunk-V2DXTLUE.js → chunk-Y5UDBQYF.js} +3 -3
- package/chunks/{chunk-MQOVXZAF.js → chunk-YN2I5I42.js} +529 -61
- package/chunks/{chunk-FMNYGWOD.js → chunk-Z4ZCVYJ5.js} +14 -8
- package/chunks/{chunk-SY7NEYX4.js → chunk-ZFHHBRRA.js} +2 -2
- package/chunks/{chunk-QYG4BY6Q.js → chunk-ZPVGEMVC.js} +1 -1
- package/chunks/{computer-use-VZZMJEQ7.js → computer-use-UY4A72X7.js} +26 -27
- package/chunks/{config-utils-IQQT3GCI.js → config-utils-KWTRFP7D.js} +3 -3
- package/chunks/{contextCommand-PORKIZSY.js → contextCommand-3DZ2SVH6.js} +30 -31
- package/chunks/{create-sub-session-EAB2U5XW.js → create-sub-session-DIJOV6HV.js} +2 -2
- package/chunks/{create-sub-session-XVFKHAFW.js → create-sub-session-OT7WXLDO.js} +27 -28
- package/chunks/{cron-create-GMVKSXZT.js → cron-create-X462ERYA.js} +4 -4
- package/chunks/{cron-delete-VDZKUAVK.js → cron-delete-SL5UJUM3.js} +4 -4
- package/chunks/{cron-list-UG7C7RAR.js → cron-list-3MD6SVXI.js} +4 -4
- package/chunks/{daemon-IHR6WSPB.js → daemon-ZW6LHOXT.js} +91 -14
- package/chunks/{daemon-status-provider-ABLBA3UJ.js → daemon-status-provider-GTZOYSBO.js} +35 -36
- package/chunks/{de-NOXPCSYN.js → de-I7ZIYXZC.js} +3 -2
- package/chunks/{dist-PX2ERZ6I.js → dist-HCUUS6IH.js} +2 -2
- package/chunks/{dist-VYWUKURL.js → dist-O2IP3G5D.js} +1 -1
- package/chunks/{dist-GXJVCOS7.js → dist-RZBJT5O2.js} +2 -1
- package/chunks/{dist-V2YVLYOW.js → dist-Y54RURGT.js} +3 -1
- package/chunks/{dist-DOPL5LSQ.js → dist-YTPTCUAD.js} +1 -1
- package/chunks/{dist-4WCQEZIN.js → dist-ZJHKKWUD.js} +1 -1
- package/chunks/{earlyInputCapture-DJCU5SPU.js → earlyInputCapture-GGWVGBZ7.js} +27 -28
- package/chunks/{edit-O4VV5AO6.js → edit-JN467GIR.js} +27 -28
- package/chunks/{en-JFVULHTB.js → en-XE34GKAA.js} +7 -3
- package/chunks/{enter-worktree-XQYDDI2I.js → enter-worktree-MMO4VBQE.js} +26 -27
- package/chunks/{enterPlanMode-CESVMQBV.js → enterPlanMode-BRZOG4DB.js} +27 -30
- package/chunks/{environment-5KXOLZNR.js → environment-LOCDCWMZ.js} +29 -30
- package/chunks/{errors-EBUTCBAQ.js → errors-XXSL5HGG.js} +29 -30
- package/chunks/{exit-worktree-MWGNSPNX.js → exit-worktree-UD7PI5H5.js} +26 -27
- package/chunks/exitPlanMode-EIUETQI6.js +389 -0
- package/chunks/{fast-path-6V73F2T2.js → fast-path-ULGAEUVK.js} +2 -2
- package/chunks/{fr-HVFNU42H.js → fr-X7KDVZIY.js} +3 -2
- package/chunks/{gemini-TEFJZCIZ.js → gemini-KW3APZ4P.js} +133 -80
- package/chunks/{geminiContentGenerator-M56BEXWI.js → geminiContentGenerator-4BQ5QHPR.js} +5 -5
- package/chunks/{glob-DMYFEEAZ.js → glob-FPKAMR6B.js} +32 -28
- package/chunks/{grep-GYE47MD4.js → grep-MOGRCYB4.js} +32 -28
- package/chunks/{handleAutoUpdate-OLUY4L7L.js → handleAutoUpdate-QUMRZZOS.js} +33 -34
- package/chunks/{i18n-462JC7IY.js → i18n-F3W6YUYP.js} +28 -29
- package/chunks/{initializer-Y7WD4D2I.js → initializer-KLU2KAIZ.js} +33 -34
- package/chunks/{installationInfo-I2Z3BKCE.js → installationInfo-P3ZP2SIZ.js} +27 -28
- package/chunks/{ja-PY5AF544.js → ja-2ICT3KXF.js} +3 -2
- package/chunks/{keychain-token-storage-AU22CQI6.js → keychain-token-storage-K23CFKGN.js} +2 -2
- package/chunks/{list-XQ5MSAXP.js → list-4X6D5JD2.js} +35 -36
- package/chunks/{loadedSettingsAdapter-TQOWYE7C.js → loadedSettingsAdapter-446B6WKA.js} +32 -33
- package/chunks/{loop-wakeup-QXWZSCYM.js → loop-wakeup-6H7OENTB.js} +5 -5
- package/chunks/{ls-2OUTG3IZ.js → ls-APTSOOS5.js} +4 -4
- package/chunks/{lsp-4VMNXZW6.js → lsp-HPACPJAN.js} +2 -2
- package/chunks/{mcp-22LLEEVM.js → mcp-JYVOJZOD.js} +32 -33
- package/chunks/{monitor-VKG324X3.js → monitor-KY7YH7TL.js} +26 -27
- package/chunks/nonInteractiveCli-EK35TLVJ.js +129 -0
- package/chunks/{notebook-edit-FJJRBQ3S.js → notebook-edit-IHGW42QK.js} +27 -28
- package/chunks/{openaiContentGenerator-4T2OGTR5.js → openaiContentGenerator-FURUCKQJ.js} +13 -14
- package/chunks/{pidfile-XL3MDCBM.js → pidfile-WKUVXDMR.js} +27 -28
- package/chunks/{processUtils-7UCFN34D.js → processUtils-LHSRN76X.js} +2 -2
- package/chunks/{pt-A3PRQXMU.js → pt-JJ4XPIBP.js} +3 -2
- package/chunks/{qwenContentGenerator-PQUXJDIZ.js → qwenContentGenerator-NRLM7R36.js} +30 -31
- package/chunks/{qwenOAuth2-LEYOU7RU.js → qwenOAuth2-5FNX3AWH.js} +22 -5
- package/chunks/{read-file-3EYQML7E.js → read-file-VLEAEXFP.js} +9 -10
- package/chunks/{read-mcp-resource-JUFHCKSC.js → read-mcp-resource-C6NT52T6.js} +2 -2
- package/chunks/{record-artifact-DWSMBXYY.js → record-artifact-ENCU7QCL.js} +2 -2
- package/chunks/{ripGrep-S6WPGQNA.js → ripGrep-MT23EIDD.js} +26 -27
- package/chunks/{ru-IBCWUTOC.js → ru-STR4KTW7.js} +3 -2
- package/chunks/{run-qwen-serve-NSKBT3HU.js → run-qwen-serve-5HWOQFLI.js} +63 -47
- package/chunks/{runtime-LG5ZZ3VH.js → runtime-6IVKJSLW.js} +38 -37
- package/chunks/{scheduler-PLX3VVTB.js → scheduler-63GJG75U.js} +26 -27
- package/chunks/{send-message-JZ752NWD.js → send-message-5GZ4FX2V.js} +3 -3
- package/chunks/{serve-S2ZLRZZR.js → serve-3LL7RRDP.js} +32 -33
- package/chunks/{server-5ECMV4TM.js → server-AA24QLPA.js} +1672 -615
- package/chunks/{session-ILSONWG4.js → session-LRVCFQQQ.js} +102 -75
- package/chunks/{settings-BOE7WWSA.js → settings-ZX3OKUT4.js} +36 -37
- package/chunks/{shell-VW664JIZ.js → shell-KR3IMYH6.js} +26 -27
- package/chunks/{skill-COHV6625.js → skill-QLM2LSF7.js} +11 -12
- package/chunks/{spawnChannel-5UUR5XOZ.js → spawnChannel-6WFOK3SA.js} +28 -29
- package/chunks/{src-XPKFZ3RN.js → src-ADLNIPHD.js} +101 -72
- package/chunks/{standalone-update-IOOJ2NZQ.js → standalone-update-C54OTJWJ.js} +29 -30
- package/chunks/{startInteractiveUI-3NGJGXXS.js → startInteractiveUI-KFAOS6HC.js} +2342 -1796
- package/chunks/{stdioHelpers-UMY72QRP.js → stdioHelpers-HGIYTWAY.js} +3 -1
- package/chunks/{syntheticOutput-LELY6HAI.js → syntheticOutput-ULOVVLY4.js} +3 -3
- package/chunks/{task-create-QY3BIMNM.js → task-create-DUE2PMNI.js} +7 -7
- package/chunks/{task-list-D2U47WBJ.js → task-list-WVPNX7BZ.js} +5 -5
- package/chunks/{task-stop-N6E5IYLH.js → task-stop-CPEQWOT6.js} +2 -2
- package/chunks/{task-update-HJCQILLB.js → task-update-HY2LNFFB.js} +7 -7
- package/chunks/{team-create-UPLVZQFX.js → team-create-Q5B5M7UD.js} +26 -27
- package/chunks/{team-delete-RCWYDRF4.js → team-delete-JJV6IUOA.js} +5 -5
- package/chunks/{team-plan-approval-F5F432RO.js → team-plan-approval-J55PXNY2.js} +26 -27
- package/chunks/{theme-manager-JJE4BJ67.js → theme-manager-QWTDRPHV.js} +27 -28
- package/chunks/todoWrite-JHHIDOIV.js +358 -0
- package/chunks/{tool-search-LRXI4JLZ.js → tool-search-LSSKWZKH.js} +10 -11
- package/chunks/{total-session-admission-I4DHN6UP.js → total-session-admission-S6ATY5HY.js} +32 -33
- package/chunks/{trustedFolders-SEH7LNX2.js → trustedFolders-CY4TUPDJ.js} +28 -29
- package/chunks/turndown.cjs-FZHGMUTY.js +17670 -0
- package/chunks/{update-relaunch-T6ZKBUA6.js → update-relaunch-IOKOUV3K.js} +6 -6
- package/chunks/{updateCheck-FWLXZPRO.js → updateCheck-YNRFE4V7.js} +30 -31
- package/chunks/{validateNonInterActiveAuth-X6K2VWYT.js → validateNonInterActiveAuth-OXTECS4B.js} +68 -68
- package/chunks/{version-LIFSOGSE.js → version-2CZQQDL5.js} +1 -1
- package/chunks/web-fetch-7QTEGJI5.js +938 -0
- package/chunks/{workflow-NX2DFWGD.js → workflow-G3CZU256.js} +29 -30
- package/chunks/{workspace-providers-status-VYD6M2NE.js → workspace-providers-status-UHQOIPAZ.js} +35 -36
- package/chunks/{workspace-registration-store-2Y6PWOEL.js → workspace-registration-store-5WPCMX4F.js} +1 -1
- package/chunks/{workspace-registry-PKBDBWCA.js → workspace-registry-LAQCRQTH.js} +32 -33
- package/chunks/{workspace-service-HHORUHTS.js → workspace-service-6ZEUJY2Z.js} +38 -39
- package/chunks/{workspace-skills-status-O5Q26LIY.js → workspace-skills-status-YG6OITSM.js} +33 -34
- package/chunks/{write-file-NVAEZ3J2.js → write-file-E7T3PU3B.js} +60 -28
- package/chunks/{zh-PAUNIMHQ.js → zh-6ANG2HTL.js} +7 -3
- package/chunks/{zh-TW-GNEUIUFF.js → zh-TW-4RR3YHNN.js} +7 -3
- package/cli-entry.js +37 -2
- package/cli.js +17 -11
- package/locales/ca.js +3 -2
- package/locales/de.js +3 -2
- package/locales/en.js +10 -5
- package/locales/fr.js +3 -2
- package/locales/ja.js +3 -2
- package/locales/pt.js +3 -2
- package/locales/ru.js +3 -2
- package/locales/zh-TW.js +9 -3
- package/locales/zh.js +9 -3
- package/package.json +3 -3
- package/web-shell/assets/{arc-H74fE8UR.js → arc-_dVIKMdB.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-DN3kEq-I.js → architectureDiagram-3BPJPVTR-MuN3nKpL.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-BrqbsFUF.js → blockDiagram-GPEHLZMM-BtmvJjzx.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-ClFhU1P-.js → c4Diagram-AAUBKEIU-Dvi58_BU.js} +1 -1
- package/web-shell/assets/channel-Bi5TehaT.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-jCIcG0_M.js → chunk-2J33WTMH-YqxOVhS1.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DSkGe62-.js → chunk-4BX2VUAB-DoBgbC9z.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-BxBHOGTd.js → chunk-55IACEB6-3NEQrBAr.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-v_DS7dH7.js → chunk-727SXJPM-DrCZQxp0.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-1RzEKRPq.js → chunk-AQP2D5EJ-m_g3zjgv.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-DyrSbLOX.js → chunk-FMBD7UC4-D1_crABJ.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-BCmdsZiy.js → chunk-ND2GUHAM-BdhgVtBx.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-CphHGW2D.js → chunk-QZHKN3VN-BlmThhm-.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BKhhRdd1.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BKhhRdd1.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-D5kRC7Fi.js → cose-bilkent-S5V4N54A-Cbjm_00Z.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-DIiyJJ0U.js → dagre-BM42HDAG-C39QNVgC.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-BOcuB8xU.js → diagram-2AECGRRQ-DNXSAixC.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-CMgiZ4d3.js → diagram-5GNKFQAL-BCbGUPEA.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-Ctx8pfN0.js → diagram-KO2AKTUF-1eujKSDC.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BT5REAWC.js → diagram-LMA3HP47-DUMKXgql.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-CJkUqXcp.js → diagram-OG6HWLK6-BFktU3_u.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-DyvNNX6v.js → erDiagram-TEJ5UH35-BvJC7DLk.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-VBf8_My8.js → flowDiagram-I6XJVG4X-Cgu3PlOt.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-CviJnR7B.js → ganttDiagram-6RSMTGT7-DLNXN5Xh.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-BY66_Oze.js → gitGraphDiagram-PVQCEYII-BQFRDv6R.js} +1 -1
- package/web-shell/assets/index-C_ouplMQ.js +1187 -0
- package/web-shell/assets/{index-DhYuvi3j.js → index-CdC3pjP3.js} +1 -1
- package/web-shell/assets/index-XGDFJE-X.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-D2jaewWZ.js → infoDiagram-5YYISTIA-DT2mOSvp.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BpnNTyi5.js → ishikawaDiagram-YF4QCWOH-9fy_K4bd.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-CA55GNZF.js → journeyDiagram-JHISSGLW-CoGSEuhE.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-DyiSl_Ts.js → kanban-definition-UN3LZRKU-BP9hHYIj.js} +1 -1
- package/web-shell/assets/{linear-B-sN3dkV.js → linear-uIroDsmp.js} +1 -1
- package/web-shell/assets/{mermaid.core-xf9SXDdb.js → mermaid.core-BUwoStsA.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-DXhr2kRk.js → mindmap-definition-RKZ34NQL-DZB-FWim.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-DUXpbfGQ.js → pieDiagram-4H26LBE5-YPptloKl.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-NeDQYeWw.js → quadrantDiagram-W4KKPZXB-dgk3DSzm.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-BSbFRL1A.js → requirementDiagram-4Y6WPE33-BH2En2i9.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-gg27cAaN.js → sankeyDiagram-5OEKKPKP-BB98feeI.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Bi_1TSs4.js → sequenceDiagram-3UESZ5HK-CunAIGh8.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CboCCRaf.js → stateDiagram-AJRCARHV-DTJs_-I2.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DrfFMYta.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-DrGpF_jn.js → timeline-definition-PNZ67QCA-Cd4AN7O0.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DCaJU67x.js → vennDiagram-CIIHVFJN-xIG-rUU9.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-p0QcW1aP.js → wardley-L42UT6IY-D1UyjIcG.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DiEVxj1W.js → wardleyDiagram-YWT4CUSO-BVM0Mb99.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-iQB8nSlA.js → xychartDiagram-2RQKCTM6-BX31JDSz.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-2HRYPZT5.js +0 -25
- package/chunks/chunk-QAW7PIHT.js +0 -168
- package/chunks/chunk-RTV5TUYB.js +0 -143
- package/chunks/chunk-ZPAHLFZU.js +0 -172
- package/chunks/exitPlanMode-HYQL4XMB.js +0 -909
- package/chunks/nonInteractiveCli-UMYUT643.js +0 -129
- package/chunks/todoWrite-FKCGATPJ.js +0 -531
- package/chunks/web-fetch-CFCWTCAP.js +0 -5673
- package/web-shell/assets/channel-NFC5ADAa.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-DHgwjz3X.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-DHgwjz3X.js +0 -1
- package/web-shell/assets/index-8xIBbHV3.js +0 -1143
- package/web-shell/assets/index-DLyhlZZO.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CXRqms6P.js +0 -1
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
import {
|
|
4
4
|
buildPermissionSettings
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LHEQDKAR.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveVoiceStreamConfig,
|
|
8
8
|
resolveVoiceTranscriptionConfig,
|
|
9
9
|
transcribeVoiceAudio
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XACBIZJB.js";
|
|
11
11
|
import {
|
|
12
12
|
getVoiceSettingsScope,
|
|
13
13
|
isSelectableVoiceModel,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
readVoiceMode,
|
|
17
17
|
readVoiceModel,
|
|
18
18
|
resolveVoiceTransport
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-SMASS3CY.js";
|
|
20
20
|
import {
|
|
21
21
|
WorkspacePermissionRulesSessionRequiredError,
|
|
22
22
|
WorkspaceSettingsPartialPersistError,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
WorkspaceInitPathEscapeError,
|
|
33
33
|
WorkspaceInitRaceError,
|
|
34
34
|
WorkspaceInitSymlinkError
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-X6YQWCIJ.js";
|
|
36
36
|
import {
|
|
37
37
|
BridgeChannelClosedError,
|
|
38
38
|
SERVE_CONTROL_EXT_METHODS,
|
|
@@ -46,34 +46,38 @@ import {
|
|
|
46
46
|
createIdleWorkspaceProvidersStatus,
|
|
47
47
|
createIdleWorkspaceSkillsStatus,
|
|
48
48
|
mapDomainErrorToErrorKind
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-TFV3XVAZ.js";
|
|
50
50
|
import {
|
|
51
51
|
getPersistScopeForModelSelection,
|
|
52
52
|
loadSettings
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-ILQ42UG6.js";
|
|
54
54
|
import {
|
|
55
55
|
getWorkspaceTrustStatus
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-XFJU7SVP.js";
|
|
57
57
|
import {
|
|
58
58
|
writeStderrLine
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-OWAK7LMW.js";
|
|
60
60
|
import {
|
|
61
|
-
ModelsConfig
|
|
62
|
-
|
|
61
|
+
ModelsConfig,
|
|
62
|
+
SkillManager,
|
|
63
|
+
require_yauzl
|
|
64
|
+
} from "./chunk-6TDA4EVC.js";
|
|
63
65
|
import {
|
|
66
|
+
Storage,
|
|
64
67
|
createDebugLogger
|
|
65
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-UE5TZ55T.js";
|
|
66
69
|
import {
|
|
67
70
|
init_esbuild_shims
|
|
68
71
|
} from "./chunk-5O2XNYP6.js";
|
|
69
72
|
import {
|
|
70
|
-
__name
|
|
73
|
+
__name,
|
|
74
|
+
__toESM
|
|
71
75
|
} from "./chunk-J2S4EL5Y.js";
|
|
72
76
|
|
|
73
77
|
// packages/cli/src/serve/workspace-service/index.ts
|
|
74
78
|
init_esbuild_shims();
|
|
75
|
-
import { promises as
|
|
76
|
-
import * as
|
|
79
|
+
import { promises as fs2, constants as fsConstants } from "node:fs";
|
|
80
|
+
import * as path2 from "node:path";
|
|
77
81
|
|
|
78
82
|
// packages/cli/src/services/voice-service.ts
|
|
79
83
|
init_esbuild_shims();
|
|
@@ -306,18 +310,748 @@ async function transcribeWorkspaceVoiceAudio(input) {
|
|
|
306
310
|
}
|
|
307
311
|
__name(transcribeWorkspaceVoiceAudio, "transcribeWorkspaceVoiceAudio");
|
|
308
312
|
|
|
313
|
+
// packages/cli/src/serve/workspace-skill-management.ts
|
|
314
|
+
init_esbuild_shims();
|
|
315
|
+
import { promises as fs } from "node:fs";
|
|
316
|
+
import { execFile } from "node:child_process";
|
|
317
|
+
import * as os from "node:os";
|
|
318
|
+
import * as path from "node:path";
|
|
319
|
+
import { promisify } from "node:util";
|
|
320
|
+
var import_yauzl = __toESM(require_yauzl(), 1);
|
|
321
|
+
var MAX_FILES = 128;
|
|
322
|
+
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
323
|
+
var MAX_TOTAL_BYTES = 6 * 1024 * 1024;
|
|
324
|
+
var MAX_PATH_LENGTH = 512;
|
|
325
|
+
var MAX_PATH_DEPTH = 16;
|
|
326
|
+
var MAX_WORKSPACE_SKILL_NAME_LENGTH = 256;
|
|
327
|
+
var execFileAsync = promisify(execFile);
|
|
328
|
+
var WorkspaceSkillManagementError = class extends Error {
|
|
329
|
+
constructor(code, message, statusCode = 400) {
|
|
330
|
+
super(message);
|
|
331
|
+
this.code = code;
|
|
332
|
+
this.statusCode = statusCode;
|
|
333
|
+
this.name = "WorkspaceSkillManagementError";
|
|
334
|
+
}
|
|
335
|
+
static {
|
|
336
|
+
__name(this, "WorkspaceSkillManagementError");
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
function skillError(code, message, statusCode = 400) {
|
|
340
|
+
throw new WorkspaceSkillManagementError(code, message, statusCode);
|
|
341
|
+
}
|
|
342
|
+
__name(skillError, "skillError");
|
|
343
|
+
function validateWorkspaceSkillName(name) {
|
|
344
|
+
const normalized = name.trim();
|
|
345
|
+
if (!normalized || normalized === "." || normalized === ".." || normalized.length > MAX_WORKSPACE_SKILL_NAME_LENGTH || !/^[A-Za-z0-9._-]+$/.test(normalized)) {
|
|
346
|
+
skillError("invalid_skill_name", "Invalid skill name");
|
|
347
|
+
}
|
|
348
|
+
return normalized;
|
|
349
|
+
}
|
|
350
|
+
__name(validateWorkspaceSkillName, "validateWorkspaceSkillName");
|
|
351
|
+
function decodeBase64(value) {
|
|
352
|
+
if (!value || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
|
|
353
|
+
skillError("invalid_skill_source", "Invalid base64 skill content");
|
|
354
|
+
}
|
|
355
|
+
const content = Buffer.from(value, "base64");
|
|
356
|
+
if (content.length > MAX_TOTAL_BYTES) {
|
|
357
|
+
skillError(
|
|
358
|
+
"skill_package_too_large",
|
|
359
|
+
"Skill package exceeds the allowed size",
|
|
360
|
+
413
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
return content;
|
|
364
|
+
}
|
|
365
|
+
__name(decodeBase64, "decodeBase64");
|
|
366
|
+
function normalizeRelativePath(value) {
|
|
367
|
+
const normalized = value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
368
|
+
const segments = normalized.split("/");
|
|
369
|
+
if (!normalized || normalized.length > MAX_PATH_LENGTH || normalized.startsWith("/") || segments.length > MAX_PATH_DEPTH || segments.some((segment) => !segment || segment === "." || segment === "..")) {
|
|
370
|
+
skillError("invalid_skill_package", `Invalid skill file path: ${value}`);
|
|
371
|
+
}
|
|
372
|
+
return segments.join("/");
|
|
373
|
+
}
|
|
374
|
+
__name(normalizeRelativePath, "normalizeRelativePath");
|
|
375
|
+
function isPlatformMetadataPath(value) {
|
|
376
|
+
return value.replaceAll("\\", "/").split("/").some(
|
|
377
|
+
(segment) => segment === "__MACOSX" || segment === ".DS_Store" || segment.startsWith("._")
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
__name(isPlatformMetadataPath, "isPlatformMetadataPath");
|
|
381
|
+
function normalizePackageFiles(files) {
|
|
382
|
+
files = files.filter((file) => !isPlatformMetadataPath(file.relativePath));
|
|
383
|
+
if (!files.length || files.length > MAX_FILES) {
|
|
384
|
+
skillError(
|
|
385
|
+
"invalid_skill_package",
|
|
386
|
+
"Skill package has an invalid file count"
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
let normalized = files.map((file) => ({
|
|
390
|
+
relativePath: normalizeRelativePath(file.relativePath),
|
|
391
|
+
content: file.content
|
|
392
|
+
}));
|
|
393
|
+
if (!normalized.some((file) => file.relativePath === "SKILL.md")) {
|
|
394
|
+
const roots = new Set(
|
|
395
|
+
normalized.map((file) => file.relativePath.split("/")[0])
|
|
396
|
+
);
|
|
397
|
+
if (roots.size !== 1) {
|
|
398
|
+
skillError(
|
|
399
|
+
"skill_manifest_missing",
|
|
400
|
+
"Skill package must contain a root SKILL.md"
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
const [root] = roots;
|
|
404
|
+
normalized = normalized.map((file) => ({
|
|
405
|
+
...file,
|
|
406
|
+
relativePath: file.relativePath.slice(root.length + 1)
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
410
|
+
let totalBytes = 0;
|
|
411
|
+
for (const file of normalized) {
|
|
412
|
+
file.relativePath = normalizeRelativePath(file.relativePath);
|
|
413
|
+
if (seen.has(file.relativePath)) {
|
|
414
|
+
skillError(
|
|
415
|
+
"invalid_skill_package",
|
|
416
|
+
`Duplicate skill file path: ${file.relativePath}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
seen.add(file.relativePath);
|
|
420
|
+
if (file.content.length > MAX_FILE_BYTES) {
|
|
421
|
+
skillError(
|
|
422
|
+
"skill_package_too_large",
|
|
423
|
+
`Skill file is too large: ${file.relativePath}`,
|
|
424
|
+
413
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
totalBytes += file.content.length;
|
|
428
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
429
|
+
skillError(
|
|
430
|
+
"skill_package_too_large",
|
|
431
|
+
"Skill package exceeds the allowed size",
|
|
432
|
+
413
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (!seen.has("SKILL.md")) {
|
|
437
|
+
skillError(
|
|
438
|
+
"skill_manifest_missing",
|
|
439
|
+
"Skill package must contain a root SKILL.md"
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
return normalized;
|
|
443
|
+
}
|
|
444
|
+
__name(normalizePackageFiles, "normalizePackageFiles");
|
|
445
|
+
function githubRequestError(code, status, resource) {
|
|
446
|
+
const message = status === 404 ? `GitHub ${resource} was not found; check the repository URL, branch, and path` : status === 401 ? "GitHub authentication failed; check GH_TOKEN or GITHUB_TOKEN" : status === 403 ? "GitHub access was denied; the repository may be private or API rate-limited" : status === 429 ? "GitHub API rate limit exceeded; try again later or configure GH_TOKEN" : `GitHub request failed (HTTP ${status})`;
|
|
447
|
+
return new WorkspaceSkillManagementError(
|
|
448
|
+
code,
|
|
449
|
+
message,
|
|
450
|
+
status === 404 ? 404 : 502
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
__name(githubRequestError, "githubRequestError");
|
|
454
|
+
async function fetchBytes(url, githubToken) {
|
|
455
|
+
const response = await fetch(url, {
|
|
456
|
+
headers: {
|
|
457
|
+
Accept: "application/vnd.github+json",
|
|
458
|
+
"User-Agent": "qwen-code",
|
|
459
|
+
...githubToken ? { Authorization: `Bearer ${githubToken}` } : {}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
if (!response.ok) {
|
|
463
|
+
throw githubRequestError(
|
|
464
|
+
"github_skill_download_failed",
|
|
465
|
+
response.status,
|
|
466
|
+
"Skill file"
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const declared = Number(response.headers.get("content-length"));
|
|
470
|
+
if (Number.isFinite(declared) && declared > MAX_FILE_BYTES) {
|
|
471
|
+
skillError("skill_package_too_large", "Skill file is too large", 413);
|
|
472
|
+
}
|
|
473
|
+
if (!response.body) return Buffer.alloc(0);
|
|
474
|
+
const reader = response.body.getReader();
|
|
475
|
+
const chunks = [];
|
|
476
|
+
let size = 0;
|
|
477
|
+
try {
|
|
478
|
+
while (true) {
|
|
479
|
+
const { done, value } = await reader.read();
|
|
480
|
+
if (done) break;
|
|
481
|
+
size += value.byteLength;
|
|
482
|
+
if (size > MAX_FILE_BYTES) {
|
|
483
|
+
await reader.cancel().catch(() => void 0);
|
|
484
|
+
skillError("skill_package_too_large", "Skill file is too large", 413);
|
|
485
|
+
}
|
|
486
|
+
chunks.push(Buffer.from(value));
|
|
487
|
+
}
|
|
488
|
+
} finally {
|
|
489
|
+
reader.releaseLock();
|
|
490
|
+
}
|
|
491
|
+
return Buffer.concat(chunks, size);
|
|
492
|
+
}
|
|
493
|
+
__name(fetchBytes, "fetchBytes");
|
|
494
|
+
async function downloadGitHubDirectory(owner, repo, ref, directory, githubToken, relativeRoot = "", depth = 0, state = {
|
|
495
|
+
files: [],
|
|
496
|
+
totalBytes: 0
|
|
497
|
+
}) {
|
|
498
|
+
if (depth > MAX_PATH_DEPTH)
|
|
499
|
+
skillError("invalid_skill_package", "GitHub Skill is nested too deeply");
|
|
500
|
+
const encodedDirectory = directory.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
501
|
+
const apiUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents${encodedDirectory ? `/${encodedDirectory}` : ""}?ref=${encodeURIComponent(ref)}`;
|
|
502
|
+
const response = await fetch(apiUrl, {
|
|
503
|
+
headers: {
|
|
504
|
+
Accept: "application/vnd.github+json",
|
|
505
|
+
"User-Agent": "qwen-code",
|
|
506
|
+
...githubToken ? { Authorization: `Bearer ${githubToken}` } : {}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
throw githubRequestError(
|
|
511
|
+
"github_api_failed",
|
|
512
|
+
response.status,
|
|
513
|
+
"Skill path"
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
const items = await response.json();
|
|
517
|
+
if (!Array.isArray(items))
|
|
518
|
+
skillError("invalid_skill_source", "GitHub URL must point to SKILL.md");
|
|
519
|
+
for (const item of items) {
|
|
520
|
+
if (!item || typeof item !== "object") continue;
|
|
521
|
+
const record = item;
|
|
522
|
+
const itemName = typeof record["name"] === "string" ? record["name"] : "";
|
|
523
|
+
const itemPath = typeof record["path"] === "string" ? record["path"] : "";
|
|
524
|
+
const itemType = typeof record["type"] === "string" ? record["type"] : "";
|
|
525
|
+
const relativePath = relativeRoot ? `${relativeRoot}/${itemName}` : itemName;
|
|
526
|
+
if (itemType === "dir") {
|
|
527
|
+
await downloadGitHubDirectory(
|
|
528
|
+
owner,
|
|
529
|
+
repo,
|
|
530
|
+
ref,
|
|
531
|
+
itemPath,
|
|
532
|
+
githubToken,
|
|
533
|
+
relativePath,
|
|
534
|
+
depth + 1,
|
|
535
|
+
state
|
|
536
|
+
);
|
|
537
|
+
} else if (itemType === "file") {
|
|
538
|
+
const downloadUrl = record["download_url"];
|
|
539
|
+
if (typeof downloadUrl !== "string") continue;
|
|
540
|
+
const parsedDownloadUrl = new URL(downloadUrl);
|
|
541
|
+
if (parsedDownloadUrl.protocol !== "https:" || parsedDownloadUrl.hostname !== "raw.githubusercontent.com") {
|
|
542
|
+
skillError(
|
|
543
|
+
"github_skill_download_failed",
|
|
544
|
+
"GitHub returned an invalid Skill file URL",
|
|
545
|
+
502
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const content = await fetchBytes(downloadUrl, githubToken);
|
|
549
|
+
state.files.push({ relativePath, content });
|
|
550
|
+
state.totalBytes += content.length;
|
|
551
|
+
if (state.files.length > MAX_FILES)
|
|
552
|
+
skillError("invalid_skill_package", "Skill package has too many files");
|
|
553
|
+
if (state.totalBytes > MAX_TOTAL_BYTES) {
|
|
554
|
+
skillError(
|
|
555
|
+
"skill_package_too_large",
|
|
556
|
+
"Skill package exceeds the allowed size",
|
|
557
|
+
413
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return state.files;
|
|
563
|
+
}
|
|
564
|
+
__name(downloadGitHubDirectory, "downloadGitHubDirectory");
|
|
565
|
+
async function downloadGitHubDirectoryWithGit(owner, repo, ref, directory) {
|
|
566
|
+
const checkout = await fs.mkdtemp(path.join(os.tmpdir(), "qwen-skill-git-"));
|
|
567
|
+
try {
|
|
568
|
+
await execFileAsync(
|
|
569
|
+
"git",
|
|
570
|
+
[
|
|
571
|
+
"clone",
|
|
572
|
+
"--depth",
|
|
573
|
+
"1",
|
|
574
|
+
"--filter=blob:none",
|
|
575
|
+
"--sparse",
|
|
576
|
+
"--branch",
|
|
577
|
+
ref,
|
|
578
|
+
`https://github.com/${owner}/${repo}.git`,
|
|
579
|
+
checkout
|
|
580
|
+
],
|
|
581
|
+
{ timeout: 6e4, maxBuffer: 1024 * 1024 }
|
|
582
|
+
);
|
|
583
|
+
if (directory) {
|
|
584
|
+
await execFileAsync(
|
|
585
|
+
"git",
|
|
586
|
+
["-C", checkout, "sparse-checkout", "set", "--cone", directory],
|
|
587
|
+
{ timeout: 3e4, maxBuffer: 1024 * 1024 }
|
|
588
|
+
);
|
|
589
|
+
} else {
|
|
590
|
+
await execFileAsync(
|
|
591
|
+
"git",
|
|
592
|
+
["-C", checkout, "sparse-checkout", "disable"],
|
|
593
|
+
{ timeout: 3e4, maxBuffer: 1024 * 1024 }
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
return await filesFromFolder(path.join(checkout, directory));
|
|
597
|
+
} catch (error) {
|
|
598
|
+
if (error instanceof WorkspaceSkillManagementError) throw error;
|
|
599
|
+
throw new WorkspaceSkillManagementError(
|
|
600
|
+
"github_skill_download_failed",
|
|
601
|
+
`Failed to download GitHub Skill: ${error instanceof Error ? error.message : String(error)}`,
|
|
602
|
+
502
|
|
603
|
+
);
|
|
604
|
+
} finally {
|
|
605
|
+
await fs.rm(checkout, { recursive: true, force: true });
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
__name(downloadGitHubDirectoryWithGit, "downloadGitHubDirectoryWithGit");
|
|
609
|
+
async function downloadGitHubSkill(sourceUrl, githubToken) {
|
|
610
|
+
let url;
|
|
611
|
+
try {
|
|
612
|
+
url = new URL(sourceUrl);
|
|
613
|
+
} catch {
|
|
614
|
+
skillError("invalid_skill_source", "Invalid GitHub Skill URL");
|
|
615
|
+
}
|
|
616
|
+
if (url.protocol !== "https:")
|
|
617
|
+
skillError("invalid_skill_source", "GitHub URL must use HTTPS");
|
|
618
|
+
if (url.hostname === "raw.githubusercontent.com") {
|
|
619
|
+
if (!url.pathname.endsWith("/SKILL.md")) {
|
|
620
|
+
skillError("invalid_skill_source", "GitHub URL must point to SKILL.md");
|
|
621
|
+
}
|
|
622
|
+
return [
|
|
623
|
+
{
|
|
624
|
+
relativePath: "SKILL.md",
|
|
625
|
+
content: await fetchBytes(url.toString(), githubToken)
|
|
626
|
+
}
|
|
627
|
+
];
|
|
628
|
+
}
|
|
629
|
+
if (url.hostname !== "github.com") {
|
|
630
|
+
skillError("invalid_skill_source", "Only GitHub Skill URLs are supported");
|
|
631
|
+
}
|
|
632
|
+
let segments;
|
|
633
|
+
try {
|
|
634
|
+
segments = url.pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
635
|
+
} catch {
|
|
636
|
+
skillError("invalid_skill_source", "Invalid GitHub Skill URL");
|
|
637
|
+
}
|
|
638
|
+
if (segments.length < 5 || segments[2] !== "blob" || segments.at(-1) !== "SKILL.md") {
|
|
639
|
+
skillError(
|
|
640
|
+
"invalid_skill_source",
|
|
641
|
+
"GitHub URL must point to a repository SKILL.md file"
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
const [owner, repo, , ref, ...filePath] = segments;
|
|
645
|
+
if (!owner || !repo || !ref || ref.startsWith("-") || !/^[A-Za-z0-9._/+-]+$/.test(ref) || !/^[A-Za-z0-9._-]+$/.test(owner) || !/^[A-Za-z0-9._-]+$/.test(repo)) {
|
|
646
|
+
skillError("invalid_skill_source", "Invalid GitHub Skill URL");
|
|
647
|
+
}
|
|
648
|
+
const directory = filePath.slice(0, -1).join("/");
|
|
649
|
+
const directorySegments = directory.split("/");
|
|
650
|
+
if (directory && (directory.length > MAX_PATH_LENGTH || directory.includes("\\") || directory.startsWith("/") || directorySegments.length > MAX_PATH_DEPTH || directorySegments.some(
|
|
651
|
+
(segment) => !segment || segment === "." || segment === ".."
|
|
652
|
+
))) {
|
|
653
|
+
skillError("invalid_skill_source", "Invalid GitHub Skill URL path");
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
return await downloadGitHubDirectory(
|
|
657
|
+
owner,
|
|
658
|
+
repo,
|
|
659
|
+
ref,
|
|
660
|
+
directory,
|
|
661
|
+
githubToken
|
|
662
|
+
);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
if (!(error instanceof WorkspaceSkillManagementError) || error.code !== "github_api_failed" || error.statusCode === 404) {
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
return downloadGitHubDirectoryWithGit(owner, repo, ref, directory);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
__name(downloadGitHubSkill, "downloadGitHubSkill");
|
|
671
|
+
function openZip(content) {
|
|
672
|
+
return new Promise((resolve3, reject) => {
|
|
673
|
+
(0, import_yauzl.fromBuffer)(content, { lazyEntries: true }, (error, zipFile) => {
|
|
674
|
+
if (error) reject(error);
|
|
675
|
+
else resolve3(zipFile);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
__name(openZip, "openZip");
|
|
680
|
+
function readZipEntry(zipFile, entry) {
|
|
681
|
+
return new Promise((resolve3, reject) => {
|
|
682
|
+
zipFile.openReadStream(entry, (error, stream) => {
|
|
683
|
+
if (error) {
|
|
684
|
+
reject(error);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const chunks = [];
|
|
688
|
+
let size = 0;
|
|
689
|
+
stream.on("data", (chunk) => {
|
|
690
|
+
size += chunk.length;
|
|
691
|
+
if (size > MAX_FILE_BYTES) {
|
|
692
|
+
stream.destroy(
|
|
693
|
+
new WorkspaceSkillManagementError(
|
|
694
|
+
"skill_package_too_large",
|
|
695
|
+
`Skill ZIP entry is too large: ${entry.fileName}`,
|
|
696
|
+
413
|
|
697
|
+
)
|
|
698
|
+
);
|
|
699
|
+
} else {
|
|
700
|
+
chunks.push(chunk);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
stream.on("error", reject);
|
|
704
|
+
stream.on("end", () => resolve3(Buffer.concat(chunks)));
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
__name(readZipEntry, "readZipEntry");
|
|
709
|
+
async function filesFromZip(content) {
|
|
710
|
+
const zipFile = await openZip(content);
|
|
711
|
+
const files = [];
|
|
712
|
+
let totalBytes = 0;
|
|
713
|
+
return new Promise((resolve3, reject) => {
|
|
714
|
+
let settled = false;
|
|
715
|
+
const finish = /* @__PURE__ */ __name((error) => {
|
|
716
|
+
if (settled) return;
|
|
717
|
+
settled = true;
|
|
718
|
+
zipFile.close();
|
|
719
|
+
if (error) reject(error);
|
|
720
|
+
else resolve3(files);
|
|
721
|
+
}, "finish");
|
|
722
|
+
zipFile.on("error", finish);
|
|
723
|
+
zipFile.on("entry", (entry) => {
|
|
724
|
+
const mode = entry.externalFileAttributes >>> 16 & 65535;
|
|
725
|
+
const fileType = mode & 61440;
|
|
726
|
+
if (fileType === 40960) {
|
|
727
|
+
finish(new Error("Skill ZIP contains a symbolic link"));
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (entry.generalPurposeBitFlag & 1) {
|
|
731
|
+
finish(new Error("Encrypted Skill ZIP entries are not supported"));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (entry.fileName.endsWith("/") || fileType === 16384) {
|
|
735
|
+
zipFile.readEntry();
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (files.length >= MAX_FILES) {
|
|
739
|
+
finish(
|
|
740
|
+
new WorkspaceSkillManagementError(
|
|
741
|
+
"invalid_skill_package",
|
|
742
|
+
"Skill ZIP contains too many files"
|
|
743
|
+
)
|
|
744
|
+
);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (entry.uncompressedSize > MAX_FILE_BYTES) {
|
|
748
|
+
finish(
|
|
749
|
+
new WorkspaceSkillManagementError(
|
|
750
|
+
"skill_package_too_large",
|
|
751
|
+
"Skill ZIP entry exceeds the allowed size",
|
|
752
|
+
413
|
|
753
|
+
)
|
|
754
|
+
);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (totalBytes + entry.uncompressedSize > MAX_TOTAL_BYTES) {
|
|
758
|
+
finish(
|
|
759
|
+
new WorkspaceSkillManagementError(
|
|
760
|
+
"skill_package_too_large",
|
|
761
|
+
"Skill ZIP expands beyond the allowed size",
|
|
762
|
+
413
|
|
763
|
+
)
|
|
764
|
+
);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
void readZipEntry(zipFile, entry).then((entryContent) => {
|
|
768
|
+
totalBytes += entryContent.length;
|
|
769
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
770
|
+
finish(
|
|
771
|
+
new WorkspaceSkillManagementError(
|
|
772
|
+
"skill_package_too_large",
|
|
773
|
+
"Skill ZIP expands beyond the allowed size",
|
|
774
|
+
413
|
|
775
|
+
)
|
|
776
|
+
);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
files.push({ relativePath: entry.fileName, content: entryContent });
|
|
780
|
+
if (!settled) zipFile.readEntry();
|
|
781
|
+
}, finish);
|
|
782
|
+
});
|
|
783
|
+
zipFile.on("end", () => finish());
|
|
784
|
+
zipFile.readEntry();
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
__name(filesFromZip, "filesFromZip");
|
|
788
|
+
async function filesFromSource(source, githubToken) {
|
|
789
|
+
try {
|
|
790
|
+
if (source.type === "github")
|
|
791
|
+
return normalizePackageFiles(
|
|
792
|
+
await downloadGitHubSkill(source.url, githubToken)
|
|
793
|
+
);
|
|
794
|
+
if (source.type === "folder")
|
|
795
|
+
return normalizePackageFiles(await filesFromFolder(source.path));
|
|
796
|
+
const archive = decodeBase64(source.contentBase64);
|
|
797
|
+
return normalizePackageFiles(await filesFromZip(archive));
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (error instanceof WorkspaceSkillManagementError) throw error;
|
|
800
|
+
const code = source.type === "github" ? "github_skill_download_failed" : source.type === "folder" ? "invalid_skill_folder" : "invalid_skill_package";
|
|
801
|
+
throw new WorkspaceSkillManagementError(
|
|
802
|
+
code,
|
|
803
|
+
error instanceof Error ? error.message : String(error),
|
|
804
|
+
source.type === "github" ? 502 : 400
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
__name(filesFromSource, "filesFromSource");
|
|
809
|
+
async function filesFromFolder(folderPath) {
|
|
810
|
+
if (!path.isAbsolute(folderPath)) {
|
|
811
|
+
skillError("invalid_skill_folder", "Skill folder path must be absolute");
|
|
812
|
+
}
|
|
813
|
+
if ((await fs.lstat(folderPath)).isSymbolicLink()) {
|
|
814
|
+
skillError(
|
|
815
|
+
"invalid_skill_folder",
|
|
816
|
+
"Skill folder path must not be a symbolic link"
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
const root = await fs.realpath(folderPath);
|
|
820
|
+
if (!(await fs.stat(root)).isDirectory()) {
|
|
821
|
+
skillError(
|
|
822
|
+
"invalid_skill_folder",
|
|
823
|
+
"Skill folder path must point to a directory"
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
const files = [];
|
|
827
|
+
let totalBytes = 0;
|
|
828
|
+
const visit = /* @__PURE__ */ __name(async (directory, relativeRoot = "", depth = 0) => {
|
|
829
|
+
if (depth > MAX_PATH_DEPTH)
|
|
830
|
+
skillError("invalid_skill_package", "Skill folder is nested too deeply");
|
|
831
|
+
for (const entry of await fs.readdir(directory, { withFileTypes: true })) {
|
|
832
|
+
if (depth === 0 && entry.name === ".git") continue;
|
|
833
|
+
const absolutePath = path.join(directory, entry.name);
|
|
834
|
+
const relativePath = relativeRoot ? `${relativeRoot}/${entry.name}` : entry.name;
|
|
835
|
+
if (entry.isSymbolicLink()) {
|
|
836
|
+
skillError(
|
|
837
|
+
"unsafe_skill_path",
|
|
838
|
+
"Skill folder contains a symbolic link"
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
if (entry.isDirectory()) {
|
|
842
|
+
await visit(absolutePath, relativePath, depth + 1);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (!entry.isFile())
|
|
846
|
+
skillError("unsafe_skill_path", "Skill folder contains a special file");
|
|
847
|
+
if (files.length >= MAX_FILES) {
|
|
848
|
+
skillError("invalid_skill_package", "Skill package has too many files");
|
|
849
|
+
}
|
|
850
|
+
const stats = await fs.lstat(absolutePath);
|
|
851
|
+
if (stats.isSymbolicLink() || !stats.isFile()) {
|
|
852
|
+
skillError("unsafe_skill_path", "Skill folder contains an unsafe file");
|
|
853
|
+
}
|
|
854
|
+
if (stats.size > MAX_FILE_BYTES) {
|
|
855
|
+
skillError(
|
|
856
|
+
"skill_package_too_large",
|
|
857
|
+
`Skill file is too large: ${relativePath}`,
|
|
858
|
+
413
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
totalBytes += stats.size;
|
|
862
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
863
|
+
skillError(
|
|
864
|
+
"skill_package_too_large",
|
|
865
|
+
"Skill package exceeds the allowed size",
|
|
866
|
+
413
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
const content = await fs.readFile(absolutePath);
|
|
870
|
+
if (content.length !== stats.size) {
|
|
871
|
+
skillError(
|
|
872
|
+
"invalid_skill_package",
|
|
873
|
+
`Skill file changed while reading: ${relativePath}`
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
files.push({ relativePath, content });
|
|
877
|
+
}
|
|
878
|
+
}, "visit");
|
|
879
|
+
await visit(root);
|
|
880
|
+
return files;
|
|
881
|
+
}
|
|
882
|
+
__name(filesFromFolder, "filesFromFolder");
|
|
883
|
+
function skillBaseDir(workspace, scope) {
|
|
884
|
+
return scope === "workspace" ? path.join(workspace, ".qwen", "skills") : path.join(Storage.getGlobalQwenDir(), "skills");
|
|
885
|
+
}
|
|
886
|
+
__name(skillBaseDir, "skillBaseDir");
|
|
887
|
+
async function removeInstallArtifacts(baseDir, skillName) {
|
|
888
|
+
const workDir = path.dirname(baseDir);
|
|
889
|
+
const workPrefix = `.${path.basename(baseDir)}-${skillName}`;
|
|
890
|
+
const locations = [
|
|
891
|
+
{
|
|
892
|
+
directory: baseDir,
|
|
893
|
+
prefixes: [`.${skillName}.installing-`, `.${skillName}.backup-`]
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
directory: workDir,
|
|
897
|
+
prefixes: [`${workPrefix}.installing-`, `${workPrefix}.backup-`]
|
|
898
|
+
}
|
|
899
|
+
];
|
|
900
|
+
for (const { directory, prefixes } of locations) {
|
|
901
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
902
|
+
await Promise.all(
|
|
903
|
+
entries.filter(
|
|
904
|
+
(entry) => prefixes.some((prefix) => entry.name.startsWith(prefix))
|
|
905
|
+
).map(
|
|
906
|
+
(entry) => fs.rm(path.join(directory, entry.name), {
|
|
907
|
+
recursive: true,
|
|
908
|
+
force: true
|
|
909
|
+
})
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
__name(removeInstallArtifacts, "removeInstallArtifacts");
|
|
915
|
+
async function ensureDirectoryWithoutSymlinks(directory) {
|
|
916
|
+
const missing = [];
|
|
917
|
+
let current = directory;
|
|
918
|
+
while (true) {
|
|
919
|
+
try {
|
|
920
|
+
const stats = await fs.lstat(current);
|
|
921
|
+
if (stats.isSymbolicLink() || !stats.isDirectory()) {
|
|
922
|
+
skillError("unsafe_skill_path", `Unsafe Skill directory: ${current}`);
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
if (error.code !== "ENOENT") throw error;
|
|
927
|
+
missing.push(current);
|
|
928
|
+
const parent = path.dirname(current);
|
|
929
|
+
if (parent === current) throw error;
|
|
930
|
+
current = parent;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
for (const entry of missing.reverse()) await fs.mkdir(entry);
|
|
934
|
+
}
|
|
935
|
+
__name(ensureDirectoryWithoutSymlinks, "ensureDirectoryWithoutSymlinks");
|
|
936
|
+
async function installWorkspaceSkill(workspace, request, githubToken) {
|
|
937
|
+
const skillName = validateWorkspaceSkillName(request.name);
|
|
938
|
+
const files = await filesFromSource(request.source, githubToken);
|
|
939
|
+
const baseDir = skillBaseDir(workspace, request.scope);
|
|
940
|
+
await ensureDirectoryWithoutSymlinks(baseDir);
|
|
941
|
+
await removeInstallArtifacts(baseDir, skillName);
|
|
942
|
+
const destination = path.join(baseDir, skillName);
|
|
943
|
+
const existing = await fs.lstat(destination).catch(() => void 0);
|
|
944
|
+
if (existing?.isSymbolicLink() || existing && !existing.isDirectory()) {
|
|
945
|
+
skillError("unsafe_skill_path", "Refusing to replace an unsafe Skill path");
|
|
946
|
+
}
|
|
947
|
+
const workDir = path.dirname(baseDir);
|
|
948
|
+
const workPrefix = `.${path.basename(baseDir)}-${skillName}`;
|
|
949
|
+
const staging = await fs.mkdtemp(
|
|
950
|
+
path.join(workDir, `${workPrefix}.installing-`)
|
|
951
|
+
);
|
|
952
|
+
const backup = path.join(workDir, `${workPrefix}.backup-${Date.now()}`);
|
|
953
|
+
let movedExisting = false;
|
|
954
|
+
try {
|
|
955
|
+
for (const file of files) {
|
|
956
|
+
const target = path.join(staging, ...file.relativePath.split("/"));
|
|
957
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
958
|
+
await fs.writeFile(target, file.content);
|
|
959
|
+
}
|
|
960
|
+
const skillFile = path.join(staging, "SKILL.md");
|
|
961
|
+
let parsed;
|
|
962
|
+
try {
|
|
963
|
+
parsed = new SkillManager({}).parseSkillContent(
|
|
964
|
+
await fs.readFile(skillFile, "utf8"),
|
|
965
|
+
skillFile,
|
|
966
|
+
request.scope === "workspace" ? "project" : "user"
|
|
967
|
+
);
|
|
968
|
+
} catch (error) {
|
|
969
|
+
throw new WorkspaceSkillManagementError(
|
|
970
|
+
"invalid_skill_manifest",
|
|
971
|
+
error instanceof Error ? error.message : String(error)
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
if (parsed.name !== skillName) {
|
|
975
|
+
skillError(
|
|
976
|
+
"skill_name_mismatch",
|
|
977
|
+
`Skill name "${parsed.name}" does not match requested name "${skillName}"`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
if (existing) {
|
|
981
|
+
await fs.rename(destination, backup);
|
|
982
|
+
movedExisting = true;
|
|
983
|
+
}
|
|
984
|
+
await fs.rename(staging, destination);
|
|
985
|
+
} catch (error) {
|
|
986
|
+
await fs.rm(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
987
|
+
if (movedExisting) {
|
|
988
|
+
await fs.rename(backup, destination).catch(() => void 0);
|
|
989
|
+
}
|
|
990
|
+
throw error;
|
|
991
|
+
}
|
|
992
|
+
if (movedExisting) {
|
|
993
|
+
await fs.rm(backup, { recursive: true, force: true }).catch(() => void 0);
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
skillName,
|
|
997
|
+
scope: request.scope,
|
|
998
|
+
installedPath: path.join(destination, "SKILL.md")
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
__name(installWorkspaceSkill, "installWorkspaceSkill");
|
|
1002
|
+
async function deleteWorkspaceSkill(workspace, scope, skillNameInput, installedPath) {
|
|
1003
|
+
const skillName = validateWorkspaceSkillName(skillNameInput);
|
|
1004
|
+
const skillDir = path.resolve(path.dirname(installedPath));
|
|
1005
|
+
const skillFile = path.resolve(installedPath);
|
|
1006
|
+
const allowedBaseDirs = new SkillManager({
|
|
1007
|
+
getProjectRoot: /* @__PURE__ */ __name(() => workspace, "getProjectRoot")
|
|
1008
|
+
}).getSkillsBaseDirs(scope === "workspace" ? "project" : "user").map((directory) => path.resolve(directory));
|
|
1009
|
+
const baseDir = path.dirname(skillDir);
|
|
1010
|
+
if (skillFile !== path.join(skillDir, "SKILL.md") || !allowedBaseDirs.includes(baseDir) || path.basename(skillDir) !== skillName) {
|
|
1011
|
+
skillError(
|
|
1012
|
+
"skill_not_managed",
|
|
1013
|
+
"Skill is not managed in the requested scope",
|
|
1014
|
+
409
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
const baseRealPath = await fs.realpath(baseDir);
|
|
1018
|
+
const skillDirStats = await fs.lstat(skillDir);
|
|
1019
|
+
const skillFileStats = await fs.lstat(skillFile);
|
|
1020
|
+
if (skillDirStats.isSymbolicLink() || !skillDirStats.isDirectory() || skillFileStats.isSymbolicLink() || !skillFileStats.isFile()) {
|
|
1021
|
+
skillError("unsafe_skill_path", "Refusing to delete an unsafe Skill path");
|
|
1022
|
+
}
|
|
1023
|
+
const skillDirRealPath = await fs.realpath(skillDir);
|
|
1024
|
+
if (path.dirname(skillDirRealPath) !== baseRealPath) {
|
|
1025
|
+
skillError("unsafe_skill_path", "Refusing to delete an unsafe Skill path");
|
|
1026
|
+
}
|
|
1027
|
+
const parsed = new SkillManager({}).parseSkillContent(
|
|
1028
|
+
await fs.readFile(skillFile, "utf8"),
|
|
1029
|
+
skillFile,
|
|
1030
|
+
scope === "workspace" ? "project" : "user"
|
|
1031
|
+
);
|
|
1032
|
+
if (parsed.name !== skillName) {
|
|
1033
|
+
skillError(
|
|
1034
|
+
"skill_name_mismatch",
|
|
1035
|
+
"Skill name does not match its installed directory"
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
await fs.rm(skillDir, { recursive: true, force: true });
|
|
1039
|
+
return { skillName, scope, deleted: true };
|
|
1040
|
+
}
|
|
1041
|
+
__name(deleteWorkspaceSkill, "deleteWorkspaceSkill");
|
|
1042
|
+
|
|
309
1043
|
// packages/cli/src/serve/workspace-service/index.ts
|
|
310
1044
|
async function canonicalizeExistingAncestor(inputPath) {
|
|
311
1045
|
let current = inputPath;
|
|
312
1046
|
while (true) {
|
|
313
1047
|
try {
|
|
314
|
-
return await
|
|
1048
|
+
return await fs2.realpath(current);
|
|
315
1049
|
} catch (err) {
|
|
316
1050
|
const code = err?.code;
|
|
317
1051
|
if (code !== "ENOENT" && code !== "ENOTDIR" && code !== "ELOOP") {
|
|
318
1052
|
throw err;
|
|
319
1053
|
}
|
|
320
|
-
const parent =
|
|
1054
|
+
const parent = path2.dirname(current);
|
|
321
1055
|
if (parent === current) throw err;
|
|
322
1056
|
current = parent;
|
|
323
1057
|
}
|
|
@@ -326,9 +1060,9 @@ async function canonicalizeExistingAncestor(inputPath) {
|
|
|
326
1060
|
__name(canonicalizeExistingAncestor, "canonicalizeExistingAncestor");
|
|
327
1061
|
async function verifyParentPostOpen(target, wsCanonical, _fh) {
|
|
328
1062
|
const parentCanonical = await canonicalizeExistingAncestor(
|
|
329
|
-
|
|
1063
|
+
path2.dirname(target)
|
|
330
1064
|
);
|
|
331
|
-
const within = parentCanonical === wsCanonical || parentCanonical.startsWith(wsCanonical +
|
|
1065
|
+
const within = parentCanonical === wsCanonical || parentCanonical.startsWith(wsCanonical + path2.sep);
|
|
332
1066
|
if (within) return;
|
|
333
1067
|
throw new WorkspaceInitSymlinkError(
|
|
334
1068
|
target,
|
|
@@ -343,14 +1077,14 @@ var TimeoutError = class extends Error {
|
|
|
343
1077
|
}
|
|
344
1078
|
};
|
|
345
1079
|
async function withTimeout(promise, timeoutMs, label) {
|
|
346
|
-
return await new Promise((
|
|
1080
|
+
return await new Promise((resolve3, reject) => {
|
|
347
1081
|
const timeout = setTimeout(() => {
|
|
348
1082
|
reject(new TimeoutError(`${label} timed out after ${timeoutMs}ms`));
|
|
349
1083
|
}, timeoutMs);
|
|
350
1084
|
promise.then(
|
|
351
1085
|
(value) => {
|
|
352
1086
|
clearTimeout(timeout);
|
|
353
|
-
|
|
1087
|
+
resolve3(value);
|
|
354
1088
|
},
|
|
355
1089
|
(err) => {
|
|
356
1090
|
clearTimeout(timeout);
|
|
@@ -372,6 +1106,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
372
1106
|
persistDisabledSkills,
|
|
373
1107
|
persistSetting,
|
|
374
1108
|
persistSettings,
|
|
1109
|
+
skillInstallEnv,
|
|
375
1110
|
voiceEnv,
|
|
376
1111
|
voiceSettingsScope,
|
|
377
1112
|
preheatAcpChild: preheatAcpChildOnBridge,
|
|
@@ -411,6 +1146,23 @@ function createDaemonWorkspaceService(deps) {
|
|
|
411
1146
|
}
|
|
412
1147
|
return status;
|
|
413
1148
|
}, "getWorkspaceSkillsStatus");
|
|
1149
|
+
const refreshWorkspaceSkillsAfterMutation = /* @__PURE__ */ __name(async () => {
|
|
1150
|
+
lastWorkspaceSkillsStatus = void 0;
|
|
1151
|
+
workspaceSkillsStatusProvider?.invalidate?.(boundWorkspace);
|
|
1152
|
+
if (!(isChannelLive?.() ?? false)) return;
|
|
1153
|
+
try {
|
|
1154
|
+
await invokeWorkspaceCommand(
|
|
1155
|
+
SERVE_CONTROL_EXT_METHODS.workspaceSkillsRefresh,
|
|
1156
|
+
{ cwd: boundWorkspace }
|
|
1157
|
+
);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
if (!(err instanceof SessionNotFoundError) && !(err instanceof BridgeChannelClosedError)) {
|
|
1160
|
+
writeStderrLine(
|
|
1161
|
+
`qwen serve: workspace skill refresh after mutation failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}, "refreshWorkspaceSkillsAfterMutation");
|
|
414
1166
|
return {
|
|
415
1167
|
// -- Status queries (delegate to ACP child via queryWorkspaceStatus) --
|
|
416
1168
|
async getWorkspaceMcpStatus(_ctx) {
|
|
@@ -750,6 +1502,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
750
1502
|
let sessionsFailed = 0;
|
|
751
1503
|
if (persisted.changed) {
|
|
752
1504
|
lastWorkspaceSkillsStatus = void 0;
|
|
1505
|
+
workspaceSkillsStatusProvider?.invalidate?.(boundWorkspace);
|
|
753
1506
|
if (channelLive) {
|
|
754
1507
|
try {
|
|
755
1508
|
const refreshed = await invokeWorkspaceCommand(
|
|
@@ -790,18 +1543,51 @@ function createDaemonWorkspaceService(deps) {
|
|
|
790
1543
|
sessionsFailed
|
|
791
1544
|
};
|
|
792
1545
|
},
|
|
1546
|
+
async installWorkspaceSkill(_ctx, request) {
|
|
1547
|
+
const result = await installWorkspaceSkill(
|
|
1548
|
+
boundWorkspace,
|
|
1549
|
+
request,
|
|
1550
|
+
skillInstallEnv?.["GH_TOKEN"] ?? skillInstallEnv?.["GITHUB_TOKEN"]
|
|
1551
|
+
);
|
|
1552
|
+
await refreshWorkspaceSkillsAfterMutation();
|
|
1553
|
+
return result;
|
|
1554
|
+
},
|
|
1555
|
+
async deleteWorkspaceSkill(_ctx, requestedSkillName, scope) {
|
|
1556
|
+
const normalizedName = requestedSkillName.trim().toLowerCase();
|
|
1557
|
+
const status = await getWorkspaceSkillsStatus();
|
|
1558
|
+
const skill = status.skills.find(
|
|
1559
|
+
(candidate) => candidate.name.trim().toLowerCase() === normalizedName
|
|
1560
|
+
);
|
|
1561
|
+
if (!skill) throw new WorkspaceSkillNotFoundError(requestedSkillName);
|
|
1562
|
+
const expectedLevel = scope === "workspace" ? "project" : "user";
|
|
1563
|
+
if (skill.level !== expectedLevel || !skill.installedPath) {
|
|
1564
|
+
throw new WorkspaceSkillManagementError(
|
|
1565
|
+
"skill_not_managed",
|
|
1566
|
+
"Skill is not managed in the requested scope",
|
|
1567
|
+
409
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
const result = await deleteWorkspaceSkill(
|
|
1571
|
+
boundWorkspace,
|
|
1572
|
+
scope,
|
|
1573
|
+
skill.name,
|
|
1574
|
+
skill.installedPath
|
|
1575
|
+
);
|
|
1576
|
+
await refreshWorkspaceSkillsAfterMutation();
|
|
1577
|
+
return result;
|
|
1578
|
+
},
|
|
793
1579
|
async initWorkspace(ctx, opts) {
|
|
794
1580
|
const filename = contextFilename;
|
|
795
|
-
const target =
|
|
796
|
-
const withinWorkspace = target === boundWorkspace || target.startsWith(boundWorkspace +
|
|
1581
|
+
const target = path2.resolve(boundWorkspace, filename);
|
|
1582
|
+
const withinWorkspace = target === boundWorkspace || target.startsWith(boundWorkspace + path2.sep);
|
|
797
1583
|
if (!withinWorkspace) {
|
|
798
1584
|
throw new WorkspaceInitPathEscapeError(filename, boundWorkspace);
|
|
799
1585
|
}
|
|
800
|
-
const wsCanonical = await
|
|
1586
|
+
const wsCanonical = await fs2.realpath(boundWorkspace);
|
|
801
1587
|
const parentCanonical = await canonicalizeExistingAncestor(
|
|
802
|
-
|
|
1588
|
+
path2.dirname(target)
|
|
803
1589
|
);
|
|
804
|
-
const parentWithinWorkspace = parentCanonical === wsCanonical || parentCanonical.startsWith(wsCanonical +
|
|
1590
|
+
const parentWithinWorkspace = parentCanonical === wsCanonical || parentCanonical.startsWith(wsCanonical + path2.sep);
|
|
805
1591
|
if (!parentWithinWorkspace) {
|
|
806
1592
|
throw new WorkspaceInitSymlinkError(
|
|
807
1593
|
target,
|
|
@@ -810,7 +1596,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
810
1596
|
);
|
|
811
1597
|
}
|
|
812
1598
|
try {
|
|
813
|
-
const lst = await
|
|
1599
|
+
const lst = await fs2.lstat(target);
|
|
814
1600
|
if (lst.isSymbolicLink()) {
|
|
815
1601
|
throw new WorkspaceInitSymlinkError(
|
|
816
1602
|
target,
|
|
@@ -832,7 +1618,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
832
1618
|
}
|
|
833
1619
|
let action = "created";
|
|
834
1620
|
try {
|
|
835
|
-
const existing = await
|
|
1621
|
+
const existing = await fs2.readFile(target, "utf8");
|
|
836
1622
|
if (existing.trim().length > 0) {
|
|
837
1623
|
const existingSize = Buffer.byteLength(existing, "utf8");
|
|
838
1624
|
if (opts.force !== true) {
|
|
@@ -850,7 +1636,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
850
1636
|
if (action === "created") {
|
|
851
1637
|
let fh;
|
|
852
1638
|
try {
|
|
853
|
-
fh = await
|
|
1639
|
+
fh = await fs2.open(target, "wx");
|
|
854
1640
|
} catch (err) {
|
|
855
1641
|
const code = err?.code;
|
|
856
1642
|
if (code === "EEXIST") {
|
|
@@ -871,7 +1657,7 @@ function createDaemonWorkspaceService(deps) {
|
|
|
871
1657
|
} else if (action === "overwrote") {
|
|
872
1658
|
let overwriteFh;
|
|
873
1659
|
try {
|
|
874
|
-
overwriteFh = await
|
|
1660
|
+
overwriteFh = await fs2.open(
|
|
875
1661
|
target,
|
|
876
1662
|
fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0)
|
|
877
1663
|
);
|
|
@@ -1085,6 +1871,9 @@ export {
|
|
|
1085
1871
|
buildWorkspaceVoiceStatus,
|
|
1086
1872
|
validateWorkspaceVoiceState,
|
|
1087
1873
|
transcribeWorkspaceVoiceAudio,
|
|
1874
|
+
MAX_WORKSPACE_SKILL_NAME_LENGTH,
|
|
1875
|
+
WorkspaceSkillManagementError,
|
|
1876
|
+
validateWorkspaceSkillName,
|
|
1088
1877
|
createDaemonWorkspaceService
|
|
1089
1878
|
};
|
|
1090
1879
|
/**
|