@qwen-code/qwen-code 0.21.6 → 0.21.7
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/qc-helper/docs/configuration/settings.md +19 -9
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +1 -1
- package/bundled/qc-helper/docs/features/code-review.md +27 -0
- package/bundled/qc-helper/docs/qwen-serve.md +1 -1
- package/bundled/review/SKILL.md +48 -16
- package/chunks/{MaxSizedBox-2HFKP4LI.js → MaxSizedBox-ANWZBPTS.js} +38 -38
- package/chunks/{StandaloneSessionPicker-RBV72M5N.js → StandaloneSessionPicker-XKS3VU3A.js} +54 -53
- package/chunks/{acpAgent-WO6JFCVQ.js → acpAgent-KUAZ3GCZ.js} +80 -80
- package/chunks/{agent-AHGLYNZ4.js → agent-T6PTGX5R.js} +35 -35
- package/chunks/{agent-headless-NTXKKGRO.js → agent-headless-PR7YCB7S.js} +35 -35
- package/chunks/{anthropicContentGenerator-3CAHEZBM.js → anthropicContentGenerator-DTMVEL5M.js} +7 -7
- package/chunks/{artifact-tool-RROCCJDA.js → artifact-tool-U4VOZIK5.js} +2 -2
- package/chunks/{askUserQuestion-2EPPBPUZ.js → askUserQuestion-XOBQLPFA.js} +2 -2
- package/chunks/{bridge-RPAU64CA.js → bridge-NU6YEEHK.js} +39 -39
- package/chunks/{channel-management-service-IGEUOVRT.js → channel-management-service-FSQ7SHYI.js} +1 -1
- package/chunks/{channel-settings-store-ON64MF7G.js → channel-settings-store-35VQVUVV.js} +41 -41
- package/chunks/{chunk-XC656O42.js → chunk-22NBYCPD.js} +3 -3
- package/chunks/{chunk-T4JSA7KU.js → chunk-23RZCH76.js} +3 -3
- package/chunks/{chunk-PX5DNLSA.js → chunk-2U3WZGZG.js} +18 -10
- package/chunks/chunk-2VSTGY6M.js +107 -0
- package/chunks/{chunk-QZKYZCN3.js → chunk-33ACCIIY.js} +3 -3
- package/chunks/{chunk-MBEMTXQT.js → chunk-3K7FH2KQ.js} +1 -32
- package/chunks/{chunk-ODPXHGLL.js → chunk-3NB7TSE2.js} +2 -2
- package/chunks/{chunk-QYPT3QUD.js → chunk-3ULCB3NE.js} +3 -3
- package/chunks/{chunk-C4CBJQKY.js → chunk-55LYDVPU.js} +1 -1
- package/chunks/{chunk-NKSF7HYF.js → chunk-5KH532V7.js} +1 -1
- package/chunks/{chunk-BOGOR4HB.js → chunk-6EOI4HOK.js} +7 -7
- package/chunks/{chunk-CA4UIDFU.js → chunk-6GONZVLS.js} +1 -1
- package/chunks/{chunk-3UOLYNTF.js → chunk-6HVUFDS2.js} +6 -6
- package/chunks/{chunk-5L6CQ4ZU.js → chunk-6M26GBKX.js} +2 -2
- package/chunks/{chunk-XA4TMLFN.js → chunk-6UWFTUQL.js} +530 -388
- package/chunks/{chunk-W5DXUTXD.js → chunk-6XJMFJ4D.js} +8 -8
- package/chunks/{chunk-ACZRB7AD.js → chunk-6XU3PH66.js} +3 -3
- package/chunks/{chunk-NGJOAKJY.js → chunk-72LNFGUY.js} +3 -3
- package/chunks/{chunk-33FNCQSY.js → chunk-7MH4J33A.js} +2 -2
- package/chunks/{chunk-2AGOQ7QO.js → chunk-7UFKAV7Y.js} +118 -6
- package/chunks/{chunk-O7V7N4G7.js → chunk-AE77C5ZU.js} +198 -35
- package/chunks/{chunk-JELIWHXD.js → chunk-AOHJKPKL.js} +1 -1
- package/chunks/{chunk-GLNBDYUX.js → chunk-AXMWHKXA.js} +6 -2
- package/chunks/{chunk-H36SETQS.js → chunk-B2GJNDTV.js} +1 -1
- package/chunks/{chunk-QMBEW7EW.js → chunk-BGG4MBYD.js} +2 -2
- package/chunks/{chunk-6WBWIPBF.js → chunk-BVWVWGLE.js} +2 -2
- package/chunks/{chunk-GK2H5GRC.js → chunk-CB2464BK.js} +5 -5
- package/chunks/{chunk-2UISUFHQ.js → chunk-CJXIIILK.js} +3 -3
- package/chunks/{chunk-WK4TJUEA.js → chunk-CKASRHZI.js} +1 -1
- package/chunks/{chunk-Z7JILXGX.js → chunk-CNIID3TV.js} +3 -3
- package/chunks/{chunk-5MJGUQU5.js → chunk-CSZEXZBE.js} +3 -3
- package/chunks/{chunk-KKN2IE7Q.js → chunk-CTJLZLB5.js} +1 -1
- package/chunks/{chunk-HY25ULVN.js → chunk-CXE7E23H.js} +131 -48
- package/chunks/{chunk-4WTAQEHV.js → chunk-CYR3HI5X.js} +3 -3
- package/chunks/{chunk-KHUA3NQV.js → chunk-DBOFL3LQ.js} +2 -2
- package/chunks/{chunk-4LKVQFHM.js → chunk-DREZZAZM.js} +2 -2
- package/chunks/{chunk-SQC6K5HI.js → chunk-E33LOKRA.js} +1 -1
- package/chunks/{chunk-2DNKEBXY.js → chunk-EI3HZX6C.js} +1 -1
- package/chunks/{chunk-U25CMJYY.js → chunk-EKPKKXY2.js} +1 -1
- package/chunks/{chunk-RHTTKJTP.js → chunk-EORNAQDT.js} +1 -1
- package/chunks/{chunk-432XCUHF.js → chunk-EQL65XGA.js} +1 -1
- package/chunks/{chunk-TYERNYH4.js → chunk-EUNWC2VS.js} +2 -2
- package/chunks/{chunk-OWSW7EYP.js → chunk-EUQTXRKZ.js} +6 -6
- package/chunks/{chunk-UH7Z7Y45.js → chunk-FOGU3LJX.js} +1 -1
- package/chunks/{chunk-536SWZTA.js → chunk-FPM27XN4.js} +4 -4
- package/chunks/{chunk-E4WHKETF.js → chunk-FVKHVJZQ.js} +2 -49
- package/chunks/{chunk-YMBOEAF4.js → chunk-FZBZSYKU.js} +1 -1
- package/chunks/{chunk-YZQTA3ID.js → chunk-GDXET5XU.js} +31 -3396
- package/chunks/{chunk-A5YZ3FA2.js → chunk-GM463R2B.js} +1 -1
- package/chunks/{chunk-MOHAWIAW.js → chunk-GNPNYXJB.js} +2 -2
- package/chunks/{chunk-2POQWYM6.js → chunk-GSS34377.js} +3 -3
- package/chunks/{chunk-CJ5WLD5Y.js → chunk-H2UQ2M5Z.js} +2 -2
- package/chunks/{chunk-SUKEUUAA.js → chunk-HQB2KUAN.js} +1 -1
- package/chunks/{chunk-JE44IPH6.js → chunk-HYRDKNUY.js} +2 -2
- package/chunks/{chunk-D7D7RXIJ.js → chunk-IC4Z6LX6.js} +1 -1
- package/chunks/{chunk-HU5TUALO.js → chunk-IIJEYBNH.js} +3 -3
- package/chunks/{chunk-EMYZIABH.js → chunk-IUFV6FYU.js} +1 -1
- package/chunks/{chunk-4S4OEQ6U.js → chunk-J6SZID63.js} +2 -2
- package/chunks/{chunk-BFHXDAR7.js → chunk-KKBPOU75.js} +1 -1
- package/chunks/{chunk-XM2O73NC.js → chunk-KKJJB5X6.js} +2455 -1314
- package/chunks/{chunk-SLSWFHDF.js → chunk-KR4ZSC7K.js} +4 -4
- package/chunks/{chunk-RFUHDCNF.js → chunk-KXSDEOFU.js} +1 -1
- package/chunks/{chunk-MGSSJT27.js → chunk-L4N6TIP7.js} +5 -3
- package/chunks/{chunk-OP2OHZ7K.js → chunk-LA7YLSR6.js} +6 -6
- package/chunks/{chunk-EXCEQD22.js → chunk-LEMXGVEP.js} +4 -4
- package/chunks/{chunk-WVAESIM6.js → chunk-LZC3B5N2.js} +4 -4
- package/chunks/{chunk-6O5DVH27.js → chunk-M6HMMKVG.js} +1 -1
- package/chunks/{chunk-75QMRMO7.js → chunk-MAJS7AT4.js} +3 -3
- package/chunks/{chunk-T5U6IM4C.js → chunk-MVUQRLOH.js} +3 -3
- package/chunks/{chunk-GLWP3FDB.js → chunk-MZSJ43WV.js} +31 -2
- package/chunks/{chunk-ZPPP6VCL.js → chunk-NG2R4N5O.js} +1 -1
- package/chunks/{chunk-UGTWD35F.js → chunk-NLREQJFK.js} +1 -1
- package/chunks/{chunk-N2GJU6KI.js → chunk-OFI7IGST.js} +12 -12
- package/chunks/{chunk-VGFPFM4U.js → chunk-OH2VKQXH.js} +42 -16
- package/chunks/{chunk-G5NNWEYE.js → chunk-OHJLEW5J.js} +4 -4
- package/chunks/{chunk-3RXUYQJI.js → chunk-OIVKILPI.js} +49 -0
- package/chunks/{chunk-VNQCPBC2.js → chunk-OMKQH5C5.js} +3 -3
- package/chunks/{chunk-LBWNSLJK.js → chunk-ONSZH6VM.js} +2 -2
- package/chunks/{chunk-DYA25Y7U.js → chunk-OWMAZHIX.js} +1 -1
- package/chunks/{chunk-4JH77A2U.js → chunk-P2AJ7FTI.js} +1 -1
- package/chunks/{chunk-WNU7TZTO.js → chunk-P3LEG54R.js} +3 -3
- package/chunks/{chunk-VSALQLT7.js → chunk-PJOBIWUG.js} +1 -1
- package/chunks/{chunk-PPUOYL77.js → chunk-PQBHU2IA.js} +49 -18
- package/chunks/{chunk-WO2B5E5B.js → chunk-Q3JQ7OSU.js} +1 -1
- package/chunks/{chunk-IMOQO6YL.js → chunk-QETYKPMG.js} +1 -1
- package/chunks/{chunk-DVBEGGLG.js → chunk-RERATVSK.js} +10 -9
- package/chunks/chunk-RGSBTHSU.js +3381 -0
- package/chunks/{chunk-DIT4V3XH.js → chunk-RMH5OWLD.js} +1 -1
- package/chunks/{chunk-TOGZTG4X.js → chunk-RNVQB725.js} +2 -2
- package/chunks/{chunk-GESMMPAK.js → chunk-RUL4S3K3.js} +79 -59
- package/chunks/{chunk-3LZKY4V4.js → chunk-RZHU3ILK.js} +5 -5
- package/chunks/{chunk-SKNE3KQC.js → chunk-SCI7K3GM.js} +4 -4
- package/chunks/{chunk-YAVY33G4.js → chunk-SEZ556DZ.js} +1 -1
- package/chunks/{chunk-Q5RM32DR.js → chunk-SHZYHGQH.js} +1 -1
- package/chunks/chunk-SOORFWKY.js +155 -0
- package/chunks/{chunk-K2B3X3E7.js → chunk-SPMY6CX6.js} +4 -4
- package/chunks/{chunk-FZFP7RZI.js → chunk-TDXGKHDN.js} +3 -3
- package/chunks/{chunk-R5RBXLNC.js → chunk-TLV2IMHR.js} +5 -5
- package/chunks/{chunk-U4AF3QLU.js → chunk-TRLYP7OF.js} +8 -8
- package/chunks/{chunk-ULPQQSDN.js → chunk-TYKC75AX.js} +3 -3
- package/chunks/{chunk-KVUBYJDM.js → chunk-TZEZYNYD.js} +1 -1
- package/chunks/{chunk-XC2VAFVZ.js → chunk-UVX4Y6IN.js} +1 -1
- package/chunks/{chunk-RK2EE3T7.js → chunk-VOARQPTA.js} +2 -2
- package/chunks/{chunk-TGGEZINK.js → chunk-W4B7UBUS.js} +7 -7
- package/chunks/{chunk-URN76TKR.js → chunk-WCG5O7IP.js} +2 -2
- package/chunks/{chunk-TSERKWCX.js → chunk-XCWO3SMQ.js} +3 -3
- package/chunks/{chunk-OXX3FG2N.js → chunk-Y4EWSQF7.js} +3 -3
- package/chunks/{chunk-3PRJYMXC.js → chunk-Y4NGLFGJ.js} +1 -1
- package/chunks/{chunk-A4VTILZT.js → chunk-Y7E5LNLO.js} +115 -85
- package/chunks/{chunk-3LGZEYOF.js → chunk-YHEEE624.js} +2 -2
- package/chunks/{chunk-SQOMTWQT.js → chunk-YJQCUTT2.js} +4 -4
- package/chunks/{chunk-GWRHVJY2.js → chunk-ZJ3VGYHK.js} +20 -20
- package/chunks/{chunk-LAAFLUQV.js → chunk-ZXU5UHMI.js} +1 -1
- package/chunks/{computer-use-5HXJ2P4X.js → computer-use-KEAPATUY.js} +35 -35
- package/chunks/{config-utils-VXD4SPVR.js → config-utils-K7SCOWHD.js} +2 -2
- package/chunks/{contextCommand-7BEHRX2H.js → contextCommand-ZJA6A5K5.js} +37 -37
- package/chunks/{core-runtime-U4VJ5TIV.js → core-runtime-QMJPWHCI.js} +35 -35
- package/chunks/{create-sub-session-QZNQY332.js → create-sub-session-DJE6XICH.js} +35 -35
- package/chunks/{create-sub-session-6EWLAZD5.js → create-sub-session-E4PBYXV5.js} +2 -2
- package/chunks/{cron-create-CCRIULJE.js → cron-create-GRBWYMIJ.js} +4 -4
- package/chunks/{cron-delete-FCFW6BGP.js → cron-delete-4CMKBR2M.js} +4 -4
- package/chunks/{cron-list-QDBVCHRF.js → cron-list-W7K4ASOA.js} +4 -4
- package/chunks/{daemon-YI5RY62O.js → daemon-SCTQMM7Z.js} +50 -2
- package/chunks/{daemon-status-provider-UJUGTDTL.js → daemon-status-provider-FSWDPE2G.js} +43 -43
- package/chunks/{daemon-trust-policy-ZHDD5VYQ.js → daemon-trust-policy-P5L5VFAY.js} +40 -40
- package/chunks/{daemon-trust-policy-monitor-5SHYBDGA.js → daemon-trust-policy-monitor-73JYN6ET.js} +40 -40
- package/chunks/{deferred-core-runtime-QWAEBTR5.js → deferred-core-runtime-BWRCCFTE.js} +35 -35
- package/chunks/{display-image-GUJA352B.js → display-image-O2XFPQTC.js} +4 -4
- package/chunks/{dist-A7H2KKUC.js → dist-K4WWQBHS.js} +277 -98
- package/chunks/{earlyInputCapture-5BMSGKOI.js → earlyInputCapture-L5ST23A6.js} +36 -36
- package/chunks/{edit-IVQQVYYK.js → edit-LHHRFNFK.js} +35 -35
- package/chunks/{enter-worktree-P3DF2N6O.js → enter-worktree-6NXCRZPF.js} +35 -35
- package/chunks/{enterPlanMode-KKTDVLAD.js → enterPlanMode-ZVQT4U7D.js} +35 -35
- package/chunks/{environment-W5TRDM3G.js → environment-GJ2HWJOS.js} +37 -37
- package/chunks/{errors-GJJB36IG.js → errors-6RLH3XXC.js} +37 -37
- package/chunks/{exit-worktree-LV5RLOCU.js → exit-worktree-I2YLUHHY.js} +35 -35
- package/chunks/{exitPlanMode-TUGOB2LL.js → exitPlanMode-7CCWKO7I.js} +35 -35
- package/chunks/{fast-path-2FU23PAQ.js → fast-path-DVFD4HQD.js} +2 -2
- package/chunks/{gemini-KDULDQPP.js → gemini-7FWGOYD4.js} +72 -72
- package/chunks/{geminiContentGenerator-LOCFHNVA.js → geminiContentGenerator-V2WVLIXU.js} +6 -6
- package/chunks/{glob-Z4NQFKWS.js → glob-3N6GGI75.js} +35 -35
- package/chunks/{goal-tools-3AZKX5WQ.js → goal-tools-APOW6UXL.js} +3 -3
- package/chunks/{grep-Z67Z7EN6.js → grep-V43ILZVX.js} +35 -35
- package/chunks/{handleAutoUpdate-DFUZE6I6.js → handleAutoUpdate-A5WFVWIA.js} +39 -39
- package/chunks/{i18n-R4FMZ7N2.js → i18n-PLVQ4FBZ.js} +36 -36
- package/chunks/{image-gen-4CQJPQP5.js → image-gen-7C6IK6DC.js} +9 -9
- package/chunks/{initializer-DK672ZVE.js → initializer-CVAIKZYL.js} +41 -41
- package/chunks/{installationInfo-PO4Z3ES7.js → installationInfo-J6QDHNTK.js} +36 -36
- package/chunks/{keychain-token-storage-7BT4TQ2A.js → keychain-token-storage-TVAMTNP7.js} +2 -2
- package/chunks/{list-QXQF6C53.js → list-ZZ4OUUWV.js} +43 -43
- package/chunks/{list-agents-GC4BRET7.js → list-agents-KFFIIS2G.js} +2 -2
- package/chunks/{loadedSettingsAdapter-4D7GUGW6.js → loadedSettingsAdapter-QRYRJSWL.js} +40 -40
- package/chunks/{loggingContentGenerator-2P3MNACM.js → loggingContentGenerator-OJCO75HM.js} +17 -17
- package/chunks/{loop-wakeup-SQQSBMEY.js → loop-wakeup-TLMJPMMP.js} +5 -5
- package/chunks/{ls-63CC3NNX.js → ls-UVVE2XGE.js} +4 -4
- package/chunks/{lsp-P6NVJVNT.js → lsp-QMEA2VLB.js} +2 -2
- package/chunks/{managed-npm-update-LWVJQTWO.js → managed-npm-update-PCOSCP2N.js} +36 -36
- package/chunks/{mcp-GHETAR6V.js → mcp-M3WKKWKY.js} +40 -40
- package/chunks/{monitor-4T7W4M6Q.js → monitor-GN24FH6C.js} +35 -35
- package/chunks/{nonInteractiveCli-P4UKCJFM.js → nonInteractiveCli-KPTU3VSQ.js} +72 -72
- package/chunks/{notebook-edit-OC2V7U5D.js → notebook-edit-4YM65BBO.js} +35 -35
- package/chunks/{openaiContentGenerator-RAJUFWQY.js → openaiContentGenerator-EDQT6GTV.js} +19 -19
- package/chunks/{pidfile-HVPYVRLS.js → pidfile-JOIIRN2C.js} +36 -36
- package/chunks/{processUtils-VIQ4JHVX.js → processUtils-5SV4MX3Q.js} +2 -2
- package/chunks/{qwenContentGenerator-2QLCUFNZ.js → qwenContentGenerator-ODRJSUGR.js} +40 -40
- package/chunks/{qwenOAuth2-J2X3EN43.js → qwenOAuth2-NESCMZ47.js} +8 -8
- package/chunks/{read-file-ZNAN2B26.js → read-file-HPSODLPK.js} +10 -10
- package/chunks/{read-mcp-resource-RTSSEER5.js → read-mcp-resource-2TNSJTC3.js} +2 -2
- package/chunks/{record-artifact-E6AWQU2Z.js → record-artifact-RAON52FC.js} +3 -3
- package/chunks/{resumeHistoryUtils-RCY7AL5Y.js → resumeHistoryUtils-TXCOFFMQ.js} +41 -40
- package/chunks/{ripGrep-C3KMLZM5.js → ripGrep-ZKQGTQAO.js} +35 -35
- package/chunks/{run-qwen-serve-3JN75CIQ.js → run-qwen-serve-ZPKPKECF.js} +65 -43
- package/chunks/{runtime-VUA4WQYD.js → runtime-2FLNNM36.js} +43 -43
- package/chunks/{scheduler-Z4IBXCOY.js → scheduler-365PJHNR.js} +35 -35
- package/chunks/{sdk-exporters-http-IL2346MJ.js → sdk-exporters-http-KHAB5JSD.js} +2 -2
- package/chunks/{sdk-impl-SYKGVWOZ.js → sdk-impl-KD7FSTC3.js} +2 -2
- package/chunks/{send-message-TL6FIMMN.js → send-message-ZM7MRUGP.js} +5 -5
- package/chunks/{serve-67T3P7XI.js → serve-QD2RNLKJ.js} +41 -41
- package/chunks/{server-46ZO2CLV.js → server-XLJVLTYM.js} +621 -332
- package/chunks/{session-B4IFAFC4.js → session-3VTUHH4Q.js} +73 -73
- package/chunks/{settings-AI2BTBOZ.js → settings-KULVPCQC.js} +39 -39
- package/chunks/{shell-BKII3B6E.js → shell-3SWW22MH.js} +35 -35
- package/chunks/{skill-6CFOWVGA.js → skill-KDHWYS7W.js} +14 -14
- package/chunks/{skill-settings-6BFQWMW4.js → skill-settings-NMY4JEJC.js} +39 -39
- package/chunks/{spawnChannel-FJXW35U7.js → spawnChannel-ZKS4Q5OU.js} +37 -37
- package/chunks/{standalone-update-WVINHRNS.js → standalone-update-7CXPGAQ4.js} +37 -37
- package/chunks/{startInteractiveUI-YE57CJ57.js → startInteractiveUI-2QQLEKM6.js} +399 -159
- package/chunks/{syntheticOutput-BN7E626U.js → syntheticOutput-ZLCUTKO2.js} +3 -3
- package/chunks/{task-create-Q727MXHJ.js → task-create-CQP5UEA3.js} +9 -9
- package/chunks/{task-list-G6KPQ5JT.js → task-list-XLEZOJXP.js} +5 -5
- package/chunks/{task-stop-TNXRNAL2.js → task-stop-7ZQM5CFF.js} +2 -2
- package/chunks/{task-update-YB7PXO4R.js → task-update-JA75U2SN.js} +9 -9
- package/chunks/{team-create-GNC7NFY6.js → team-create-2WTNGAOX.js} +35 -35
- package/chunks/{team-delete-GDO7H2T4.js → team-delete-MHY3UZGY.js} +5 -5
- package/chunks/{team-plan-approval-ECALJLIO.js → team-plan-approval-LZTYLEG4.js} +35 -35
- package/chunks/{terminal-image-renderer-II74TFVW.js → terminal-image-renderer-R5HHAFJN.js} +41 -36
- package/chunks/{theme-manager-2MGEDD22.js → theme-manager-PAUCSJFF.js} +36 -36
- package/chunks/{todoWrite-MHWHO5UO.js → todoWrite-UPVOHYGD.js} +4 -4
- package/chunks/{tool-search-S6LRRACA.js → tool-search-2BFCGLI6.js} +13 -13
- package/chunks/{total-session-admission-7WVAWQPU.js → total-session-admission-MK4SINFK.js} +39 -39
- package/chunks/{trustedFolders-NFS4KHO4.js → trustedFolders-TJ5S26N7.js} +36 -36
- package/chunks/{update-relaunch-Q5HLZ6UW.js → update-relaunch-N7AYLARF.js} +5 -5
- package/chunks/{updateCheck-UOVZSTP7.js → updateCheck-QRZ3DVT5.js} +38 -38
- package/chunks/{useAutoAcceptIndicator-PC6MOJCO.js → useAutoAcceptIndicator-DBJ7Y5AF.js} +42 -42
- package/chunks/{validateNonInterActiveAuth-YMQP3ZM7.js → validateNonInterActiveAuth-6ZQWUDS3.js} +69 -69
- package/chunks/{version-T4AQFZJH.js → version-AJQGQWXC.js} +1 -1
- package/chunks/{web-fetch-JQCJHEEX.js → web-fetch-QWJYRXFL.js} +13 -13
- package/chunks/{web-search-QGZ2KY4G.js → web-search-2AOXJGHU.js} +9 -9
- package/chunks/web-shell-static-O73YPVVC.js +33 -0
- package/chunks/{workflow-YIT27YWZ.js → workflow-ERH3SJG2.js} +36 -36
- package/chunks/{workspace-providers-status-FIDZQ2PL.js → workspace-providers-status-HZ474RA4.js} +43 -43
- package/chunks/{workspace-registration-store-RCSPE4DU.js → workspace-registration-store-6EUP4JXG.js} +1 -1
- package/chunks/{workspace-registry-5PEUCY6E.js → workspace-registry-R4APRRNC.js} +39 -39
- package/chunks/{workspace-service-LANDVPL7.js → workspace-service-HFIFDMU7.js} +45 -45
- package/chunks/{workspace-skills-status-2IQVJNIN.js → workspace-skills-status-HAHEIHGK.js} +41 -41
- package/chunks/{workspace-trust-reconciler-CZPZZCZC.js → workspace-trust-reconciler-65GTAGDK.js} +45 -45
- package/chunks/{write-file-G5VNGCOG.js → write-file-B34CDELV.js} +35 -35
- package/chunks/{zoom-image-6RBLD7X6.js → zoom-image-HPRBZHSS.js} +10 -10
- package/cli.js +12 -12
- package/package.json +3 -3
- package/web-shell/assets/{arc-B-Vtkp1W.js → arc-Ip-FFDJ0.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-qmTXIj64.js → architectureDiagram-3BPJPVTR-CChqgwIb.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-ZlnXEtdP.js → blockDiagram-GPEHLZMM-BHN-Tfss.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-Wyoirf6Z.js → c4Diagram-AAUBKEIU-BhyjzDjN.js} +1 -1
- package/web-shell/assets/channel-Cu8qd6OB.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-WQwbq9d8.js → chunk-2J33WTMH-BB9eyHrx.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DQKnAXC4.js → chunk-4BX2VUAB-BeV7ce_J.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DIdgXNcO.js → chunk-55IACEB6-DO7kXDiV.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-CUwGOLEd.js → chunk-727SXJPM-B27MPznD.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-CCBGZFZ4.js → chunk-AQP2D5EJ-DHIjqjD_.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-DMGXProk.js → chunk-FMBD7UC4-DabMqyN2.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-yejcFqsh.js → chunk-ND2GUHAM-Dw7Nnnxc.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-DxCyqTf7.js → chunk-QZHKN3VN-DdaZew1Q.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BI3KrXhq.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BI3KrXhq.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-DOrF3cg_.js → cose-bilkent-S5V4N54A-CgBpCYH5.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-Be6CdYQ_.js → dagre-BM42HDAG-BB4IU8G0.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-B_Rjsft2.js → diagram-2AECGRRQ-BcUTHU37.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-BvQbKSm1.js → diagram-5GNKFQAL-B6Lyihyv.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-BsdmMdpS.js → diagram-KO2AKTUF-CRIJOjpG.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-CrQlBaNr.js → diagram-LMA3HP47-DdNiwWWT.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-aB1MD_DL.js → diagram-OG6HWLK6-Dz4b32Do.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-F-eUUn9n.js → erDiagram-TEJ5UH35-D-8NGw77.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-DY_d8lUK.js → flowDiagram-I6XJVG4X-Dun26LyM.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-Dqu7KBPg.js → ganttDiagram-6RSMTGT7-F10Hu1Gc.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-2IkU_VEj.js → gitGraphDiagram-PVQCEYII-Bf8Ez6-D.js} +1 -1
- package/web-shell/assets/{index-BuSbqggh.js → index-BXqw-nGq.js} +1 -1
- package/web-shell/assets/{index-DrfC2e9s.js → index-hcgjV7vS.js} +333 -333
- package/web-shell/assets/{infoDiagram-5YYISTIA-C4orwzPw.js → infoDiagram-5YYISTIA-DVgTMLvj.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-fXUEfef_.js → ishikawaDiagram-YF4QCWOH-uT-KepvK.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-Bs0I1_J5.js → journeyDiagram-JHISSGLW-Vg_XadQf.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-BPRj1dw_.js → kanban-definition-UN3LZRKU-Dyv1ESBE.js} +1 -1
- package/web-shell/assets/{linear-DFS6ZDpW.js → linear-BMIepYX3.js} +1 -1
- package/web-shell/assets/{mermaid.core-CZxjWMaO.js → mermaid.core-zA8pJ9ag.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-Dsr3PmNs.js → mindmap-definition-RKZ34NQL-BCW_BZ_5.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-D5Ngpz5Y.js → pieDiagram-4H26LBE5-DseNEGp2.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-WJK9I25z.js → quadrantDiagram-W4KKPZXB-6Z7wr1Fi.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-CmrYcwOH.js → requirementDiagram-4Y6WPE33-CVOlCRPf.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-7SjCc4Mv.js → sankeyDiagram-5OEKKPKP-CGhgrUJZ.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Dl9E0IRM.js → sequenceDiagram-3UESZ5HK-dlqmpLuQ.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CRRio7yV.js → stateDiagram-AJRCARHV-B9EW10Xb.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CJTZJJ5X.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-DVyXjjfq.js → timeline-definition-PNZ67QCA-BBRh0mgZ.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-Nb2Lckzq.js → vennDiagram-CIIHVFJN-B8Gs-REP.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-DuL_otIP.js → wardley-L42UT6IY-D_y80Ur7.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DVI9Q4Aa.js → wardleyDiagram-YWT4CUSO-D-CNfHJ-.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-BNjAm7KD.js → xychartDiagram-2RQKCTM6-DQ5CvELo.js} +1 -1
- package/web-shell/index.html +1 -1
- package/web-shell/assets/channel-DXaHnfjS.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BigU82A0.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BigU82A0.js +0 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-1atT7XaU.js +0 -1
|
@@ -1,35 +1,17 @@
|
|
|
1
1
|
// Force strict mode and setup for ESM
|
|
2
2
|
"use strict";
|
|
3
|
-
import {
|
|
4
|
-
isLoopbackBind
|
|
5
|
-
} from "./chunk-RXFQM6FQ.js";
|
|
6
|
-
import {
|
|
7
|
-
MAX_CHILD_HEAP_MB,
|
|
8
|
-
MIN_CHILD_HEAP_MB,
|
|
9
|
-
recommendedChildShareMb
|
|
10
|
-
} from "./chunk-HGCQ5SE7.js";
|
|
11
3
|
import {
|
|
12
4
|
require_mime_db
|
|
13
5
|
} from "./chunk-K6KS3LTN.js";
|
|
14
6
|
import {
|
|
15
7
|
require_lib
|
|
16
8
|
} from "./chunk-FC4KQFCS.js";
|
|
17
|
-
import {
|
|
18
|
-
CLIENT_MCP_OVER_WS_CONFIG_FLAG
|
|
19
|
-
} from "./chunk-R574ATRM.js";
|
|
20
|
-
import {
|
|
21
|
-
MAX_WORKSPACE_PATH_LENGTH,
|
|
22
|
-
translateAndCheckAbsoluteWorkspacePath
|
|
23
|
-
} from "./chunk-6PLDPT2C.js";
|
|
24
9
|
import {
|
|
25
10
|
require_content_type
|
|
26
11
|
} from "./chunk-GOFAQQZA.js";
|
|
27
12
|
import {
|
|
28
13
|
require_safer
|
|
29
14
|
} from "./chunk-XWRJCPHC.js";
|
|
30
|
-
import {
|
|
31
|
-
writeStderrLine
|
|
32
|
-
} from "./chunk-KGJGEEVR.js";
|
|
33
15
|
import {
|
|
34
16
|
require_src
|
|
35
17
|
} from "./chunk-DMTGGOSA.js";
|
|
@@ -49,7 +31,7 @@ import {
|
|
|
49
31
|
var require_depd = __commonJS({
|
|
50
32
|
"node_modules/depd/index.js"(exports, module) {
|
|
51
33
|
init_esbuild_shims();
|
|
52
|
-
var
|
|
34
|
+
var relative = __require("path").relative;
|
|
53
35
|
module.exports = depd;
|
|
54
36
|
var basePath = process.cwd();
|
|
55
37
|
function containsNamespace(str, namespace) {
|
|
@@ -255,7 +237,7 @@ var require_depd = __commonJS({
|
|
|
255
237
|
}
|
|
256
238
|
__name(formatColor, "formatColor");
|
|
257
239
|
function formatLocation(callSite) {
|
|
258
|
-
return
|
|
240
|
+
return relative(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
|
|
259
241
|
}
|
|
260
242
|
__name(formatLocation, "formatLocation");
|
|
261
243
|
function getStack() {
|
|
@@ -4739,10 +4721,10 @@ var require_raw_body = __commonJS({
|
|
|
4739
4721
|
if (done) {
|
|
4740
4722
|
return readStream(stream, encoding, length, limit, wrap(done));
|
|
4741
4723
|
}
|
|
4742
|
-
return new Promise(/* @__PURE__ */ __name(function executor(
|
|
4724
|
+
return new Promise(/* @__PURE__ */ __name(function executor(resolve, reject) {
|
|
4743
4725
|
readStream(stream, encoding, length, limit, /* @__PURE__ */ __name(function onRead(err, buf) {
|
|
4744
4726
|
if (err) return reject(err);
|
|
4745
|
-
|
|
4727
|
+
resolve(buf);
|
|
4746
4728
|
}, "onRead"));
|
|
4747
4729
|
}, "executor"));
|
|
4748
4730
|
}
|
|
@@ -10243,11 +10225,11 @@ var require_view = __commonJS({
|
|
|
10243
10225
|
var debug = require_src()("express:view");
|
|
10244
10226
|
var path2 = __require("node:path");
|
|
10245
10227
|
var fs = __require("node:fs");
|
|
10246
|
-
var
|
|
10247
|
-
var
|
|
10228
|
+
var dirname2 = path2.dirname;
|
|
10229
|
+
var basename2 = path2.basename;
|
|
10248
10230
|
var extname = path2.extname;
|
|
10249
|
-
var
|
|
10250
|
-
var
|
|
10231
|
+
var join2 = path2.join;
|
|
10232
|
+
var resolve = path2.resolve;
|
|
10251
10233
|
module.exports = View;
|
|
10252
10234
|
function View(name, options) {
|
|
10253
10235
|
var opts = options || {};
|
|
@@ -10282,9 +10264,9 @@ var require_view = __commonJS({
|
|
|
10282
10264
|
debug('lookup "%s"', name);
|
|
10283
10265
|
for (var i = 0; i < roots.length && !path3; i++) {
|
|
10284
10266
|
var root = roots[i];
|
|
10285
|
-
var loc =
|
|
10286
|
-
var dir =
|
|
10287
|
-
var file =
|
|
10267
|
+
var loc = resolve(root, name);
|
|
10268
|
+
var dir = dirname2(loc);
|
|
10269
|
+
var file = basename2(loc);
|
|
10288
10270
|
path3 = this.resolve(dir, file);
|
|
10289
10271
|
}
|
|
10290
10272
|
return path3;
|
|
@@ -10307,14 +10289,14 @@ var require_view = __commonJS({
|
|
|
10307
10289
|
}, "onRender"));
|
|
10308
10290
|
sync = false;
|
|
10309
10291
|
}, "render");
|
|
10310
|
-
View.prototype.resolve = /* @__PURE__ */ __name(function
|
|
10292
|
+
View.prototype.resolve = /* @__PURE__ */ __name(function resolve2(dir, file) {
|
|
10311
10293
|
var ext = this.ext;
|
|
10312
|
-
var path3 =
|
|
10294
|
+
var path3 = join2(dir, file);
|
|
10313
10295
|
var stat = tryStat(path3);
|
|
10314
10296
|
if (stat && stat.isFile()) {
|
|
10315
10297
|
return path3;
|
|
10316
10298
|
}
|
|
10317
|
-
path3 =
|
|
10299
|
+
path3 = join2(dir, basename2(file, ext), "index" + ext);
|
|
10318
10300
|
stat = tryStat(path3);
|
|
10319
10301
|
if (stat && stat.isFile()) {
|
|
10320
10302
|
return path3;
|
|
@@ -12551,7 +12533,7 @@ var require_application = __commonJS({
|
|
|
12551
12533
|
var compileETag = require_utils2().compileETag;
|
|
12552
12534
|
var compileQueryParser = require_utils2().compileQueryParser;
|
|
12553
12535
|
var compileTrust = require_utils2().compileTrust;
|
|
12554
|
-
var
|
|
12536
|
+
var resolve = __require("node:path").resolve;
|
|
12555
12537
|
var once = require_once();
|
|
12556
12538
|
var Router = require_router();
|
|
12557
12539
|
var slice = Array.prototype.slice;
|
|
@@ -12605,7 +12587,7 @@ var require_application = __commonJS({
|
|
|
12605
12587
|
this.mountpath = "/";
|
|
12606
12588
|
this.locals.settings = this.settings;
|
|
12607
12589
|
this.set("view", View);
|
|
12608
|
-
this.set("views",
|
|
12590
|
+
this.set("views", resolve("views"));
|
|
12609
12591
|
this.set("jsonp callback name", "callback");
|
|
12610
12592
|
if (env === "production") {
|
|
12611
12593
|
this.enable("view cache");
|
|
@@ -13797,9 +13779,9 @@ var require_content_disposition = __commonJS({
|
|
|
13797
13779
|
if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
|
|
13798
13780
|
throw new TypeError("fallback must be ISO-8859-1 string");
|
|
13799
13781
|
}
|
|
13800
|
-
var name =
|
|
13782
|
+
var name = basename2(filename);
|
|
13801
13783
|
var isQuotedString = TEXT_REGEXP.test(name);
|
|
13802
|
-
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) :
|
|
13784
|
+
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename2(fallback);
|
|
13803
13785
|
var hasFallback = typeof fallbackName === "string" && fallbackName !== name;
|
|
13804
13786
|
if (hasFallback || !isQuotedString || hasHexEscape(name)) {
|
|
13805
13787
|
params["filename*"] = name;
|
|
@@ -13928,7 +13910,7 @@ var require_content_disposition = __commonJS({
|
|
|
13928
13910
|
this.parameters = parameters;
|
|
13929
13911
|
}
|
|
13930
13912
|
__name(ContentDisposition, "ContentDisposition");
|
|
13931
|
-
function
|
|
13913
|
+
function basename2(path2) {
|
|
13932
13914
|
const normalized = path2.replaceAll("\\", "/");
|
|
13933
13915
|
let end = normalized.length;
|
|
13934
13916
|
while (end > 0 && normalized[end - 1] === "/") {
|
|
@@ -13943,7 +13925,7 @@ var require_content_disposition = __commonJS({
|
|
|
13943
13925
|
}
|
|
13944
13926
|
return normalized.slice(start + 1, end);
|
|
13945
13927
|
}
|
|
13946
|
-
__name(
|
|
13928
|
+
__name(basename2, "basename");
|
|
13947
13929
|
function isHexDigit(char) {
|
|
13948
13930
|
const code = char.charCodeAt(0);
|
|
13949
13931
|
return code >= 48 && code <= 57 || // 0-9
|
|
@@ -14194,9 +14176,9 @@ var require_send = __commonJS({
|
|
|
14194
14176
|
var Stream = __require("stream");
|
|
14195
14177
|
var util = __require("util");
|
|
14196
14178
|
var extname = path2.extname;
|
|
14197
|
-
var
|
|
14179
|
+
var join2 = path2.join;
|
|
14198
14180
|
var normalize = path2.normalize;
|
|
14199
|
-
var
|
|
14181
|
+
var resolve = path2.resolve;
|
|
14200
14182
|
var sep = path2.sep;
|
|
14201
14183
|
var BYTES_RANGE_REGEXP = /^ *bytes=/;
|
|
14202
14184
|
var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
|
|
@@ -14226,7 +14208,7 @@ var require_send = __commonJS({
|
|
|
14226
14208
|
this._maxage = opts.maxAge || opts.maxage;
|
|
14227
14209
|
this._maxage = typeof this._maxage === "string" ? ms(this._maxage) : Number(this._maxage);
|
|
14228
14210
|
this._maxage = !isNaN(this._maxage) ? Math.min(Math.max(0, this._maxage), MAX_MAXAGE) : 0;
|
|
14229
|
-
this._root = opts.root ?
|
|
14211
|
+
this._root = opts.root ? resolve(opts.root) : null;
|
|
14230
14212
|
}
|
|
14231
14213
|
__name(SendStream, "SendStream");
|
|
14232
14214
|
util.inherits(SendStream, Stream);
|
|
@@ -14368,7 +14350,7 @@ var require_send = __commonJS({
|
|
|
14368
14350
|
return res;
|
|
14369
14351
|
}
|
|
14370
14352
|
parts = path3.split(sep);
|
|
14371
|
-
path3 = normalize(
|
|
14353
|
+
path3 = normalize(join2(root, path3));
|
|
14372
14354
|
} else {
|
|
14373
14355
|
if (UP_PATH_REGEXP.test(path3)) {
|
|
14374
14356
|
debug('malicious path "%s"', path3);
|
|
@@ -14376,7 +14358,7 @@ var require_send = __commonJS({
|
|
|
14376
14358
|
return res;
|
|
14377
14359
|
}
|
|
14378
14360
|
parts = normalize(path3).split(sep);
|
|
14379
|
-
path3 =
|
|
14361
|
+
path3 = resolve(path3);
|
|
14380
14362
|
}
|
|
14381
14363
|
if (containsDotFile(parts)) {
|
|
14382
14364
|
debug('%s dotfile "%s"', this._dotfiles, path3);
|
|
@@ -14502,7 +14484,7 @@ var require_send = __commonJS({
|
|
|
14502
14484
|
if (err) return self.onStatError(err);
|
|
14503
14485
|
return self.error(404);
|
|
14504
14486
|
}
|
|
14505
|
-
var p =
|
|
14487
|
+
var p = join2(path3, self._index[i]);
|
|
14506
14488
|
debug('stat "%s"', p);
|
|
14507
14489
|
fs.stat(p, function(err2, stat) {
|
|
14508
14490
|
if (err2) return next(err2);
|
|
@@ -14774,7 +14756,7 @@ var require_response = __commonJS({
|
|
|
14774
14756
|
var cookie = require_cookie();
|
|
14775
14757
|
var send = require_send();
|
|
14776
14758
|
var extname = path2.extname;
|
|
14777
|
-
var
|
|
14759
|
+
var resolve = path2.resolve;
|
|
14778
14760
|
var vary = require_vary();
|
|
14779
14761
|
var { Buffer: Buffer2 } = __require("node:buffer");
|
|
14780
14762
|
var res = Object.create(http.ServerResponse.prototype);
|
|
@@ -14980,7 +14962,7 @@ var require_response = __commonJS({
|
|
|
14980
14962
|
}
|
|
14981
14963
|
opts = Object.create(opts);
|
|
14982
14964
|
opts.headers = headers;
|
|
14983
|
-
var fullPath = !opts.root ?
|
|
14965
|
+
var fullPath = !opts.root ? resolve(path3) : path3;
|
|
14984
14966
|
return this.sendFile(fullPath, opts, done);
|
|
14985
14967
|
}, "download");
|
|
14986
14968
|
res.contentType = res.type = /* @__PURE__ */ __name(function contentType(type) {
|
|
@@ -15239,7 +15221,7 @@ var require_serve_static = __commonJS({
|
|
|
15239
15221
|
var encodeUrl = require_encodeurl();
|
|
15240
15222
|
var escapeHtml = require_escape_html();
|
|
15241
15223
|
var parseUrl = require_parseurl();
|
|
15242
|
-
var
|
|
15224
|
+
var resolve = __require("path").resolve;
|
|
15243
15225
|
var send = require_send();
|
|
15244
15226
|
var url = __require("url");
|
|
15245
15227
|
module.exports = serveStatic;
|
|
@@ -15258,7 +15240,7 @@ var require_serve_static = __commonJS({
|
|
|
15258
15240
|
throw new TypeError("option setHeaders must be function");
|
|
15259
15241
|
}
|
|
15260
15242
|
opts.maxage = opts.maxage || opts.maxAge || 0;
|
|
15261
|
-
opts.root =
|
|
15243
|
+
opts.root = resolve(root);
|
|
15262
15244
|
var onDirectory = redirect ? createRedirectDirectoryListener() : createNotFoundDirectoryListener();
|
|
15263
15245
|
return /* @__PURE__ */ __name(function serveStatic2(req, res, next) {
|
|
15264
15246
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
@@ -15392,871 +15374,6 @@ var require_express2 = __commonJS({
|
|
|
15392
15374
|
}
|
|
15393
15375
|
});
|
|
15394
15376
|
|
|
15395
|
-
// packages/cli/src/serve/auth/device-flow.ts
|
|
15396
|
-
init_esbuild_shims();
|
|
15397
|
-
import { randomUUID } from "node:crypto";
|
|
15398
|
-
var SANITIZE_FOR_STDERR_RE = new RegExp(
|
|
15399
|
-
// ASCII C0 (0x00–0x1f), DEL (0x7f), C1 (0x80–0x9f), plus Unicode
|
|
15400
|
-
// lookalike controls:
|
|
15401
|
-
// \u200b–\u200f: zero-width chars + LRM/RLM
|
|
15402
|
-
// \u2028–\u2029: LINE / PARAGRAPH SEPARATOR (terminal newline equivalents)
|
|
15403
|
-
// \u202a–\u202e: bidirectional EMBEDDING / OVERRIDE controls
|
|
15404
|
-
// \u2066–\u2069: bidirectional ISOLATE controls (CVE-2021-42574)
|
|
15405
|
-
// \ufeff: BOM / ZWNBSP
|
|
15406
|
-
String.raw`[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]`,
|
|
15407
|
-
"g"
|
|
15408
|
-
);
|
|
15409
|
-
function sanitizeForStderr(value) {
|
|
15410
|
-
return value.replace(SANITIZE_FOR_STDERR_RE, "?");
|
|
15411
|
-
}
|
|
15412
|
-
__name(sanitizeForStderr, "sanitizeForStderr");
|
|
15413
|
-
var DEVICE_FLOW_DEFAULT_INTERVAL_MS = 5e3;
|
|
15414
|
-
var DEVICE_FLOW_TERMINAL_GRACE_MS = 5 * 6e4;
|
|
15415
|
-
var DEVICE_FLOW_SWEEP_INTERVAL_MS = 3e4;
|
|
15416
|
-
var DEVICE_FLOW_MAX_CONCURRENT = 4;
|
|
15417
|
-
var DEVICE_FLOW_SLOW_DOWN_BUMP_MS = 5e3;
|
|
15418
|
-
var DEVICE_FLOW_PERSIST_TIMEOUT_MS = 3e4;
|
|
15419
|
-
var DEVICE_FLOW_START_TIMEOUT_MS = 3e4;
|
|
15420
|
-
var DEVICE_FLOW_POLL_TIMEOUT_MS = 3e4;
|
|
15421
|
-
var DEVICE_FLOW_MAX_EXPIRES_IN_SEC = 60 * 60;
|
|
15422
|
-
var DEVICE_FLOW_MIN_EXPIRES_IN_SEC = 30;
|
|
15423
|
-
var DEVICE_FLOW_MAX_INTERVAL_MS = 6e4;
|
|
15424
|
-
var SECRET_BRAND = Symbol("DeviceFlowSecret");
|
|
15425
|
-
var SECRETS = /* @__PURE__ */ new WeakMap();
|
|
15426
|
-
function brandSecret(value) {
|
|
15427
|
-
const wrapper = Object.freeze({
|
|
15428
|
-
[SECRET_BRAND]: true,
|
|
15429
|
-
toString: /* @__PURE__ */ __name(() => "[redacted]", "toString"),
|
|
15430
|
-
toJSON: /* @__PURE__ */ __name(() => "[redacted]", "toJSON"),
|
|
15431
|
-
[Symbol.toPrimitive]: () => "[redacted]"
|
|
15432
|
-
});
|
|
15433
|
-
SECRETS.set(wrapper, value);
|
|
15434
|
-
return wrapper;
|
|
15435
|
-
}
|
|
15436
|
-
__name(brandSecret, "brandSecret");
|
|
15437
|
-
function unsafeRevealSecret(secret) {
|
|
15438
|
-
const value = SECRETS.get(secret);
|
|
15439
|
-
if (value === void 0) {
|
|
15440
|
-
throw new Error(
|
|
15441
|
-
"unsafeRevealSecret: argument is not a BrandedSecret (was never registered, or its WeakMap binding was lost via serialization)"
|
|
15442
|
-
);
|
|
15443
|
-
}
|
|
15444
|
-
return value;
|
|
15445
|
-
}
|
|
15446
|
-
__name(unsafeRevealSecret, "unsafeRevealSecret");
|
|
15447
|
-
var UnsupportedDeviceFlowProviderError = class extends Error {
|
|
15448
|
-
static {
|
|
15449
|
-
__name(this, "UnsupportedDeviceFlowProviderError");
|
|
15450
|
-
}
|
|
15451
|
-
code = "unsupported_provider";
|
|
15452
|
-
constructor(providerId) {
|
|
15453
|
-
super(
|
|
15454
|
-
`Unsupported device-flow provider (internal: declared but not registered): ${providerId}`
|
|
15455
|
-
);
|
|
15456
|
-
this.name = "UnsupportedDeviceFlowProviderError";
|
|
15457
|
-
}
|
|
15458
|
-
};
|
|
15459
|
-
var TooManyActiveDeviceFlowsError = class extends Error {
|
|
15460
|
-
static {
|
|
15461
|
-
__name(this, "TooManyActiveDeviceFlowsError");
|
|
15462
|
-
}
|
|
15463
|
-
code = "too_many_active_flows";
|
|
15464
|
-
constructor() {
|
|
15465
|
-
super(
|
|
15466
|
-
`Too many active device-flow attempts. Cancel one of the existing flows or wait for them to expire.`
|
|
15467
|
-
);
|
|
15468
|
-
this.name = "TooManyActiveDeviceFlowsError";
|
|
15469
|
-
}
|
|
15470
|
-
};
|
|
15471
|
-
var UpstreamDeviceFlowError = class extends Error {
|
|
15472
|
-
static {
|
|
15473
|
-
__name(this, "UpstreamDeviceFlowError");
|
|
15474
|
-
}
|
|
15475
|
-
code = "upstream_error";
|
|
15476
|
-
constructor(message) {
|
|
15477
|
-
super(message);
|
|
15478
|
-
this.name = "UpstreamDeviceFlowError";
|
|
15479
|
-
}
|
|
15480
|
-
};
|
|
15481
|
-
var DeviceFlowPollTimeoutError = class extends Error {
|
|
15482
|
-
static {
|
|
15483
|
-
__name(this, "DeviceFlowPollTimeoutError");
|
|
15484
|
-
}
|
|
15485
|
-
code = "poll_timeout";
|
|
15486
|
-
timeoutMs;
|
|
15487
|
-
/**
|
|
15488
|
-
* Runtime brand the registry sets ONLY on instances it constructed
|
|
15489
|
-
* inside its own timer callback. Default `false` for any
|
|
15490
|
-
* `new DeviceFlowPollTimeoutError(...)` call from outside the registry
|
|
15491
|
-
* — prevents a provider from spoofing the timeout signal.
|
|
15492
|
-
*/
|
|
15493
|
-
_isRegistryTimeout = false;
|
|
15494
|
-
constructor(timeoutMs) {
|
|
15495
|
-
super(`device-flow poll timeout after ${timeoutMs}ms`);
|
|
15496
|
-
this.name = "DeviceFlowPollTimeoutError";
|
|
15497
|
-
this.timeoutMs = timeoutMs;
|
|
15498
|
-
}
|
|
15499
|
-
};
|
|
15500
|
-
function makeRegistryPollTimeoutError(timeoutMs) {
|
|
15501
|
-
const err = new DeviceFlowPollTimeoutError(timeoutMs);
|
|
15502
|
-
err._isRegistryTimeout = true;
|
|
15503
|
-
return err;
|
|
15504
|
-
}
|
|
15505
|
-
__name(makeRegistryPollTimeoutError, "makeRegistryPollTimeoutError");
|
|
15506
|
-
var DEVICE_FLOW_REGISTRY_LOCAL = "deviceFlowRegistry";
|
|
15507
|
-
function setDeviceFlowRegistry(app, registry) {
|
|
15508
|
-
app.locals[DEVICE_FLOW_REGISTRY_LOCAL] = registry;
|
|
15509
|
-
}
|
|
15510
|
-
__name(setDeviceFlowRegistry, "setDeviceFlowRegistry");
|
|
15511
|
-
function getDeviceFlowRegistry(app) {
|
|
15512
|
-
return app.locals[DEVICE_FLOW_REGISTRY_LOCAL];
|
|
15513
|
-
}
|
|
15514
|
-
__name(getDeviceFlowRegistry, "getDeviceFlowRegistry");
|
|
15515
|
-
var DeviceFlowRegistry = class {
|
|
15516
|
-
constructor(deps) {
|
|
15517
|
-
this.deps = deps;
|
|
15518
|
-
this.now = deps.now ?? (() => Date.now());
|
|
15519
|
-
this.schedule = deps.schedule ?? ((ms, cb) => setTimeout(cb, ms));
|
|
15520
|
-
this.scheduleInterval = deps.scheduleInterval ?? ((ms, cb) => setInterval(cb, ms));
|
|
15521
|
-
this.clearScheduled = deps.clearScheduled ?? ((h) => clearTimeout(h));
|
|
15522
|
-
this.clearScheduledInterval = deps.clearScheduledInterval ?? ((h) => clearInterval(h));
|
|
15523
|
-
this.sweeperHandle = this.scheduleInterval(
|
|
15524
|
-
DEVICE_FLOW_SWEEP_INTERVAL_MS,
|
|
15525
|
-
() => this.sweep()
|
|
15526
|
-
);
|
|
15527
|
-
if (this.sweeperHandle && typeof this.sweeperHandle.unref === "function") {
|
|
15528
|
-
this.sweeperHandle.unref();
|
|
15529
|
-
}
|
|
15530
|
-
}
|
|
15531
|
-
static {
|
|
15532
|
-
__name(this, "DeviceFlowRegistry");
|
|
15533
|
-
}
|
|
15534
|
-
byId = /* @__PURE__ */ new Map();
|
|
15535
|
-
byProvider = /* @__PURE__ */ new Map();
|
|
15536
|
-
/**
|
|
15537
|
-
* Coalesces concurrent `start()` calls for the same `providerId`. Two
|
|
15538
|
-
* SDK clients posting `POST /workspace/auth/device-flow` in parallel
|
|
15539
|
-
* would otherwise both pass the "no existing pending entry" check,
|
|
15540
|
-
* each call `provider.start()` (a real IdP round-trip), and one's
|
|
15541
|
-
* write to `byProvider` would clobber the other — leaving an orphan
|
|
15542
|
-
* `byId` entry with a still-running poll timer that consumes IdP
|
|
15543
|
-
* quota for nothing. Mirrors `SharedTokenManager`'s in-flight refresh
|
|
15544
|
-
* coalescing pattern.
|
|
15545
|
-
*/
|
|
15546
|
-
inFlightStarts = /* @__PURE__ */ new Map();
|
|
15547
|
-
sweeperHandle;
|
|
15548
|
-
disposed = false;
|
|
15549
|
-
now;
|
|
15550
|
-
schedule;
|
|
15551
|
-
scheduleInterval;
|
|
15552
|
-
clearScheduled;
|
|
15553
|
-
clearScheduledInterval;
|
|
15554
|
-
/**
|
|
15555
|
-
* Start a new device flow OR — under per-provider singleton semantics —
|
|
15556
|
-
* return the existing pending entry (`attached: true`). The take-over
|
|
15557
|
-
* branch deliberately does NOT re-call `provider.start()`; making the
|
|
15558
|
-
* second POST a no-op (rather than a fresh IdP request) is the property
|
|
15559
|
-
* that lets a reconnecting SDK pick up an in-flight login without
|
|
15560
|
-
* burning IdP quota.
|
|
15561
|
-
*/
|
|
15562
|
-
async start(params) {
|
|
15563
|
-
if (this.disposed) {
|
|
15564
|
-
throw new Error("DeviceFlowRegistry disposed");
|
|
15565
|
-
}
|
|
15566
|
-
const provider = this.deps.resolveProvider(params.providerId);
|
|
15567
|
-
if (!provider) {
|
|
15568
|
-
throw new UnsupportedDeviceFlowProviderError(params.providerId);
|
|
15569
|
-
}
|
|
15570
|
-
const existing = this.byProvider.get(params.providerId);
|
|
15571
|
-
if (existing && existing.status === "pending") {
|
|
15572
|
-
this.recordTakeover(existing, params.initiatorClientId);
|
|
15573
|
-
return { view: toPublicView(existing), attached: true };
|
|
15574
|
-
}
|
|
15575
|
-
const inFlight = this.inFlightStarts.get(params.providerId);
|
|
15576
|
-
if (inFlight) {
|
|
15577
|
-
const result = await inFlight;
|
|
15578
|
-
const justCreated = this.byProvider.get(params.providerId);
|
|
15579
|
-
if (justCreated) {
|
|
15580
|
-
this.recordTakeover(justCreated, params.initiatorClientId);
|
|
15581
|
-
}
|
|
15582
|
-
return { view: result.view, attached: true };
|
|
15583
|
-
}
|
|
15584
|
-
if (this.countActive() >= DEVICE_FLOW_MAX_CONCURRENT) {
|
|
15585
|
-
throw new TooManyActiveDeviceFlowsError();
|
|
15586
|
-
}
|
|
15587
|
-
const promise = this.doStart(params, provider);
|
|
15588
|
-
this.inFlightStarts.set(params.providerId, promise);
|
|
15589
|
-
try {
|
|
15590
|
-
return await promise;
|
|
15591
|
-
} finally {
|
|
15592
|
-
if (this.inFlightStarts.get(params.providerId) === promise) {
|
|
15593
|
-
this.inFlightStarts.delete(params.providerId);
|
|
15594
|
-
}
|
|
15595
|
-
}
|
|
15596
|
-
}
|
|
15597
|
-
async doStart(params, provider) {
|
|
15598
|
-
const cancelController = new AbortController();
|
|
15599
|
-
let startResult;
|
|
15600
|
-
let startTimer;
|
|
15601
|
-
try {
|
|
15602
|
-
startResult = await new Promise(
|
|
15603
|
-
(resolve2, reject) => {
|
|
15604
|
-
startTimer = this.schedule(DEVICE_FLOW_START_TIMEOUT_MS, () => {
|
|
15605
|
-
try {
|
|
15606
|
-
cancelController.abort(new Error("device-flow start timeout"));
|
|
15607
|
-
} catch {
|
|
15608
|
-
}
|
|
15609
|
-
reject(
|
|
15610
|
-
new UpstreamDeviceFlowError(
|
|
15611
|
-
"device-flow start timeout (upstream IdP unresponsive)"
|
|
15612
|
-
)
|
|
15613
|
-
);
|
|
15614
|
-
});
|
|
15615
|
-
provider.start({ signal: cancelController.signal }).then(resolve2, reject);
|
|
15616
|
-
}
|
|
15617
|
-
);
|
|
15618
|
-
} finally {
|
|
15619
|
-
if (startTimer !== void 0) this.clearScheduled(startTimer);
|
|
15620
|
-
}
|
|
15621
|
-
if (this.disposed) {
|
|
15622
|
-
throw new Error("DeviceFlowRegistry disposed during start");
|
|
15623
|
-
}
|
|
15624
|
-
const expiresInSec = Number.isFinite(startResult.expiresIn) && startResult.expiresIn > 0 ? Math.min(
|
|
15625
|
-
DEVICE_FLOW_MAX_EXPIRES_IN_SEC,
|
|
15626
|
-
Math.max(DEVICE_FLOW_MIN_EXPIRES_IN_SEC, startResult.expiresIn)
|
|
15627
|
-
) : 600;
|
|
15628
|
-
const expiresAt = this.now() + expiresInSec * 1e3;
|
|
15629
|
-
const intervalSec = Number.isFinite(startResult.interval) && startResult.interval > 0 ? startResult.interval : DEVICE_FLOW_DEFAULT_INTERVAL_MS / 1e3;
|
|
15630
|
-
const intervalMs = Math.min(
|
|
15631
|
-
DEVICE_FLOW_MAX_INTERVAL_MS,
|
|
15632
|
-
Math.max(1e3, intervalSec * 1e3)
|
|
15633
|
-
);
|
|
15634
|
-
const entry = {
|
|
15635
|
-
deviceFlowId: randomUUID(),
|
|
15636
|
-
providerId: params.providerId,
|
|
15637
|
-
deviceCode: startResult.deviceCode,
|
|
15638
|
-
pkceVerifier: startResult.pkceVerifier,
|
|
15639
|
-
userCode: startResult.userCode,
|
|
15640
|
-
verificationUri: startResult.verificationUri,
|
|
15641
|
-
verificationUriComplete: startResult.verificationUriComplete,
|
|
15642
|
-
intervalMs,
|
|
15643
|
-
expiresAt,
|
|
15644
|
-
status: "pending",
|
|
15645
|
-
initiatorClientId: params.initiatorClientId,
|
|
15646
|
-
createdAt: this.now(),
|
|
15647
|
-
cancelController
|
|
15648
|
-
};
|
|
15649
|
-
this.byId.set(entry.deviceFlowId, entry);
|
|
15650
|
-
this.byProvider.set(entry.providerId, entry);
|
|
15651
|
-
this.deps.events.publish(
|
|
15652
|
-
{
|
|
15653
|
-
type: "started",
|
|
15654
|
-
data: {
|
|
15655
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15656
|
-
providerId: entry.providerId,
|
|
15657
|
-
expiresAt: entry.expiresAt
|
|
15658
|
-
}
|
|
15659
|
-
},
|
|
15660
|
-
entry.initiatorClientId
|
|
15661
|
-
);
|
|
15662
|
-
this.deps.audit?.record({
|
|
15663
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15664
|
-
providerId: entry.providerId,
|
|
15665
|
-
clientId: entry.initiatorClientId,
|
|
15666
|
-
status: "started",
|
|
15667
|
-
expiresInMs: entry.expiresAt - this.now()
|
|
15668
|
-
});
|
|
15669
|
-
this.schedulePoll(entry, provider);
|
|
15670
|
-
return { view: toPublicView(entry), attached: false };
|
|
15671
|
-
}
|
|
15672
|
-
get(deviceFlowId) {
|
|
15673
|
-
const entry = this.byId.get(deviceFlowId);
|
|
15674
|
-
if (!entry) return void 0;
|
|
15675
|
-
return toPublicView(entry);
|
|
15676
|
-
}
|
|
15677
|
-
/**
|
|
15678
|
-
* Cancel a pending flow. Idempotent on terminal entries (returns
|
|
15679
|
-
* `{ alreadyTerminal: true }` and does NOT re-emit `cancelled` —
|
|
15680
|
-
* RFC 7231 §4.3.5: DELETE may still be a 204 even when nothing was
|
|
15681
|
-
* removed). Returns `undefined` for unknown ids so the route layer
|
|
15682
|
-
* can map it to 404.
|
|
15683
|
-
*/
|
|
15684
|
-
cancel(deviceFlowId, cancellerClientId) {
|
|
15685
|
-
const entry = this.byId.get(deviceFlowId);
|
|
15686
|
-
if (!entry) return void 0;
|
|
15687
|
-
if (entry.persistInFlight) {
|
|
15688
|
-
entry.cancelRequestedDuringPersist = true;
|
|
15689
|
-
if (!entry.cancellerRecorded) {
|
|
15690
|
-
entry.cancellerRecorded = true;
|
|
15691
|
-
if (cancellerClientId) {
|
|
15692
|
-
entry.cancellerClientId = cancellerClientId;
|
|
15693
|
-
}
|
|
15694
|
-
}
|
|
15695
|
-
try {
|
|
15696
|
-
entry.cancelController.abort(new Error("cancel during persist"));
|
|
15697
|
-
} catch {
|
|
15698
|
-
}
|
|
15699
|
-
this.deps.audit?.record({
|
|
15700
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15701
|
-
providerId: entry.providerId,
|
|
15702
|
-
clientId: cancellerClientId,
|
|
15703
|
-
status: "cancelled",
|
|
15704
|
-
hint: "deferred (persist in flight; final state decided by persist resolution)"
|
|
15705
|
-
});
|
|
15706
|
-
return { alreadyTerminal: false };
|
|
15707
|
-
}
|
|
15708
|
-
if (!this.transitionTerminal(entry, "cancelled")) {
|
|
15709
|
-
return { alreadyTerminal: true };
|
|
15710
|
-
}
|
|
15711
|
-
this.deps.events.publish(
|
|
15712
|
-
{
|
|
15713
|
-
type: "cancelled",
|
|
15714
|
-
data: { deviceFlowId: entry.deviceFlowId }
|
|
15715
|
-
},
|
|
15716
|
-
cancellerClientId
|
|
15717
|
-
);
|
|
15718
|
-
this.deps.audit?.record({
|
|
15719
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15720
|
-
providerId: entry.providerId,
|
|
15721
|
-
clientId: cancellerClientId,
|
|
15722
|
-
status: "cancelled"
|
|
15723
|
-
});
|
|
15724
|
-
return { alreadyTerminal: false };
|
|
15725
|
-
}
|
|
15726
|
-
/**
|
|
15727
|
-
* Active = pending entries already installed in `byProvider` PLUS
|
|
15728
|
-
* in-flight starts that haven't yet completed `provider.start()`.
|
|
15729
|
-
* Terminal entries in grace don't count toward the cap. Including
|
|
15730
|
-
* `inFlightStarts.size` prevents concurrent starts for distinct
|
|
15731
|
-
* providers from bypassing the workspace-wide cap.
|
|
15732
|
-
*/
|
|
15733
|
-
countActive() {
|
|
15734
|
-
let n = 0;
|
|
15735
|
-
for (const entry of this.byProvider.values()) {
|
|
15736
|
-
if (entry.status === "pending") n += 1;
|
|
15737
|
-
}
|
|
15738
|
-
return n + this.inFlightStarts.size;
|
|
15739
|
-
}
|
|
15740
|
-
schedulePoll(entry, provider) {
|
|
15741
|
-
if (entry.status !== "pending") return;
|
|
15742
|
-
if (entry.deviceCode === void 0) return;
|
|
15743
|
-
if (this.disposed) return;
|
|
15744
|
-
entry.pollHandle = this.schedule(entry.intervalMs, () => {
|
|
15745
|
-
void this.runPollTick(entry, provider);
|
|
15746
|
-
});
|
|
15747
|
-
if (entry.pollHandle && typeof entry.pollHandle.unref === "function") {
|
|
15748
|
-
entry.pollHandle.unref();
|
|
15749
|
-
}
|
|
15750
|
-
}
|
|
15751
|
-
async runPollTick(entry, provider) {
|
|
15752
|
-
if (entry.status !== "pending") return;
|
|
15753
|
-
if (this.disposed) return;
|
|
15754
|
-
if (entry.deviceCode === void 0) return;
|
|
15755
|
-
const now = this.now();
|
|
15756
|
-
if (now >= entry.expiresAt) {
|
|
15757
|
-
this.expireEntry(entry);
|
|
15758
|
-
return;
|
|
15759
|
-
}
|
|
15760
|
-
entry.lastPolledAt = now;
|
|
15761
|
-
let result;
|
|
15762
|
-
let rawProviderError;
|
|
15763
|
-
let pollTimedOut = false;
|
|
15764
|
-
let pollTimer;
|
|
15765
|
-
let providerPollPromise;
|
|
15766
|
-
try {
|
|
15767
|
-
result = await new Promise((resolve2, reject) => {
|
|
15768
|
-
pollTimer = this.schedule(DEVICE_FLOW_POLL_TIMEOUT_MS, () => {
|
|
15769
|
-
const timeoutError = makeRegistryPollTimeoutError(
|
|
15770
|
-
DEVICE_FLOW_POLL_TIMEOUT_MS
|
|
15771
|
-
);
|
|
15772
|
-
try {
|
|
15773
|
-
entry.cancelController.abort(timeoutError);
|
|
15774
|
-
} catch {
|
|
15775
|
-
}
|
|
15776
|
-
reject(timeoutError);
|
|
15777
|
-
});
|
|
15778
|
-
providerPollPromise = provider.poll(
|
|
15779
|
-
{
|
|
15780
|
-
deviceCode: entry.deviceCode,
|
|
15781
|
-
pkceVerifier: entry.pkceVerifier
|
|
15782
|
-
},
|
|
15783
|
-
{ signal: entry.cancelController.signal }
|
|
15784
|
-
);
|
|
15785
|
-
providerPollPromise.then(resolve2, reject);
|
|
15786
|
-
});
|
|
15787
|
-
} catch (err) {
|
|
15788
|
-
if (err instanceof DeviceFlowPollTimeoutError && err._isRegistryTimeout === true) {
|
|
15789
|
-
pollTimedOut = true;
|
|
15790
|
-
result = {
|
|
15791
|
-
kind: "error",
|
|
15792
|
-
errorKind: "upstream_error",
|
|
15793
|
-
hint: `provider.poll() timed out after ${err.timeoutMs}ms; check IdP connectivity`
|
|
15794
|
-
};
|
|
15795
|
-
} else {
|
|
15796
|
-
rawProviderError = err instanceof Error ? err.message : String(err);
|
|
15797
|
-
result = {
|
|
15798
|
-
kind: "error",
|
|
15799
|
-
errorKind: "upstream_error",
|
|
15800
|
-
hint: "provider.poll() failed; see daemon audit log for details"
|
|
15801
|
-
};
|
|
15802
|
-
}
|
|
15803
|
-
} finally {
|
|
15804
|
-
if (pollTimer !== void 0) this.clearScheduled(pollTimer);
|
|
15805
|
-
}
|
|
15806
|
-
if (pollTimedOut && providerPollPromise !== void 0) {
|
|
15807
|
-
const tracked = providerPollPromise;
|
|
15808
|
-
const auditDeviceFlowId = entry.deviceFlowId;
|
|
15809
|
-
const auditProviderId = entry.providerId;
|
|
15810
|
-
const auditClientId = entry.initiatorClientId;
|
|
15811
|
-
const audit = this.deps.audit;
|
|
15812
|
-
void tracked.then(
|
|
15813
|
-
(latePollResult) => {
|
|
15814
|
-
audit?.record({
|
|
15815
|
-
deviceFlowId: auditDeviceFlowId,
|
|
15816
|
-
providerId: auditProviderId,
|
|
15817
|
-
clientId: auditClientId,
|
|
15818
|
-
status: "failed",
|
|
15819
|
-
errorKind: "upstream_error",
|
|
15820
|
-
// Distinguish "real late response" (pending/slow_down/
|
|
15821
|
-
// success) from "provider's abort cooperation" (error).
|
|
15822
|
-
// `latePollResult.kind` is provider-controlled — sanitize
|
|
15823
|
-
// before interpolation to prevent log injection.
|
|
15824
|
-
hint: latePollResult.kind === "error" ? `lost_late_poll_after_timeout: provider.poll() resolved kind=error after ${DEVICE_FLOW_POLL_TIMEOUT_MS}ms ceiling \u2014 likely abort-driven cooperation; IdP responsiveness unknown` : `lost_late_poll_after_timeout: provider.poll() resolved kind=${sanitizeForStderr(latePollResult.kind)} after ${DEVICE_FLOW_POLL_TIMEOUT_MS}ms ceiling \u2014 IdP is responsive but slow; consider raising the operator-side IdP latency alert threshold`
|
|
15825
|
-
});
|
|
15826
|
-
},
|
|
15827
|
-
(lateErr) => {
|
|
15828
|
-
if (lateErr instanceof DeviceFlowPollTimeoutError && lateErr._isRegistryTimeout === true)
|
|
15829
|
-
return;
|
|
15830
|
-
const safeDetail = lateErr instanceof Error ? `${sanitizeForStderr(lateErr.name)} (message ${lateErr.message.length} bytes; raw suppressed)` : `<non-Error throw: ${typeof lateErr}>`;
|
|
15831
|
-
audit?.record({
|
|
15832
|
-
deviceFlowId: auditDeviceFlowId,
|
|
15833
|
-
providerId: auditProviderId,
|
|
15834
|
-
clientId: auditClientId,
|
|
15835
|
-
status: "failed",
|
|
15836
|
-
errorKind: "upstream_error",
|
|
15837
|
-
hint: `lost_late_poll_after_timeout: provider.poll() rejected after ${DEVICE_FLOW_POLL_TIMEOUT_MS}ms ceiling: ${safeDetail}`
|
|
15838
|
-
});
|
|
15839
|
-
}
|
|
15840
|
-
// Terminal `.catch` so a throw inside either handler doesn't
|
|
15841
|
-
// surface as an unhandled rejection (audit is best-effort).
|
|
15842
|
-
).catch(() => {
|
|
15843
|
-
});
|
|
15844
|
-
}
|
|
15845
|
-
if (this.disposed) return;
|
|
15846
|
-
if (entry.status !== "pending") return;
|
|
15847
|
-
switch (result.kind) {
|
|
15848
|
-
case "pending":
|
|
15849
|
-
this.schedulePoll(entry, provider);
|
|
15850
|
-
return;
|
|
15851
|
-
case "slow_down":
|
|
15852
|
-
entry.intervalMs = Math.min(
|
|
15853
|
-
DEVICE_FLOW_MAX_INTERVAL_MS,
|
|
15854
|
-
entry.intervalMs + DEVICE_FLOW_SLOW_DOWN_BUMP_MS
|
|
15855
|
-
);
|
|
15856
|
-
this.deps.events.publish(
|
|
15857
|
-
{
|
|
15858
|
-
type: "throttled",
|
|
15859
|
-
data: {
|
|
15860
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15861
|
-
intervalMs: entry.intervalMs
|
|
15862
|
-
}
|
|
15863
|
-
},
|
|
15864
|
-
entry.initiatorClientId
|
|
15865
|
-
);
|
|
15866
|
-
this.schedulePoll(entry, provider);
|
|
15867
|
-
return;
|
|
15868
|
-
case "success": {
|
|
15869
|
-
let metadata = {};
|
|
15870
|
-
let persistError;
|
|
15871
|
-
let persistTimer;
|
|
15872
|
-
let persistTimedOut = false;
|
|
15873
|
-
entry.persistInFlight = true;
|
|
15874
|
-
let persistTracker;
|
|
15875
|
-
try {
|
|
15876
|
-
persistTracker = result.persist({
|
|
15877
|
-
signal: entry.cancelController.signal
|
|
15878
|
-
});
|
|
15879
|
-
} catch (syncErr) {
|
|
15880
|
-
persistError = syncErr;
|
|
15881
|
-
persistTracker = Promise.reject(syncErr);
|
|
15882
|
-
persistTracker.catch(() => {
|
|
15883
|
-
});
|
|
15884
|
-
}
|
|
15885
|
-
persistTracker.then(
|
|
15886
|
-
(lateMeta) => {
|
|
15887
|
-
if (!persistTimedOut) return;
|
|
15888
|
-
this.deps.audit?.record({
|
|
15889
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15890
|
-
providerId: entry.providerId,
|
|
15891
|
-
clientId: entry.initiatorClientId,
|
|
15892
|
-
status: "authorized",
|
|
15893
|
-
hint: `lost_success_after_timeout (provider.persist ignored timeout signal; credentials on disk but registry already published persist_failed; expiresAt=${lateMeta.expiresAt ?? "unknown"})`
|
|
15894
|
-
});
|
|
15895
|
-
},
|
|
15896
|
-
() => {
|
|
15897
|
-
}
|
|
15898
|
-
);
|
|
15899
|
-
if (persistError === void 0) {
|
|
15900
|
-
try {
|
|
15901
|
-
metadata = await new Promise((resolve2, reject) => {
|
|
15902
|
-
persistTimer = this.schedule(
|
|
15903
|
-
DEVICE_FLOW_PERSIST_TIMEOUT_MS,
|
|
15904
|
-
() => {
|
|
15905
|
-
persistTimedOut = true;
|
|
15906
|
-
try {
|
|
15907
|
-
entry.cancelController.abort(new Error("persist timeout"));
|
|
15908
|
-
} catch {
|
|
15909
|
-
}
|
|
15910
|
-
reject(new Error("persist timeout"));
|
|
15911
|
-
}
|
|
15912
|
-
);
|
|
15913
|
-
persistTracker.then(resolve2, reject);
|
|
15914
|
-
});
|
|
15915
|
-
} catch (err) {
|
|
15916
|
-
persistError = err;
|
|
15917
|
-
} finally {
|
|
15918
|
-
if (persistTimer !== void 0) this.clearScheduled(persistTimer);
|
|
15919
|
-
entry.persistInFlight = false;
|
|
15920
|
-
}
|
|
15921
|
-
} else {
|
|
15922
|
-
entry.persistInFlight = false;
|
|
15923
|
-
}
|
|
15924
|
-
if (this.disposed) return;
|
|
15925
|
-
const cancelDuringPersist = entry.cancelRequestedDuringPersist === true;
|
|
15926
|
-
if (persistError) {
|
|
15927
|
-
if (cancelDuringPersist) {
|
|
15928
|
-
if (this.transitionTerminal(entry, "cancelled")) {
|
|
15929
|
-
this.deps.events.publish(
|
|
15930
|
-
{
|
|
15931
|
-
type: "cancelled",
|
|
15932
|
-
data: { deviceFlowId: entry.deviceFlowId }
|
|
15933
|
-
},
|
|
15934
|
-
entry.cancellerClientId ?? entry.initiatorClientId
|
|
15935
|
-
);
|
|
15936
|
-
}
|
|
15937
|
-
} else if (this.transitionTerminal(entry, "error", "persist_failed")) {
|
|
15938
|
-
const pastExpiry = this.now() >= entry.expiresAt;
|
|
15939
|
-
this.deps.events.publish(
|
|
15940
|
-
{
|
|
15941
|
-
type: "failed",
|
|
15942
|
-
data: {
|
|
15943
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15944
|
-
errorKind: "persist_failed",
|
|
15945
|
-
hint: "credentials could not be written to the daemon filesystem \u2014 check disk space and permissions"
|
|
15946
|
-
}
|
|
15947
|
-
},
|
|
15948
|
-
entry.initiatorClientId
|
|
15949
|
-
);
|
|
15950
|
-
this.deps.audit?.record({
|
|
15951
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15952
|
-
providerId: entry.providerId,
|
|
15953
|
-
clientId: entry.initiatorClientId,
|
|
15954
|
-
status: "failed",
|
|
15955
|
-
errorKind: "persist_failed",
|
|
15956
|
-
...pastExpiry ? {
|
|
15957
|
-
hint: "persist_also_failed_past_expiry (root cause is disk I/O; entry was past expiresAt by the time persist resolved)"
|
|
15958
|
-
} : {}
|
|
15959
|
-
});
|
|
15960
|
-
}
|
|
15961
|
-
return;
|
|
15962
|
-
}
|
|
15963
|
-
if (this.transitionTerminal(entry, "authorized")) {
|
|
15964
|
-
this.deps.events.publish(
|
|
15965
|
-
{
|
|
15966
|
-
type: "authorized",
|
|
15967
|
-
data: {
|
|
15968
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15969
|
-
providerId: entry.providerId,
|
|
15970
|
-
expiresAt: metadata.expiresAt,
|
|
15971
|
-
accountAlias: metadata.accountAlias
|
|
15972
|
-
}
|
|
15973
|
-
},
|
|
15974
|
-
entry.initiatorClientId
|
|
15975
|
-
);
|
|
15976
|
-
this.deps.audit?.record({
|
|
15977
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15978
|
-
providerId: entry.providerId,
|
|
15979
|
-
clientId: entry.initiatorClientId,
|
|
15980
|
-
status: "authorized",
|
|
15981
|
-
...cancelDuringPersist ? {
|
|
15982
|
-
hint: "lost_success_kept (cancel during persist; credentials kept per IdP approval)"
|
|
15983
|
-
} : {}
|
|
15984
|
-
});
|
|
15985
|
-
}
|
|
15986
|
-
return;
|
|
15987
|
-
}
|
|
15988
|
-
case "error":
|
|
15989
|
-
entry.hint = result.hint;
|
|
15990
|
-
if (this.transitionTerminal(entry, "error", result.errorKind)) {
|
|
15991
|
-
this.deps.events.publish(
|
|
15992
|
-
{
|
|
15993
|
-
type: "failed",
|
|
15994
|
-
data: {
|
|
15995
|
-
deviceFlowId: entry.deviceFlowId,
|
|
15996
|
-
errorKind: result.errorKind,
|
|
15997
|
-
hint: result.hint
|
|
15998
|
-
}
|
|
15999
|
-
},
|
|
16000
|
-
entry.initiatorClientId
|
|
16001
|
-
);
|
|
16002
|
-
this.deps.audit?.record({
|
|
16003
|
-
deviceFlowId: entry.deviceFlowId,
|
|
16004
|
-
providerId: entry.providerId,
|
|
16005
|
-
clientId: entry.initiatorClientId,
|
|
16006
|
-
status: "failed",
|
|
16007
|
-
errorKind: result.errorKind,
|
|
16008
|
-
// When a provider threw, include the full raw message in
|
|
16009
|
-
// the audit hint. When `rawProviderError` is undefined,
|
|
16010
|
-
// use the structured `result.hint` so the audit trail
|
|
16011
|
-
// matches the SSE event.
|
|
16012
|
-
...rawProviderError !== void 0 ? {
|
|
16013
|
-
hint: `provider.poll() threw (raw): ${sanitizeForStderr(rawProviderError)}`
|
|
16014
|
-
} : result.hint !== void 0 ? { hint: result.hint } : {}
|
|
16015
|
-
});
|
|
16016
|
-
}
|
|
16017
|
-
return;
|
|
16018
|
-
default: {
|
|
16019
|
-
const _exhaustive = result;
|
|
16020
|
-
void _exhaustive;
|
|
16021
|
-
}
|
|
16022
|
-
}
|
|
16023
|
-
}
|
|
16024
|
-
/**
|
|
16025
|
-
* Record a take-over: a second SDK client posted for a provider that
|
|
16026
|
-
* already has a pending entry. When the caller differs from the
|
|
16027
|
-
* entry's initiator, stamp it on `lastOriginatorClientId` and emit
|
|
16028
|
-
* an audit breadcrumb. No event publish — the `attached: true` HTTP
|
|
16029
|
-
* response is the second caller's signal.
|
|
16030
|
-
*/
|
|
16031
|
-
recordTakeover(entry, takeoverClientId) {
|
|
16032
|
-
if (!takeoverClientId) return;
|
|
16033
|
-
if (takeoverClientId === entry.initiatorClientId) return;
|
|
16034
|
-
if (takeoverClientId === entry.lastOriginatorClientId) return;
|
|
16035
|
-
entry.lastOriginatorClientId = takeoverClientId;
|
|
16036
|
-
this.deps.audit?.record({
|
|
16037
|
-
deviceFlowId: entry.deviceFlowId,
|
|
16038
|
-
providerId: entry.providerId,
|
|
16039
|
-
clientId: takeoverClientId,
|
|
16040
|
-
status: "started",
|
|
16041
|
-
hint: `take-over (per-provider singleton; original initiator=${entry.initiatorClientId ?? "(none)"})`
|
|
16042
|
-
});
|
|
16043
|
-
}
|
|
16044
|
-
/**
|
|
16045
|
-
* Drive a pending entry to the time-based `expired` terminal.
|
|
16046
|
-
* Extracted so the poll-tick and sweeper share a single event shape.
|
|
16047
|
-
* No-op if the entry has already transitioned.
|
|
16048
|
-
*/
|
|
16049
|
-
expireEntry(entry) {
|
|
16050
|
-
if (!this.transitionTerminal(entry, "expired", "expired_token")) return;
|
|
16051
|
-
this.deps.events.publish(
|
|
16052
|
-
{
|
|
16053
|
-
type: "failed",
|
|
16054
|
-
data: {
|
|
16055
|
-
deviceFlowId: entry.deviceFlowId,
|
|
16056
|
-
errorKind: "expired_token",
|
|
16057
|
-
hint: "device-flow expired without authorization"
|
|
16058
|
-
}
|
|
16059
|
-
},
|
|
16060
|
-
entry.initiatorClientId
|
|
16061
|
-
);
|
|
16062
|
-
this.deps.audit?.record({
|
|
16063
|
-
deviceFlowId: entry.deviceFlowId,
|
|
16064
|
-
providerId: entry.providerId,
|
|
16065
|
-
clientId: entry.initiatorClientId,
|
|
16066
|
-
status: "expired",
|
|
16067
|
-
errorKind: "expired_token"
|
|
16068
|
-
});
|
|
16069
|
-
}
|
|
16070
|
-
/**
|
|
16071
|
-
* Move a pending entry to terminal state. Returns **`true` exactly once**
|
|
16072
|
-
* — the call site that successfully drove the transition. Subsequent
|
|
16073
|
-
* calls (sweeper × poll-tick race, double cancel, etc.) return `false`
|
|
16074
|
-
* so the caller can suppress duplicate event publish + audit log.
|
|
16075
|
-
*
|
|
16076
|
-
* On a successful transition:
|
|
16077
|
-
* 1. clears any pending poll timer
|
|
16078
|
-
* 2. wipes the secret material from `entry.deviceCode` /
|
|
16079
|
-
* `entry.pkceVerifier`. The PRIMARY guard against secret leaks
|
|
16080
|
-
* is the `entry.status !== 'pending'` check at the top of
|
|
16081
|
-
* `runPollTick` — a stale timer that managed to fire post-clear
|
|
16082
|
-
* bails out before touching the entry. Secret-clearing here is
|
|
16083
|
-
* DEFENSE IN DEPTH: even if a future refactor weakens the
|
|
16084
|
-
* status guard, the registry's in-memory state can no longer
|
|
16085
|
-
* hand out the upstream `device_code` to a late-arriving
|
|
16086
|
-
* logger / serializer.
|
|
16087
|
-
* 3. records `terminalAt` for the sweeper to evict after grace
|
|
16088
|
-
* 4. removes the per-provider singleton index so a new POST creates
|
|
16089
|
-
* a fresh flow instead of taking over the terminal one
|
|
16090
|
-
*/
|
|
16091
|
-
transitionTerminal(entry, status, errorKind) {
|
|
16092
|
-
if (entry.status !== "pending") return false;
|
|
16093
|
-
entry.status = status;
|
|
16094
|
-
if (errorKind) entry.errorKind = errorKind;
|
|
16095
|
-
entry.terminalAt = this.now();
|
|
16096
|
-
if (entry.pollHandle) {
|
|
16097
|
-
this.clearScheduled(entry.pollHandle);
|
|
16098
|
-
entry.pollHandle = void 0;
|
|
16099
|
-
}
|
|
16100
|
-
entry.deviceCode = void 0;
|
|
16101
|
-
entry.pkceVerifier = void 0;
|
|
16102
|
-
try {
|
|
16103
|
-
entry.cancelController.abort();
|
|
16104
|
-
} catch {
|
|
16105
|
-
}
|
|
16106
|
-
if (this.byProvider.get(entry.providerId) === entry) {
|
|
16107
|
-
this.byProvider.delete(entry.providerId);
|
|
16108
|
-
}
|
|
16109
|
-
return true;
|
|
16110
|
-
}
|
|
16111
|
-
/**
|
|
16112
|
-
* Periodic sweeper:
|
|
16113
|
-
* (a) pending entries past `expiresAt` get a synthetic timeout event
|
|
16114
|
-
* (the polling loop also handles this on its next tick, but a
|
|
16115
|
-
* wedged poll path should not block expiry)
|
|
16116
|
-
* (b) terminal entries past their grace window get evicted entirely
|
|
16117
|
-
*/
|
|
16118
|
-
sweep() {
|
|
16119
|
-
if (this.disposed) return;
|
|
16120
|
-
const now = this.now();
|
|
16121
|
-
for (const entry of [...this.byId.values()]) {
|
|
16122
|
-
if (entry.persistInFlight) continue;
|
|
16123
|
-
if (entry.status === "pending" && now >= entry.expiresAt) {
|
|
16124
|
-
this.expireEntry(entry);
|
|
16125
|
-
continue;
|
|
16126
|
-
}
|
|
16127
|
-
if (entry.status !== "pending" && entry.terminalAt !== void 0 && now - entry.terminalAt >= DEVICE_FLOW_TERMINAL_GRACE_MS) {
|
|
16128
|
-
this.byId.delete(entry.deviceFlowId);
|
|
16129
|
-
}
|
|
16130
|
-
}
|
|
16131
|
-
}
|
|
16132
|
-
/**
|
|
16133
|
-
* For diagnostics / GET /workspace/auth/status: report only pending
|
|
16134
|
-
* flows. Terminal entries are an implementation detail of the SDK
|
|
16135
|
-
* reconnect path and shouldn't be enumerated to all bearer-token
|
|
16136
|
-
* holders.
|
|
16137
|
-
*/
|
|
16138
|
-
listPending() {
|
|
16139
|
-
const out = [];
|
|
16140
|
-
for (const entry of this.byId.values()) {
|
|
16141
|
-
if (entry.status === "pending") out.push(toPublicView(entry));
|
|
16142
|
-
}
|
|
16143
|
-
return out;
|
|
16144
|
-
}
|
|
16145
|
-
dispose() {
|
|
16146
|
-
if (this.disposed) return;
|
|
16147
|
-
this.disposed = true;
|
|
16148
|
-
if (this.sweeperHandle) {
|
|
16149
|
-
this.clearScheduledInterval(this.sweeperHandle);
|
|
16150
|
-
this.sweeperHandle = void 0;
|
|
16151
|
-
}
|
|
16152
|
-
for (const entry of this.byId.values()) {
|
|
16153
|
-
if (entry.pollHandle) {
|
|
16154
|
-
this.clearScheduled(entry.pollHandle);
|
|
16155
|
-
entry.pollHandle = void 0;
|
|
16156
|
-
}
|
|
16157
|
-
try {
|
|
16158
|
-
entry.cancelController.abort();
|
|
16159
|
-
} catch {
|
|
16160
|
-
}
|
|
16161
|
-
entry.deviceCode = void 0;
|
|
16162
|
-
entry.pkceVerifier = void 0;
|
|
16163
|
-
}
|
|
16164
|
-
this.byId.clear();
|
|
16165
|
-
this.byProvider.clear();
|
|
16166
|
-
}
|
|
16167
|
-
};
|
|
16168
|
-
function toPublicView(entry) {
|
|
16169
|
-
const base = {
|
|
16170
|
-
deviceFlowId: entry.deviceFlowId,
|
|
16171
|
-
providerId: entry.providerId,
|
|
16172
|
-
status: entry.status,
|
|
16173
|
-
createdAt: entry.createdAt,
|
|
16174
|
-
initiatorClientId: entry.initiatorClientId
|
|
16175
|
-
};
|
|
16176
|
-
if (entry.errorKind) base.errorKind = entry.errorKind;
|
|
16177
|
-
if (entry.hint) base.hint = entry.hint;
|
|
16178
|
-
if (entry.lastPolledAt !== void 0) base.lastPolledAt = entry.lastPolledAt;
|
|
16179
|
-
if (entry.status === "pending") {
|
|
16180
|
-
base.userCode = entry.userCode;
|
|
16181
|
-
base.verificationUri = entry.verificationUri;
|
|
16182
|
-
base.verificationUriComplete = entry.verificationUriComplete;
|
|
16183
|
-
base.expiresAt = entry.expiresAt;
|
|
16184
|
-
base.intervalMs = entry.intervalMs;
|
|
16185
|
-
}
|
|
16186
|
-
return base;
|
|
16187
|
-
}
|
|
16188
|
-
__name(toPublicView, "toPublicView");
|
|
16189
|
-
|
|
16190
|
-
// packages/cli/src/serve/bridge-file-system-adapter.ts
|
|
16191
|
-
init_esbuild_shims();
|
|
16192
|
-
var ACP_WRITE_ROUTE = "ACP writeTextFile";
|
|
16193
|
-
var ACP_READ_ROUTE = "ACP readTextFile";
|
|
16194
|
-
function buildAuditContext(params, route) {
|
|
16195
|
-
return {
|
|
16196
|
-
route,
|
|
16197
|
-
...params.sessionId ? { sessionId: params.sessionId } : {}
|
|
16198
|
-
};
|
|
16199
|
-
}
|
|
16200
|
-
__name(buildAuditContext, "buildAuditContext");
|
|
16201
|
-
function createBridgeFileSystemAdapter(factory) {
|
|
16202
|
-
return {
|
|
16203
|
-
async writeText(params) {
|
|
16204
|
-
const wfs = factory.forRequest(
|
|
16205
|
-
buildAuditContext(params, ACP_WRITE_ROUTE)
|
|
16206
|
-
);
|
|
16207
|
-
const resolved = await wfs.resolve(params.path, "write");
|
|
16208
|
-
await wfs.writeTextOverwrite(resolved, params.content);
|
|
16209
|
-
return {};
|
|
16210
|
-
},
|
|
16211
|
-
async readText(params) {
|
|
16212
|
-
const wfs = factory.forRequest(buildAuditContext(params, ACP_READ_ROUTE));
|
|
16213
|
-
const resolved = await wfs.resolve(params.path, "read");
|
|
16214
|
-
const opts = {};
|
|
16215
|
-
if (typeof params.line === "number" && params.line > 0) {
|
|
16216
|
-
opts.line = params.line;
|
|
16217
|
-
}
|
|
16218
|
-
if (typeof params.limit === "number" && params.limit > 0) {
|
|
16219
|
-
opts.limit = params.limit;
|
|
16220
|
-
}
|
|
16221
|
-
const { content } = await wfs.readText(resolved, opts);
|
|
16222
|
-
return { content };
|
|
16223
|
-
}
|
|
16224
|
-
};
|
|
16225
|
-
}
|
|
16226
|
-
__name(createBridgeFileSystemAdapter, "createBridgeFileSystemAdapter");
|
|
16227
|
-
|
|
16228
|
-
// packages/cli/src/serve/fs/path-mutex-registry.ts
|
|
16229
|
-
init_esbuild_shims();
|
|
16230
|
-
var PathMutexRegistry = class {
|
|
16231
|
-
static {
|
|
16232
|
-
__name(this, "PathMutexRegistry");
|
|
16233
|
-
}
|
|
16234
|
-
tails = /* @__PURE__ */ new Map();
|
|
16235
|
-
async runExclusive(key, fn) {
|
|
16236
|
-
const previous = this.tails.get(key) ?? Promise.resolve();
|
|
16237
|
-
let release;
|
|
16238
|
-
const current = new Promise((resolve2) => {
|
|
16239
|
-
release = resolve2;
|
|
16240
|
-
});
|
|
16241
|
-
const tail = previous.catch(() => void 0).then(() => current);
|
|
16242
|
-
this.tails.set(key, tail);
|
|
16243
|
-
await previous.catch(() => void 0);
|
|
16244
|
-
try {
|
|
16245
|
-
return await fn();
|
|
16246
|
-
} finally {
|
|
16247
|
-
release();
|
|
16248
|
-
if (this.tails.get(key) === tail) this.tails.delete(key);
|
|
16249
|
-
}
|
|
16250
|
-
}
|
|
16251
|
-
};
|
|
16252
|
-
|
|
16253
|
-
// packages/cli/src/serve/health-query.ts
|
|
16254
|
-
init_esbuild_shims();
|
|
16255
|
-
function isDeepHealthQuery(raw) {
|
|
16256
|
-
return raw === "1" || raw === "true" || raw === "";
|
|
16257
|
-
}
|
|
16258
|
-
__name(isDeepHealthQuery, "isDeepHealthQuery");
|
|
16259
|
-
|
|
16260
15377
|
// packages/cli/src/serve/web-shell-resolver.ts
|
|
16261
15378
|
init_esbuild_shims();
|
|
16262
15379
|
import { existsSync } from "node:fs";
|
|
@@ -16288,2497 +15405,15 @@ function resolveWebShellDir() {
|
|
|
16288
15405
|
}
|
|
16289
15406
|
__name(resolveWebShellDir, "resolveWebShellDir");
|
|
16290
15407
|
|
|
16291
|
-
// packages/cli/src/serve/auth.ts
|
|
16292
|
-
init_esbuild_shims();
|
|
16293
|
-
import { createHash, timingSafeEqual } from "node:crypto";
|
|
16294
|
-
var denyBrowserOriginCors = /* @__PURE__ */ __name((req, res, next) => {
|
|
16295
|
-
if (req.headers.origin) {
|
|
16296
|
-
res.setHeader("Vary", "Origin");
|
|
16297
|
-
res.status(403).json({ error: "Request denied by CORS policy" });
|
|
16298
|
-
return;
|
|
16299
|
-
}
|
|
16300
|
-
next();
|
|
16301
|
-
}, "denyBrowserOriginCors");
|
|
16302
|
-
var InvalidAllowOriginPatternError = class extends Error {
|
|
16303
|
-
static {
|
|
16304
|
-
__name(this, "InvalidAllowOriginPatternError");
|
|
16305
|
-
}
|
|
16306
|
-
pattern;
|
|
16307
|
-
constructor(pattern, reason) {
|
|
16308
|
-
super(
|
|
16309
|
-
`Invalid --allow-origin pattern ${JSON.stringify(pattern)}: ${reason}. Expected \`*\` or a URL origin of the form \`<scheme>://<host>[:<port>]\` (no trailing slash, no path, no userinfo, no query).`
|
|
16310
|
-
);
|
|
16311
|
-
this.name = "InvalidAllowOriginPatternError";
|
|
16312
|
-
this.pattern = pattern;
|
|
16313
|
-
}
|
|
16314
|
-
};
|
|
16315
|
-
function parseAllowOriginPatterns(raw) {
|
|
16316
|
-
const origins = /* @__PURE__ */ new Set();
|
|
16317
|
-
let allowAny = false;
|
|
16318
|
-
for (const entry of raw) {
|
|
16319
|
-
if (entry === "*") {
|
|
16320
|
-
allowAny = true;
|
|
16321
|
-
continue;
|
|
16322
|
-
}
|
|
16323
|
-
let parsed;
|
|
16324
|
-
try {
|
|
16325
|
-
parsed = new URL(entry);
|
|
16326
|
-
} catch {
|
|
16327
|
-
throw new InvalidAllowOriginPatternError(entry, "not a parseable URL");
|
|
16328
|
-
}
|
|
16329
|
-
const canonical = parsed.origin === "null" ? `${parsed.protocol}//${parsed.host}` : parsed.origin;
|
|
16330
|
-
if (canonical !== entry) {
|
|
16331
|
-
throw new InvalidAllowOriginPatternError(
|
|
16332
|
-
entry,
|
|
16333
|
-
`expected the canonical origin ${JSON.stringify(canonical)} without trailing slash, path, userinfo, or query`
|
|
16334
|
-
);
|
|
16335
|
-
}
|
|
16336
|
-
origins.add(canonical.toLowerCase());
|
|
16337
|
-
}
|
|
16338
|
-
return { allowAny, origins };
|
|
16339
|
-
}
|
|
16340
|
-
__name(parseAllowOriginPatterns, "parseAllowOriginPatterns");
|
|
16341
|
-
function allowOriginCors(patterns) {
|
|
16342
|
-
const allowedMethods = "GET, POST, PATCH, DELETE, OPTIONS";
|
|
16343
|
-
const allowedHeaders = "Authorization, Content-Type, X-Qwen-Client-Id, Last-Event-ID, X-Qwen-Event-Epoch";
|
|
16344
|
-
const maxAgeSeconds = "86400";
|
|
16345
|
-
const exposedHeaders = "Retry-After, X-Qwen-Event-Epoch";
|
|
16346
|
-
return (req, res, next) => {
|
|
16347
|
-
const origin = req.headers.origin;
|
|
16348
|
-
if (!origin) {
|
|
16349
|
-
next();
|
|
16350
|
-
return;
|
|
16351
|
-
}
|
|
16352
|
-
if (origin === "null") {
|
|
16353
|
-
res.setHeader("Vary", "Origin");
|
|
16354
|
-
res.status(403).json({ error: "Request denied by CORS policy" });
|
|
16355
|
-
return;
|
|
16356
|
-
}
|
|
16357
|
-
const matched = patterns.allowAny || patterns.origins.has(origin.toLowerCase());
|
|
16358
|
-
if (matched) {
|
|
16359
|
-
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
16360
|
-
res.setHeader("Vary", "Origin");
|
|
16361
|
-
res.setHeader("Access-Control-Allow-Methods", allowedMethods);
|
|
16362
|
-
res.setHeader("Access-Control-Allow-Headers", allowedHeaders);
|
|
16363
|
-
res.setHeader("Access-Control-Max-Age", maxAgeSeconds);
|
|
16364
|
-
res.setHeader("Access-Control-Expose-Headers", exposedHeaders);
|
|
16365
|
-
if (req.method === "OPTIONS" && (req.headers["access-control-request-method"] || req.headers["access-control-request-headers"])) {
|
|
16366
|
-
res.status(204).end();
|
|
16367
|
-
return;
|
|
16368
|
-
}
|
|
16369
|
-
next();
|
|
16370
|
-
return;
|
|
16371
|
-
}
|
|
16372
|
-
res.setHeader("Vary", "Origin");
|
|
16373
|
-
res.status(403).json({ error: "Request denied by CORS policy" });
|
|
16374
|
-
};
|
|
16375
|
-
}
|
|
16376
|
-
__name(allowOriginCors, "allowOriginCors");
|
|
16377
|
-
function hostAllowlist(bind, getPort) {
|
|
16378
|
-
if (!isLoopbackBind(bind)) {
|
|
16379
|
-
return (_req, _res, next) => next();
|
|
16380
|
-
}
|
|
16381
|
-
let cachedPort = -1;
|
|
16382
|
-
let cachedAllowed = /* @__PURE__ */ new Set();
|
|
16383
|
-
const allowedFor = /* @__PURE__ */ __name((port) => {
|
|
16384
|
-
if (port === cachedPort) return cachedAllowed;
|
|
16385
|
-
cachedPort = port;
|
|
16386
|
-
cachedAllowed = /* @__PURE__ */ new Set([
|
|
16387
|
-
`localhost:${port}`,
|
|
16388
|
-
`127.0.0.1:${port}`,
|
|
16389
|
-
`[::1]:${port}`,
|
|
16390
|
-
`host.docker.internal:${port}`
|
|
16391
|
-
]);
|
|
16392
|
-
if (port === 80 || port === 443) {
|
|
16393
|
-
cachedAllowed.add("localhost");
|
|
16394
|
-
cachedAllowed.add("127.0.0.1");
|
|
16395
|
-
cachedAllowed.add("[::1]");
|
|
16396
|
-
cachedAllowed.add("host.docker.internal");
|
|
16397
|
-
}
|
|
16398
|
-
return cachedAllowed;
|
|
16399
|
-
}, "allowedFor");
|
|
16400
|
-
return (req, res, next) => {
|
|
16401
|
-
const port = getPort();
|
|
16402
|
-
const host = (req.headers.host || "").toLowerCase();
|
|
16403
|
-
if (!allowedFor(port).has(host)) {
|
|
16404
|
-
res.status(403).json({ error: "Invalid Host header" });
|
|
16405
|
-
return;
|
|
16406
|
-
}
|
|
16407
|
-
next();
|
|
16408
|
-
};
|
|
16409
|
-
}
|
|
16410
|
-
__name(hostAllowlist, "hostAllowlist");
|
|
16411
|
-
function bearerAuth(token) {
|
|
16412
|
-
if (!token) {
|
|
16413
|
-
return (_req, _res, next) => next();
|
|
16414
|
-
}
|
|
16415
|
-
const expected = createHash("sha256").update(token, "utf8").digest();
|
|
16416
|
-
return (req, res, next) => {
|
|
16417
|
-
const header = req.headers.authorization;
|
|
16418
|
-
if (!header) {
|
|
16419
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
16420
|
-
return;
|
|
16421
|
-
}
|
|
16422
|
-
const schemeEnd = header.indexOf(" ");
|
|
16423
|
-
if (schemeEnd <= 0) {
|
|
16424
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
16425
|
-
return;
|
|
16426
|
-
}
|
|
16427
|
-
const scheme = header.slice(0, schemeEnd).toLowerCase();
|
|
16428
|
-
if (scheme !== "bearer") {
|
|
16429
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
16430
|
-
return;
|
|
16431
|
-
}
|
|
16432
|
-
let credStart = schemeEnd + 1;
|
|
16433
|
-
while (credStart < header.length && (header.charCodeAt(credStart) === 32 || header.charCodeAt(credStart) === 9)) {
|
|
16434
|
-
credStart++;
|
|
16435
|
-
}
|
|
16436
|
-
const credentials = header.slice(credStart);
|
|
16437
|
-
if (credentials.length === 0) {
|
|
16438
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
16439
|
-
return;
|
|
16440
|
-
}
|
|
16441
|
-
const candidate = createHash("sha256").update(credentials, "utf8").digest();
|
|
16442
|
-
if (!timingSafeEqual(candidate, expected)) {
|
|
16443
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
16444
|
-
return;
|
|
16445
|
-
}
|
|
16446
|
-
next();
|
|
16447
|
-
};
|
|
16448
|
-
}
|
|
16449
|
-
__name(bearerAuth, "bearerAuth");
|
|
16450
|
-
function createMutationGate(deps) {
|
|
16451
|
-
const passthrough = /* @__PURE__ */ __name((_req, _res, next) => next(), "passthrough");
|
|
16452
|
-
if (deps.requireAuth || deps.tokenConfigured) {
|
|
16453
|
-
return () => passthrough;
|
|
16454
|
-
}
|
|
16455
|
-
const strictDenier = /* @__PURE__ */ __name((_req, res) => {
|
|
16456
|
-
res.status(401).json({
|
|
16457
|
-
error: "This route requires the daemon to be configured with a bearer token. Set QWEN_SERVER_TOKEN or pass --token to enable bearer auth.",
|
|
16458
|
-
code: "token_required"
|
|
16459
|
-
});
|
|
16460
|
-
}, "strictDenier");
|
|
16461
|
-
return (opts = {}) => opts.strict ? strictDenier : passthrough;
|
|
16462
|
-
}
|
|
16463
|
-
__name(createMutationGate, "createMutationGate");
|
|
16464
|
-
|
|
16465
|
-
// packages/cli/src/serve/acp-http/client-mcp-sender-registry.ts
|
|
16466
|
-
init_esbuild_shims();
|
|
16467
|
-
var ClientMcpSenderRegistry = class {
|
|
16468
|
-
static {
|
|
16469
|
-
__name(this, "ClientMcpSenderRegistry");
|
|
16470
|
-
}
|
|
16471
|
-
senders = /* @__PURE__ */ new Map();
|
|
16472
|
-
sessionSenders = /* @__PURE__ */ new Map();
|
|
16473
|
-
sessionScopedServerNames = /* @__PURE__ */ new Set();
|
|
16474
|
-
/**
|
|
16475
|
-
* Record a server's WS sender, owned by `owner` (the registering
|
|
16476
|
-
* connection's stable client id). Idempotent; last writer wins and takes
|
|
16477
|
-
* ownership, so the new owner's `delete` is the one that takes effect.
|
|
16478
|
-
*/
|
|
16479
|
-
set(serverName, sender, owner) {
|
|
16480
|
-
this.senders.set(serverName, { sender, owner });
|
|
16481
|
-
}
|
|
16482
|
-
/**
|
|
16483
|
-
* Forget a server's WS sender — but only when `owner` still owns the entry.
|
|
16484
|
-
* Idempotent. The ownership guard stops a disconnecting connection from
|
|
16485
|
-
* clobbering an entry a later connection re-registered under the same name.
|
|
16486
|
-
*/
|
|
16487
|
-
delete(serverName, owner) {
|
|
16488
|
-
if (this.senders.get(serverName)?.owner === owner) {
|
|
16489
|
-
this.senders.delete(serverName);
|
|
16490
|
-
}
|
|
16491
|
-
}
|
|
16492
|
-
/** Whether `owner` currently owns the entry for `serverName`. */
|
|
16493
|
-
owns(serverName, owner) {
|
|
16494
|
-
return this.senders.get(serverName)?.owner === owner;
|
|
16495
|
-
}
|
|
16496
|
-
setSession(serverName, sessionId, sender, owner) {
|
|
16497
|
-
let bySession = this.sessionSenders.get(serverName);
|
|
16498
|
-
if (!bySession) {
|
|
16499
|
-
bySession = /* @__PURE__ */ new Map();
|
|
16500
|
-
this.sessionSenders.set(serverName, bySession);
|
|
16501
|
-
}
|
|
16502
|
-
bySession.set(sessionId, { sender, owner });
|
|
16503
|
-
this.sessionScopedServerNames.add(serverName);
|
|
16504
|
-
}
|
|
16505
|
-
ownsSession(serverName, sessionId, owner) {
|
|
16506
|
-
return this.sessionSenders.get(serverName)?.get(sessionId)?.owner === owner;
|
|
16507
|
-
}
|
|
16508
|
-
deleteSession(serverName, sessionId, owner) {
|
|
16509
|
-
const bySession = this.sessionSenders.get(serverName);
|
|
16510
|
-
if (bySession?.get(sessionId)?.owner !== owner) return false;
|
|
16511
|
-
bySession.delete(sessionId);
|
|
16512
|
-
if (bySession.size === 0) {
|
|
16513
|
-
this.sessionSenders.delete(serverName);
|
|
16514
|
-
}
|
|
16515
|
-
return true;
|
|
16516
|
-
}
|
|
16517
|
-
/** Currently-registered server names (tests / accounting). */
|
|
16518
|
-
serverNames() {
|
|
16519
|
-
return [...this.senders.keys()];
|
|
16520
|
-
}
|
|
16521
|
-
/**
|
|
16522
|
-
* The {@link ClientMcpMessageSender} the bridge consumes. Returns a
|
|
16523
|
-
* `(payload) => Promise<payload>` bound to the named server, or `undefined`
|
|
16524
|
-
* when no client currently hosts it. The bridge passes a `JSONRPCMessage` as
|
|
16525
|
-
* `payload`; we keep the public type `unknown` to match the bridge's
|
|
16526
|
-
* SDK-free contract.
|
|
16527
|
-
*/
|
|
16528
|
-
lookup = /* @__PURE__ */ __name((serverName) => {
|
|
16529
|
-
const entry = this.senders.get(serverName);
|
|
16530
|
-
const sessionEntries = this.sessionSenders.get(serverName);
|
|
16531
|
-
if (!entry && !sessionEntries) return void 0;
|
|
16532
|
-
return (payload, context) => {
|
|
16533
|
-
if (context?.sessionId) {
|
|
16534
|
-
const sessionEntry = sessionEntries?.get(context.sessionId);
|
|
16535
|
-
if (sessionEntry) return sessionEntry.sender(payload);
|
|
16536
|
-
if (this.sessionScopedServerNames.has(serverName)) {
|
|
16537
|
-
return Promise.reject(
|
|
16538
|
-
new Error(
|
|
16539
|
-
`No session-scoped MCP sender for '${serverName}' in session '${context.sessionId}'.`
|
|
16540
|
-
)
|
|
16541
|
-
);
|
|
16542
|
-
}
|
|
16543
|
-
}
|
|
16544
|
-
if (this.sessionScopedServerNames.has(serverName)) {
|
|
16545
|
-
return Promise.reject(
|
|
16546
|
-
new Error(
|
|
16547
|
-
`Session-scoped MCP server '${serverName}' requires a session context.`
|
|
16548
|
-
)
|
|
16549
|
-
);
|
|
16550
|
-
}
|
|
16551
|
-
if (!entry) {
|
|
16552
|
-
return Promise.reject(
|
|
16553
|
-
new Error(`No client MCP sender for '${serverName}'.`)
|
|
16554
|
-
);
|
|
16555
|
-
}
|
|
16556
|
-
return entry.sender(
|
|
16557
|
-
serverName,
|
|
16558
|
-
payload
|
|
16559
|
-
);
|
|
16560
|
-
};
|
|
16561
|
-
}, "lookup");
|
|
16562
|
-
};
|
|
16563
|
-
function createClientMcpServerProvider(registry, bridge, originatorClientId) {
|
|
16564
|
-
return {
|
|
16565
|
-
async registerClientMcpServer(serverName, sendSdkMcpMessage) {
|
|
16566
|
-
registry.set(serverName, sendSdkMcpMessage, originatorClientId);
|
|
16567
|
-
try {
|
|
16568
|
-
const runtimeConfig = {
|
|
16569
|
-
// SDK-type so the child binds `SdkControlClientTransport`
|
|
16570
|
-
// (`isSdkMcpServerConfig`); the flag tells the child to KEEP the
|
|
16571
|
-
// type and bind `sendSdkMcpMessage` to the reverse ext-method.
|
|
16572
|
-
type: "sdk",
|
|
16573
|
-
[CLIENT_MCP_OVER_WS_CONFIG_FLAG]: true
|
|
16574
|
-
};
|
|
16575
|
-
const result = await bridge.addRuntimeMcpServer(
|
|
16576
|
-
serverName,
|
|
16577
|
-
runtimeConfig,
|
|
16578
|
-
originatorClientId
|
|
16579
|
-
);
|
|
16580
|
-
if (result.skipped) {
|
|
16581
|
-
registry.delete(serverName, originatorClientId);
|
|
16582
|
-
throw new Error(
|
|
16583
|
-
`runtime MCP add skipped: ${result.reason ?? "unknown"}`
|
|
16584
|
-
);
|
|
16585
|
-
}
|
|
16586
|
-
if (result.shadowedSettings) {
|
|
16587
|
-
await bridge.removeRuntimeMcpServer(serverName, originatorClientId).catch(() => {
|
|
16588
|
-
});
|
|
16589
|
-
throw new Error(
|
|
16590
|
-
`client MCP server '${serverName}' conflicts with a configured MCP server`
|
|
16591
|
-
);
|
|
16592
|
-
}
|
|
16593
|
-
return { toolCount: result.toolCount };
|
|
16594
|
-
} catch (err) {
|
|
16595
|
-
registry.delete(serverName, originatorClientId);
|
|
16596
|
-
throw err;
|
|
16597
|
-
}
|
|
16598
|
-
},
|
|
16599
|
-
async unregisterClientMcpServer(serverName) {
|
|
16600
|
-
if (!registry.owns(serverName, originatorClientId)) return;
|
|
16601
|
-
registry.delete(serverName, originatorClientId);
|
|
16602
|
-
await bridge.removeRuntimeMcpServer(serverName, originatorClientId).catch(() => {
|
|
16603
|
-
});
|
|
16604
|
-
}
|
|
16605
|
-
};
|
|
16606
|
-
}
|
|
16607
|
-
__name(createClientMcpServerProvider, "createClientMcpServerProvider");
|
|
16608
|
-
|
|
16609
|
-
// packages/cli/src/serve/capabilities.ts
|
|
16610
|
-
init_esbuild_shims();
|
|
16611
|
-
var SERVE_PROTOCOL_VERSION = "v1";
|
|
16612
|
-
var SUPPORTED_SERVE_PROTOCOL_VERSIONS = [
|
|
16613
|
-
SERVE_PROTOCOL_VERSION
|
|
16614
|
-
];
|
|
16615
|
-
var SERVE_CAPABILITY_REGISTRY = {
|
|
16616
|
-
health: { since: "v1" },
|
|
16617
|
-
daemon_status: { since: "v1" },
|
|
16618
|
-
capabilities: { since: "v1" },
|
|
16619
|
-
session_create: { since: "v1" },
|
|
16620
|
-
session_scope_override: { since: "v1" },
|
|
16621
|
-
session_load: { since: "v1" },
|
|
16622
|
-
session_resume: { since: "v1" },
|
|
16623
|
-
// Deprecated alias — kept until @agentclientprotocol/sdk graduates
|
|
16624
|
-
// the underlying ACP method from unstable_resumeSession to resumeSession.
|
|
16625
|
-
unstable_session_resume: { since: "v1" },
|
|
16626
|
-
session_list: { since: "v1" },
|
|
16627
|
-
// Aggregate persisted session counts via
|
|
16628
|
-
// `GET /workspace/:id/session-info` (and the plural
|
|
16629
|
-
// `/workspaces/:workspace/session-info` twin). Performs a disk scan of
|
|
16630
|
-
// local JSONL files — advertised so clients can discover it, but the
|
|
16631
|
-
// response itself marks `expensive: true` / `cost: "disk_scan"` and
|
|
16632
|
-
// must not be polled in a tight loop.
|
|
16633
|
-
session_info: { since: "v1" },
|
|
16634
|
-
session_source_metadata: { since: "v1" },
|
|
16635
|
-
session_side_task: { since: "v1" },
|
|
16636
|
-
session_prompt: { since: "v1" },
|
|
16637
|
-
session_mid_turn_message_mutation: { since: "v1" },
|
|
16638
|
-
session_cancel: { since: "v1" },
|
|
16639
|
-
session_events: { since: "v1" },
|
|
16640
|
-
session_artifacts: { since: "v1" },
|
|
16641
|
-
session_artifacts_persistence: { since: "v1" },
|
|
16642
|
-
// Daemon emits `slow_client_warning` synthetic frames at 75% queue
|
|
16643
|
-
// fill and honors `?maxQueued=N` (range [16, 2048]) on
|
|
16644
|
-
// `GET /session/:id/events`. Old daemons silently lack both — SDK
|
|
16645
|
-
// clients pre-flight this tag before opting in.
|
|
16646
|
-
slow_client_warning: { since: "v1" },
|
|
16647
|
-
// SDK consumers can detect `KnownDaemonEvent` schema support without
|
|
16648
|
-
// pinning against this SDK release — `narrowDaemonEvent` falls back
|
|
16649
|
-
// to `kind: 'unknown'` for daemons that don't advertise the tag,
|
|
16650
|
-
// so the tag is purely informational.
|
|
16651
|
-
typed_event_schema: { since: "v1" },
|
|
16652
|
-
session_set_model: { since: "v1" },
|
|
16653
|
-
client_identity: { since: "v1" },
|
|
16654
|
-
client_heartbeat: { since: "v1" },
|
|
16655
|
-
session_permission_vote: { since: "v1" },
|
|
16656
|
-
permission_vote: { since: "v1" },
|
|
16657
|
-
workspace_mcp: { since: "v1" },
|
|
16658
|
-
workspace_skills: { since: "v1" },
|
|
16659
|
-
workspace_providers: { since: "v1" },
|
|
16660
|
-
workspace_acp_preheat: { since: "v1" },
|
|
16661
|
-
workspace_acp_status: { since: "v1" },
|
|
16662
|
-
auth_provider_install: { since: "v1" },
|
|
16663
|
-
// Workspace memory CRUD (`GET/POST /workspace/memory`). Daemon exposes
|
|
16664
|
-
// hierarchical QWEN.md state and accepts append/replace writes scoped
|
|
16665
|
-
// to either the bound workspace or the global ~/.qwen directory.
|
|
16666
|
-
workspace_memory: { since: "v1" },
|
|
16667
|
-
workspace_memory_remember: {
|
|
16668
|
-
since: "v1",
|
|
16669
|
-
modes: ["workspace", "clean"]
|
|
16670
|
-
},
|
|
16671
|
-
workspace_memory_forget: { since: "v1" },
|
|
16672
|
-
workspace_memory_dream: { since: "v1" },
|
|
16673
|
-
// Workspace agents CRUD (`GET/POST /workspace/agents` +
|
|
16674
|
-
// `GET/POST/DELETE /workspace/agents/:agentType`). Wraps
|
|
16675
|
-
// `SubagentManager` over HTTP so remote clients can list / read /
|
|
16676
|
-
// create / update / delete project- and user-level subagent
|
|
16677
|
-
// definitions. Built-in / extension agents stay read-only.
|
|
16678
|
-
workspace_agents: { since: "v1" },
|
|
16679
|
-
workspace_agent_generate: { since: "v1" },
|
|
16680
|
-
workspace_env: { since: "v1" },
|
|
16681
|
-
workspace_preflight: { since: "v1" },
|
|
16682
|
-
session_context: { since: "v1" },
|
|
16683
|
-
session_context_usage: { since: "v1" },
|
|
16684
|
-
session_supported_commands: { since: "v1" },
|
|
16685
|
-
session_tasks: { since: "v1" },
|
|
16686
|
-
session_monitor_tool_correlation: { since: "v1" },
|
|
16687
|
-
session_stats: { since: "v1" },
|
|
16688
|
-
session_lsp: { since: "v1" },
|
|
16689
|
-
session_status: { since: "v1" },
|
|
16690
|
-
session_close: { since: "v1" },
|
|
16691
|
-
session_archive: { since: "v1" },
|
|
16692
|
-
session_metadata: { since: "v1" },
|
|
16693
|
-
session_organization: { since: "v1" },
|
|
16694
|
-
session_export: { since: "v1" },
|
|
16695
|
-
session_transcript: { since: "v1" },
|
|
16696
|
-
session_transcript_pagination: { since: "v1" },
|
|
16697
|
-
// Daemon supports the MCP client guardrail surface: an in-process
|
|
16698
|
-
// counter exposed on `GET /workspace/mcp`, a `--mcp-client-budget=N`
|
|
16699
|
-
// flag with `--mcp-budget-mode={enforce, warn, off}`, and a
|
|
16700
|
-
// `disabledReason: 'budget'` tag on per-server cells when refused at
|
|
16701
|
-
// discovery. `modes` enumerates the implemented behaviors.
|
|
16702
|
-
mcp_guardrails: { since: "v1", modes: ["warn", "enforce"] },
|
|
16703
|
-
workspace_mcp_manage: { since: "v1" },
|
|
16704
|
-
// Daemon emits typed push events for MCP budget state crossings:
|
|
16705
|
-
// `mcp_budget_warning` and `mcp_child_refused_batch`. Always-on;
|
|
16706
|
-
// orthogonal to `mcp_guardrails` (the snapshot surface).
|
|
16707
|
-
mcp_guardrail_events: { since: "v1" },
|
|
16708
|
-
// Managed ACP invokes an authenticated external policy provider exactly once
|
|
16709
|
-
// at the final tool-execution boundary. Advertised only after the required
|
|
16710
|
-
// provider completed its startup handshake.
|
|
16711
|
-
external_tool_guard: { since: "v1", modes: ["required"] },
|
|
16712
|
-
// Always-on. Daemon supports runtime MCP server mutation via
|
|
16713
|
-
// `POST /workspace/mcp/servers` (add) and
|
|
16714
|
-
// `DELETE /workspace/mcp/servers/:name` (remove). SDK clients
|
|
16715
|
-
// pre-flight this tag before calling those routes.
|
|
16716
|
-
mcp_server_runtime_mutation: { since: "v1" },
|
|
16717
|
-
// Daemon supports the read-only workspace file surface:
|
|
16718
|
-
// `GET /file`, `GET /list`, `GET /glob`, `GET /stat`. The four
|
|
16719
|
-
// routes are gated as a single feature because they share the same
|
|
16720
|
-
// backing `WorkspaceFileSystem` boundary and failure shape.
|
|
16721
|
-
workspace_file_read: { since: "v1" },
|
|
16722
|
-
// Daemon supports bounded raw byte reads via `GET /file/bytes`.
|
|
16723
|
-
// Separate from `workspace_file_read` because older daemons may
|
|
16724
|
-
// advertise the text/list/stat/glob surface without byte-window
|
|
16725
|
-
// support.
|
|
16726
|
-
workspace_file_bytes: { since: "v1" },
|
|
16727
|
-
// Daemon supports byte-cursor paging on `GET /file`: responses carry
|
|
16728
|
-
// `nextCursor`/`hasMore` and requests accept `cursor`. A separate tag from
|
|
16729
|
-
// `workspace_file_read` because the convention here is that new behavior
|
|
16730
|
-
// gets a new tag — a client that preflighted the old one must not silently
|
|
16731
|
-
// receive a surface it cannot recognise. Same split as
|
|
16732
|
-
// `workspace_file_bytes` from `workspace_file_read`, and
|
|
16733
|
-
// `session_transcript_pagination` from `session_transcript`.
|
|
16734
|
-
workspace_file_read_cursor: { since: "v1" },
|
|
16735
|
-
// Daemon supports hash-aware text mutation routes
|
|
16736
|
-
// (`POST /file/write`, `POST /file/edit`) behind the strict mutation
|
|
16737
|
-
// gate. Clients should still pre-flight `require_auth` separately for
|
|
16738
|
-
// deployment posture; this tag only means the route contract exists.
|
|
16739
|
-
workspace_file_write: { since: "v1" },
|
|
16740
|
-
// Daemon hosts the session-level approval-mode
|
|
16741
|
-
// control route `POST /session/:id/approval-mode` (gated by the
|
|
16742
|
-
// mutation gate, strict). The route accepts `{mode, persist?}` —
|
|
16743
|
-
// `persist:true` also writes `tools.approvalMode` to workspace
|
|
16744
|
-
// settings via the daemon's `loadedSettings` handle. SDK helper:
|
|
16745
|
-
// `DaemonClient.setSessionApprovalMode`.
|
|
16746
|
-
session_approval_mode_control: { since: "v1" },
|
|
16747
|
-
// `POST /workspace/tools/:name/enable` toggles a
|
|
16748
|
-
// tool name in the workspace's `tools.disabled` settings list. The
|
|
16749
|
-
// bridge writes the settings file directly (no ACP roundtrip) and
|
|
16750
|
-
// fan-outs a `tool_toggled` event to all live session SSE buses.
|
|
16751
|
-
// Already-registered tools in active sessions are NOT retroactively
|
|
16752
|
-
// unregistered — the toggle takes effect on the next ACP child spawn
|
|
16753
|
-
// (`tools.disabled` is consulted at `Config` construction time).
|
|
16754
|
-
workspace_tool_toggle: { since: "v1" },
|
|
16755
|
-
workspace_skill_toggle: { since: "v1" },
|
|
16756
|
-
workspace_skill_manage: { since: "v1" },
|
|
16757
|
-
workspace_settings: { since: "v1" },
|
|
16758
|
-
// `GET /workspace/permissions` is always available when this tag is
|
|
16759
|
-
// advertised. `POST /workspace/permissions` updates the active ACP
|
|
16760
|
-
// child and returns `permission_session_required` when no live ACP
|
|
16761
|
-
// session exists; the tag means the route contract exists, not that
|
|
16762
|
-
// the current daemon state can accept a write.
|
|
16763
|
-
workspace_permissions: { since: "v1" },
|
|
16764
|
-
workspace_voice: { since: "v1" },
|
|
16765
|
-
workspace_voice_transcription: { since: "v1", modes: ["batch"] },
|
|
16766
|
-
// Inspect bound workspace trust and request local operator action.
|
|
16767
|
-
// Remote clients cannot directly write trustedFolders.json.
|
|
16768
|
-
workspace_trust: { since: "v1" },
|
|
16769
|
-
// Workspace trust policy changes rebuild the affected runtime generation
|
|
16770
|
-
// without restarting the daemon. V2 trust status exposes convergence.
|
|
16771
|
-
workspace_trust_hot_reload: { since: "v1" },
|
|
16772
|
-
// `POST /workspace/init` scaffolds an empty
|
|
16773
|
-
// `QWEN.md` (or whatever `getCurrentGeminiMdFilename()` returns) at
|
|
16774
|
-
// the bound workspace root. Body: `{force?: boolean}`. Default
|
|
16775
|
-
// refuses with 409 when the file already exists; `force: true`
|
|
16776
|
-
// overwrites. Mechanical only — does NOT call the LLM. To AI-fill
|
|
16777
|
-
// the file, the caller should follow up with
|
|
16778
|
-
// `POST /session/:id/prompt`.
|
|
16779
|
-
workspace_init: { since: "v1" },
|
|
16780
|
-
// `POST /workspace/setup-github` installs the fixed
|
|
16781
|
-
// qwen-code-action workflow set into the bound workspace after
|
|
16782
|
-
// explicit consent. The route reuses the interactive `/setup-github`
|
|
16783
|
-
// release lookup, workflow download, and `.gitignore` update logic.
|
|
16784
|
-
workspace_github_setup: { since: "v1" },
|
|
16785
|
-
// `GET /workspaces/:workspace/github/prs` lists the open pull requests
|
|
16786
|
-
// of the workspace's GitHub remote via the `gh` CLI (read-only). The
|
|
16787
|
-
// tag means the route contract exists; runtime availability of `gh`
|
|
16788
|
-
// and its auth is reported per-request through the
|
|
16789
|
-
// `github_cli_unavailable` / `github_prs_failed` error codes.
|
|
16790
|
-
workspace_github_prs: { since: "v1" },
|
|
16791
|
-
// `POST /workspace/mcp/:server/restart` performs
|
|
16792
|
-
// a single-server MCP restart (disconnect + reconnect + rediscover)
|
|
16793
|
-
// through the ACP child's `McpClientManager`. Pre-checks the live
|
|
16794
|
-
// budget snapshot: when the target server is not
|
|
16795
|
-
// already in `reservedSlots` AND the live count would exceed the
|
|
16796
|
-
// configured budget under `enforce` mode, returns 200 with
|
|
16797
|
-
// `{restarted:false, skipped:true, reason:'budget_would_exceed'}`
|
|
16798
|
-
// rather than triggering a refusal cascade. Other skip reasons:
|
|
16799
|
-
// `'in_flight'` (concurrent discovery in progress), `'disabled'`
|
|
16800
|
-
// (server is configured but explicitly disabled).
|
|
16801
|
-
workspace_mcp_restart: { since: "v1" },
|
|
16802
|
-
// Daemon hosts `POST /session/:id/recap`, which
|
|
16803
|
-
// generates a one-sentence "where did I leave off" summary by
|
|
16804
|
-
// running `generateSessionRecap` (`core/services/sessionRecap.ts`) as
|
|
16805
|
-
// a side-query against the fast model. Non-strict mutation gate —
|
|
16806
|
-
// posture mirrors `/session/:id/prompt` (token cost, not state
|
|
16807
|
-
// mutation). The route returns `{sessionId, recap}` where `recap`
|
|
16808
|
-
// may be `null` for too-short histories or transient model failures
|
|
16809
|
-
// (best-effort, never throws). SDK helper: `DaemonClient.recapSession`.
|
|
16810
|
-
session_recap: { since: "v1" },
|
|
16811
|
-
// `POST /session/:id/generate` streams a stateless, tool-free model call.
|
|
16812
|
-
// The ACP child prefers fastModel and falls back to the main session model.
|
|
16813
|
-
session_generation: { since: "v1" },
|
|
16814
|
-
// `POST /workspace/generate` runs the same stateless, tool-free generation
|
|
16815
|
-
// protocol against the resolved workspace runtime without a live session.
|
|
16816
|
-
workspace_generation: { since: "v1" },
|
|
16817
|
-
// Side question (/btw) against the session's conversation context.
|
|
16818
|
-
// Single-turn, tool-free LLM call via runForkedAgent (cache path).
|
|
16819
|
-
session_btw: { since: "v1" },
|
|
16820
|
-
// Direct daemon-side shell execution for an existing session.
|
|
16821
|
-
// Advertised CONDITIONALLY: operators must explicitly enable it and
|
|
16822
|
-
// configure bearer auth. Clients must still send a session-bound
|
|
16823
|
-
// X-Qwen-Client-Id when calling the route.
|
|
16824
|
-
session_shell_command: { since: "v1" },
|
|
16825
|
-
// Daemon hosts a workspace-shared MCP transport
|
|
16826
|
-
// pool (`QwenAgent.mcpPool`); `GET /workspace/mcp` reflects pool-level
|
|
16827
|
-
// accounting (`entryCount`, `entrySummary` on each per-server cell).
|
|
16828
|
-
// Advertised CONDITIONALLY — the kill switch
|
|
16829
|
-
// `QWEN_SERVE_NO_MCP_POOL=1` env var falls back to per-session MCP
|
|
16830
|
-
// clients and the tag is omitted so SDK consumers
|
|
16831
|
-
// pre-flighting on the tag get accurate "pool is on" semantics.
|
|
16832
|
-
mcp_workspace_pool: { since: "v1" },
|
|
16833
|
-
// `POST /workspace/mcp/:server/restart`
|
|
16834
|
-
// accepts an optional `?entryIndex=N` (or `*`) query parameter
|
|
16835
|
-
// and may return the new `{entries: RestartResult[]}` shape when
|
|
16836
|
-
// the pool holds multiple entries for the same server name (e.g.
|
|
16837
|
-
// sessions injected divergent OAuth headers). Single-entry
|
|
16838
|
-
// restarts continue to return the legacy `{restarted, durationMs}`
|
|
16839
|
-
// shape for compatibility with pre-F2 SDK clients. Advertised
|
|
16840
|
-
// CONDITIONALLY in lockstep with `mcp_workspace_pool`: pool
|
|
16841
|
-
// off → both tags absent, pool on → both tags present. Operators
|
|
16842
|
-
// pre-flighting on this tag can branch on whether the response
|
|
16843
|
-
// shape may include `entries[]`.
|
|
16844
|
-
mcp_pool_restart: { since: "v1" },
|
|
16845
|
-
// Daemon was booted with `--require-auth` (or
|
|
16846
|
-
// `requireAuth: true`), so even loopback callers must carry a bearer
|
|
16847
|
-
// token. Advertised CONDITIONALLY — only when the flag is on — so
|
|
16848
|
-
// SDK clients can branch on its presence to surface a clear "this
|
|
16849
|
-
// deployment requires auth" hint instead of speculatively trying
|
|
16850
|
-
// requests and parsing the resulting 401 body. Loopback developer
|
|
16851
|
-
// defaults (no flag) omit the tag, preserving the bit-for-bit shape
|
|
16852
|
-
// older clients expect.
|
|
16853
|
-
require_auth: { since: "v1" },
|
|
16854
|
-
// Daemon was booted with `--allow-origin <pattern>`
|
|
16855
|
-
// (at least one entry, including the `*` literal). Advertised
|
|
16856
|
-
// CONDITIONALLY — only when the flag is set — so browser SDK clients
|
|
16857
|
-
// can pre-flight whether the daemon will honor their cross-origin
|
|
16858
|
-
// request before issuing it (and parsing a 403). The configured
|
|
16859
|
-
// pattern list is intentionally NOT echoed in the capabilities
|
|
16860
|
-
// envelope — browser webui knows its own origin, and surfacing the
|
|
16861
|
-
// list would let an unauthenticated `/capabilities` reader
|
|
16862
|
-
// enumerate every trusted origin, which is useful recon for a
|
|
16863
|
-
// misconfigured deployment.
|
|
16864
|
-
allow_origin: { since: "v1" },
|
|
16865
|
-
// Daemon exposes the device-flow auth surface
|
|
16866
|
-
// (`POST /workspace/auth/device-flow`, GET/DELETE on `/:id`, and
|
|
16867
|
-
// `GET /workspace/auth/status`). Advertised UNCONDITIONALLY: the
|
|
16868
|
-
// routes themselves return `400 unsupported_provider` if the daemon
|
|
16869
|
-
// can't satisfy a specific provider, so clients always probe via the
|
|
16870
|
-
// route. The list of supported providers is surfaced through the
|
|
16871
|
-
// status route (extension data on `/capabilities` would inflate the
|
|
16872
|
-
// descriptor shape; we keep the registry uniform).
|
|
16873
|
-
auth_device_flow: { since: "v1" },
|
|
16874
|
-
permission_mediation: {
|
|
16875
|
-
since: "v1",
|
|
16876
|
-
modes: ["first-responder", "designated", "consensus", "local-only"]
|
|
16877
|
-
},
|
|
16878
|
-
prompt_absolute_deadline: { since: "v1" },
|
|
16879
|
-
writer_idle_timeout: { since: "v1" },
|
|
16880
|
-
non_blocking_prompt: { since: "v1" },
|
|
16881
|
-
session_language: { since: "v1" },
|
|
16882
|
-
session_rewind: { since: "v1" },
|
|
16883
|
-
workspace_hooks: { since: "v1" },
|
|
16884
|
-
session_hooks: { since: "v1" },
|
|
16885
|
-
workspace_extensions: { since: "v1" },
|
|
16886
|
-
session_branch: { since: "v1" },
|
|
16887
|
-
rate_limit: { since: "v1" },
|
|
16888
|
-
workspace_reload: { since: "v1" },
|
|
16889
|
-
// Immediate best-effort channel delivery for prompt/scheduled finals and
|
|
16890
|
-
// direct workspace notifications. Presence describes the route/event
|
|
16891
|
-
// contract; current worker availability is reported per delivery.
|
|
16892
|
-
channel_delivery: { since: "v1" },
|
|
16893
|
-
// Daemon supports reloading its daemon-managed channel worker via
|
|
16894
|
-
// `POST /workspace/channel/reload`. The worker is stopped and relaunched;
|
|
16895
|
-
// on relaunch it re-reads settings.json (channels / proxy / per-channel
|
|
16896
|
-
// model), so channel settings changes apply without a full daemon restart.
|
|
16897
|
-
// Advertised CONDITIONALLY while the runtime manager has a committed or
|
|
16898
|
-
// recoverable channel worker selection.
|
|
16899
|
-
channel_reload: { since: "v1" },
|
|
16900
|
-
// Runtime GET/PUT/DELETE control for daemon-managed channel selection.
|
|
16901
|
-
// The route exists even when no selection was supplied at daemon boot.
|
|
16902
|
-
channel_control: { since: "v1" },
|
|
16903
|
-
// Sanitized workspace Channel configuration, lifecycle, and pairing.
|
|
16904
|
-
channel_management: { since: "v1" },
|
|
16905
|
-
// Read-only workspace graph of recently observed channel contacts.
|
|
16906
|
-
workspace_channel_observed_contacts: { since: "v1" },
|
|
16907
|
-
// Multi-workspace session routing. Advertised only when one daemon hosts
|
|
16908
|
-
// more than one registered workspace runtime.
|
|
16909
|
-
multi_workspace_sessions: { since: "v1" },
|
|
16910
|
-
// Singular session rewind routes resolve the owning live workspace runtime.
|
|
16911
|
-
multi_workspace_session_rewind: { since: "v1" },
|
|
16912
|
-
// Singular session shell routes resolve the owning live workspace runtime.
|
|
16913
|
-
multi_workspace_session_shell: { since: "v1" },
|
|
16914
|
-
dynamic_workspace_registration: { since: "v1" },
|
|
16915
|
-
persistent_workspace_registration: { since: "v1" },
|
|
16916
|
-
// Optional presentation-only names and updates to those names for workspace
|
|
16917
|
-
// runtimes. Workspace ids and canonical paths remain the routing identities.
|
|
16918
|
-
workspace_display_name: { since: "v1" },
|
|
16919
|
-
scratch_workspace_registration: { since: "v1" },
|
|
16920
|
-
workspace_runtime_removal: { since: "v1" },
|
|
16921
|
-
// Workspace-qualified core REST routes under `/workspaces/:workspace/...`.
|
|
16922
|
-
// Covers core file/status/permissions/trust/lifecycle/MCP/tool, memory,
|
|
16923
|
-
// workspace agent CRUD, and persisted session organization surfaces.
|
|
16924
|
-
// Workspace-qualified settings also require the existing
|
|
16925
|
-
// `workspace_settings` tag because that surface depends on settings
|
|
16926
|
-
// persistence. ACP/WebSocket and auth stay outside this core tag;
|
|
16927
|
-
// workspace-qualified Voice REST/WebSocket routes use their separate
|
|
16928
|
-
// `workspace_qualified_voice` capability below. V2 extension management
|
|
16929
|
-
// is advertised separately via `extension_management_v2`.
|
|
16930
|
-
workspace_qualified_rest_core: { since: "v1" },
|
|
16931
|
-
// Workspace-qualified Voice REST and WebSocket routes. This tag is enough
|
|
16932
|
-
// to discover plural modalities because legacy Voice tags describe only
|
|
16933
|
-
// the primary runtime and may be absent for a secondary-only setup.
|
|
16934
|
-
workspace_qualified_voice: { since: "v1" },
|
|
16935
|
-
// Workspace-qualified managed-memory routes
|
|
16936
|
-
// (`/workspaces/:workspace/memory/{remember,forget,dream}`). Each
|
|
16937
|
-
// registered workspace gets its own task lane; the primary lane is the
|
|
16938
|
-
// same instance as the singular `/workspace/memory` surface.
|
|
16939
|
-
workspace_qualified_memory: { since: "v1" },
|
|
16940
|
-
// Global extension catalog/mutations plus workspace-qualified activation
|
|
16941
|
-
// projections. This is additive to the legacy primary-workspace
|
|
16942
|
-
// `workspace_extensions` contract.
|
|
16943
|
-
extension_management_v2: { since: "v1" },
|
|
16944
|
-
// Workspace-qualified, daemon-local persisted transcript paging. The tag is
|
|
16945
|
-
// unconditional because the route also serves a trusted single-workspace
|
|
16946
|
-
// primary; authorization is evaluated for the selected runtime per request.
|
|
16947
|
-
workspace_persisted_transcript: { since: "v1" },
|
|
16948
|
-
// Workspace-qualified full session export from active persisted storage.
|
|
16949
|
-
// This is separate from `session_export` so clients do not infer the plural
|
|
16950
|
-
// route from the legacy primary-workspace export capability.
|
|
16951
|
-
workspace_session_export: { since: "v1" },
|
|
16952
|
-
// Workspace-qualified full session export from archived persisted storage.
|
|
16953
|
-
// This remains independent from active export so older daemons cannot ignore
|
|
16954
|
-
// archive intent and return an active transcript with the same session id.
|
|
16955
|
-
workspace_archived_session_export: { since: "v1" },
|
|
16956
|
-
// Workspace-qualified ACP transport (issue #6378 Phase 4):
|
|
16957
|
-
// `/workspaces/:workspace/acp` mounts a per-runtime ACP dispatcher (HTTP +
|
|
16958
|
-
// WebSocket) for each registered workspace, with per-runtime device-flow and
|
|
16959
|
-
// reverse client-MCP. Legacy `/acp` stays bound to the primary runtime.
|
|
16960
|
-
// Advertised only when the daemon hosts more than one workspace runtime.
|
|
16961
|
-
workspace_qualified_acp: { since: "v1" },
|
|
16962
|
-
// Phase 2 "reverse tool channel" (issue #5626). A connected WS client (e.g.
|
|
16963
|
-
// the Chrome extension) can host an MCP server that the daemon's agent
|
|
16964
|
-
// calls by carrying `mcp_message` JSON-RPC frames over the daemon WS,
|
|
16965
|
-
// reusing the SDK-MCP-server control-plane pattern. Inbound WS frame types:
|
|
16966
|
-
// `mcp_register` { server }, `mcp_message` { id, server, payload }
|
|
16967
|
-
// (bidirectional, request/response correlated by `id`), `mcp_unregister`
|
|
16968
|
-
// { server }. Advertised CONDITIONALLY so clients pre-flight this tag before
|
|
16969
|
-
// attempting to register a client-hosted server. `runQwenServe` enables it
|
|
16970
|
-
// only when explicitly requested by option or env.
|
|
16971
|
-
client_mcp_over_ws: { since: "v1" },
|
|
16972
|
-
// Plan C "CDP tunnel" (issue #5626): the daemon exposes a `/cdp` WebSocket
|
|
16973
|
-
// where a loopback CDP client drives ONE real tab
|
|
16974
|
-
// via the extension's `chrome.debugger`, tunneled over `/acp` as `cdp_*`
|
|
16975
|
-
// frames. Advertised when explicitly enabled or when the daemon is serving a
|
|
16976
|
-
// Chrome extension origin.
|
|
16977
|
-
cdp_tunnel_over_ws: { since: "v1" },
|
|
16978
|
-
// Browser automation MCP tools are available only when the CDP tunnel is on
|
|
16979
|
-
// and the operator has configured an external stdio adapter via
|
|
16980
|
-
// QWEN_CDP_MCP_COMMAND. This is separate from `cdp_tunnel_over_ws`: a daemon
|
|
16981
|
-
// may expose the tunnel while intentionally not bundling/registering a
|
|
16982
|
-
// chrome-devtools MCP adapter.
|
|
16983
|
-
browser_automation_mcp: { since: "v1" },
|
|
16984
|
-
// Daemon hosts the `/voice/stream` WebSocket: the browser captures audio and
|
|
16985
|
-
// streams raw PCM, the daemon transcribes server-side via the configured
|
|
16986
|
-
// `voiceModel` (credentials never reach the client). Advertised
|
|
16987
|
-
// UNCONDITIONALLY (like `auth_device_flow`): presence means the endpoint
|
|
16988
|
-
// exists, not that a voice model is configured. The WS returns an `error`
|
|
16989
|
-
// frame when no transcribable `voiceModel` is set, so clients probe by
|
|
16990
|
-
// connecting rather than reading ambient settings into `/capabilities` (which
|
|
16991
|
-
// would make the envelope depend on the user's home config). `modes`
|
|
16992
|
-
// enumerates the two transcription paths (realtime vs. on-stop batch).
|
|
16993
|
-
voice_transcribe: { since: "v1", modes: ["streaming", "batch"] },
|
|
16994
|
-
// Process-global Live Voice control plane. Advertisement requires a macOS
|
|
16995
|
-
// WebShell daemon with native Host integration and the hot-applied enabled
|
|
16996
|
-
// gate. `/live/status` remains the dynamic readiness surface for the Host,
|
|
16997
|
-
// permissions, self-checks, and provider reachability.
|
|
16998
|
-
realtime_voice: { since: "v1" }
|
|
16999
|
-
};
|
|
17000
|
-
var CONDITIONAL_SERVE_FEATURES = /* @__PURE__ */ new Map([
|
|
17001
|
-
["require_auth", (toggles) => toggles.requireAuth === true],
|
|
17002
|
-
["mcp_workspace_pool", (toggles) => toggles.mcpPoolActive === true],
|
|
17003
|
-
["mcp_pool_restart", (toggles) => toggles.mcpPoolActive === true],
|
|
17004
|
-
[
|
|
17005
|
-
"external_tool_guard",
|
|
17006
|
-
(toggles) => toggles.externalToolGuardActive === true
|
|
17007
|
-
],
|
|
17008
|
-
["allow_origin", (toggles) => toggles.allowOriginActive === true],
|
|
17009
|
-
[
|
|
17010
|
-
"prompt_absolute_deadline",
|
|
17011
|
-
(toggles) => typeof toggles.promptDeadlineMs === "number" && toggles.promptDeadlineMs > 0
|
|
17012
|
-
],
|
|
17013
|
-
[
|
|
17014
|
-
"writer_idle_timeout",
|
|
17015
|
-
(toggles) => typeof toggles.writerIdleTimeoutMs === "number" && toggles.writerIdleTimeoutMs > 0
|
|
17016
|
-
],
|
|
17017
|
-
["workspace_settings", (toggles) => toggles.persistSettingAvailable === true],
|
|
17018
|
-
["workspace_voice", (toggles) => toggles.persistSettingAvailable === true],
|
|
17019
|
-
[
|
|
17020
|
-
"workspace_voice_transcription",
|
|
17021
|
-
(toggles) => toggles.voiceTranscriptionAvailable === true
|
|
17022
|
-
],
|
|
17023
|
-
[
|
|
17024
|
-
"session_shell_command",
|
|
17025
|
-
(toggles) => toggles.sessionShellCommandEnabled === true
|
|
17026
|
-
],
|
|
17027
|
-
[
|
|
17028
|
-
"session_artifacts_persistence",
|
|
17029
|
-
(toggles) => toggles.sessionArtifactsPersistenceAvailable === true
|
|
17030
|
-
],
|
|
17031
|
-
[
|
|
17032
|
-
"session_generation",
|
|
17033
|
-
(toggles) => toggles.sessionGenerationAvailable === true
|
|
17034
|
-
],
|
|
17035
|
-
[
|
|
17036
|
-
"workspace_generation",
|
|
17037
|
-
(toggles) => toggles.workspaceGenerationAvailable === true
|
|
17038
|
-
],
|
|
17039
|
-
["rate_limit", (toggles) => toggles.rateLimit === true],
|
|
17040
|
-
["workspace_reload", (toggles) => toggles.reloadAvailable === true],
|
|
17041
|
-
[
|
|
17042
|
-
"workspace_trust_hot_reload",
|
|
17043
|
-
(toggles) => toggles.workspaceTrustHotReloadAvailable === true
|
|
17044
|
-
],
|
|
17045
|
-
["channel_reload", (toggles) => toggles.channelReloadAvailable === true],
|
|
17046
|
-
["channel_control", (toggles) => toggles.channelControlAvailable === true],
|
|
17047
|
-
[
|
|
17048
|
-
"channel_management",
|
|
17049
|
-
(toggles) => toggles.channelManagementAvailable === true
|
|
17050
|
-
],
|
|
17051
|
-
[
|
|
17052
|
-
"multi_workspace_sessions",
|
|
17053
|
-
(toggles) => toggles.multiWorkspaceSessionsEnabled === true
|
|
17054
|
-
],
|
|
17055
|
-
[
|
|
17056
|
-
"multi_workspace_session_rewind",
|
|
17057
|
-
(toggles) => toggles.multiWorkspaceSessionsEnabled === true
|
|
17058
|
-
],
|
|
17059
|
-
[
|
|
17060
|
-
"multi_workspace_session_shell",
|
|
17061
|
-
(toggles) => toggles.multiWorkspaceSessionsEnabled === true && toggles.sessionShellCommandEnabled === true
|
|
17062
|
-
],
|
|
17063
|
-
[
|
|
17064
|
-
"dynamic_workspace_registration",
|
|
17065
|
-
(toggles) => toggles.dynamicWorkspaceRegistrationAvailable === true
|
|
17066
|
-
],
|
|
17067
|
-
[
|
|
17068
|
-
"persistent_workspace_registration",
|
|
17069
|
-
(toggles) => toggles.persistentWorkspaceRegistrationAvailable === true
|
|
17070
|
-
],
|
|
17071
|
-
[
|
|
17072
|
-
"scratch_workspace_registration",
|
|
17073
|
-
(toggles) => toggles.scratchWorkspaceRegistrationAvailable === true
|
|
17074
|
-
],
|
|
17075
|
-
[
|
|
17076
|
-
"workspace_runtime_removal",
|
|
17077
|
-
(toggles) => toggles.workspaceRuntimeRemovalAvailable === true
|
|
17078
|
-
],
|
|
17079
|
-
[
|
|
17080
|
-
"workspace_qualified_acp",
|
|
17081
|
-
// The plural routes are pre-mounted for workspaces registered after app
|
|
17082
|
-
// creation, but the capability becomes meaningful only once a secondary
|
|
17083
|
-
// runtime exists. Until then the qualified primary route is only an alias
|
|
17084
|
-
// for the always-available legacy `/acp` surface.
|
|
17085
|
-
(toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
|
|
17086
|
-
],
|
|
17087
|
-
[
|
|
17088
|
-
"workspace_qualified_voice",
|
|
17089
|
-
// Like qualified ACP, the plural Voice surface is mounted ahead of time
|
|
17090
|
-
// but only becomes useful once the daemon has a secondary runtime.
|
|
17091
|
-
(toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
|
|
17092
|
-
],
|
|
17093
|
-
[
|
|
17094
|
-
"workspace_qualified_memory",
|
|
17095
|
-
(toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
|
|
17096
|
-
],
|
|
17097
|
-
["client_mcp_over_ws", (toggles) => toggles.clientMcpOverWsEnabled === true],
|
|
17098
|
-
["cdp_tunnel_over_ws", (toggles) => toggles.cdpTunnelOverWsEnabled === true],
|
|
17099
|
-
[
|
|
17100
|
-
"browser_automation_mcp",
|
|
17101
|
-
(toggles) => toggles.browserAutomationMcpAvailable === true
|
|
17102
|
-
],
|
|
17103
|
-
[
|
|
17104
|
-
// Advertised whenever the `/voice/stream` WS endpoint exists. A configured
|
|
17105
|
-
// token (or `--require-auth`) no longer suppresses it: browsers can't set
|
|
17106
|
-
// an `Authorization` header on a WebSocket, so the Web Shell carries the
|
|
17107
|
-
// bearer token in the `Sec-WebSocket-Protocol` subprotocol, which the ACP
|
|
17108
|
-
// upgrade listener verifies (see acp-http/index.ts).
|
|
17109
|
-
"voice_transcribe",
|
|
17110
|
-
(toggles) => toggles.voiceWsAvailable !== false
|
|
17111
|
-
],
|
|
17112
|
-
[
|
|
17113
|
-
"realtime_voice",
|
|
17114
|
-
(toggles) => toggles.acpHttpEnabled === true && toggles.realtimeVoiceEnabled === true
|
|
17115
|
-
]
|
|
17116
|
-
]);
|
|
17117
|
-
var SERVE_FEATURES = Object.freeze(
|
|
17118
|
-
Object.keys(SERVE_CAPABILITY_REGISTRY)
|
|
17119
|
-
);
|
|
17120
|
-
function serveProtocolVersionIndex(version) {
|
|
17121
|
-
return SUPPORTED_SERVE_PROTOCOL_VERSIONS.indexOf(version);
|
|
17122
|
-
}
|
|
17123
|
-
__name(serveProtocolVersionIndex, "serveProtocolVersionIndex");
|
|
17124
|
-
function isFeatureAvailableInProtocol(feature, protocolVersion) {
|
|
17125
|
-
return serveProtocolVersionIndex(SERVE_CAPABILITY_REGISTRY[feature].since) <= serveProtocolVersionIndex(protocolVersion);
|
|
17126
|
-
}
|
|
17127
|
-
__name(isFeatureAvailableInProtocol, "isFeatureAvailableInProtocol");
|
|
17128
|
-
function getAdvertisedServeFeatures(protocolVersion = SERVE_PROTOCOL_VERSION, toggles = {}) {
|
|
17129
|
-
return SERVE_FEATURES.filter((feature) => {
|
|
17130
|
-
if (!isFeatureAvailableInProtocol(feature, protocolVersion)) return false;
|
|
17131
|
-
const predicate = CONDITIONAL_SERVE_FEATURES.get(feature);
|
|
17132
|
-
if (predicate !== void 0) return predicate(toggles);
|
|
17133
|
-
return true;
|
|
17134
|
-
});
|
|
17135
|
-
}
|
|
17136
|
-
__name(getAdvertisedServeFeatures, "getAdvertisedServeFeatures");
|
|
17137
|
-
function getServeProtocolVersions() {
|
|
17138
|
-
return {
|
|
17139
|
-
current: SERVE_PROTOCOL_VERSION,
|
|
17140
|
-
supported: [...SUPPORTED_SERVE_PROTOCOL_VERSIONS]
|
|
17141
|
-
};
|
|
17142
|
-
}
|
|
17143
|
-
__name(getServeProtocolVersions, "getServeProtocolVersions");
|
|
17144
|
-
|
|
17145
|
-
// packages/cli/src/serve/types.ts
|
|
17146
|
-
init_esbuild_shims();
|
|
17147
|
-
var CAPABILITIES_SCHEMA_VERSION = 1;
|
|
17148
|
-
|
|
17149
|
-
// packages/cli/src/serve/managed-scratch-workspace.ts
|
|
17150
|
-
init_esbuild_shims();
|
|
17151
|
-
import { lstatSync, mkdirSync, realpathSync } from "node:fs";
|
|
17152
|
-
import { lstat, mkdtemp } from "node:fs/promises";
|
|
17153
|
-
import {
|
|
17154
|
-
basename as basename2,
|
|
17155
|
-
dirname as dirname2,
|
|
17156
|
-
isAbsolute,
|
|
17157
|
-
join as join2,
|
|
17158
|
-
relative,
|
|
17159
|
-
resolve
|
|
17160
|
-
} from "node:path";
|
|
17161
|
-
var isSamePath = /* @__PURE__ */ __name((left, right) => process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right, "isSamePath");
|
|
17162
|
-
var isWithinRoot = /* @__PURE__ */ __name((candidate, root) => {
|
|
17163
|
-
const rel = relative(root, candidate);
|
|
17164
|
-
return rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
|
|
17165
|
-
}, "isWithinRoot");
|
|
17166
|
-
function isManagedScratchChild(canonicalCwd, canonicalRoot) {
|
|
17167
|
-
return isSamePath(dirname2(canonicalCwd), canonicalRoot) && /^scratch-.+/.test(basename2(canonicalCwd));
|
|
17168
|
-
}
|
|
17169
|
-
__name(isManagedScratchChild, "isManagedScratchChild");
|
|
17170
|
-
function isScratchRootCompatible(canonicalCwd, canonicalRoot) {
|
|
17171
|
-
if (isSamePath(canonicalCwd, canonicalRoot)) return false;
|
|
17172
|
-
if (isManagedScratchChild(canonicalCwd, canonicalRoot)) return true;
|
|
17173
|
-
return !isWithinRoot(canonicalCwd, canonicalRoot) && !isWithinRoot(canonicalRoot, canonicalCwd);
|
|
17174
|
-
}
|
|
17175
|
-
__name(isScratchRootCompatible, "isScratchRootCompatible");
|
|
17176
|
-
function validateRootStats(stats) {
|
|
17177
|
-
if (stats.isSymbolicLink() || !stats.isDirectory()) {
|
|
17178
|
-
throw new Error("Managed scratch root must be a non-symlink directory");
|
|
17179
|
-
}
|
|
17180
|
-
if (process.platform !== "win32" && typeof process.getuid === "function" && stats.uid !== process.getuid()) {
|
|
17181
|
-
throw new Error("Managed scratch root must be owned by the daemon user");
|
|
17182
|
-
}
|
|
17183
|
-
if (process.platform !== "win32" && (stats.mode & 63) !== 0) {
|
|
17184
|
-
throw new Error(
|
|
17185
|
-
"Managed scratch root must be accessible only to its owner"
|
|
17186
|
-
);
|
|
17187
|
-
}
|
|
17188
|
-
}
|
|
17189
|
-
__name(validateRootStats, "validateRootStats");
|
|
17190
|
-
function prepareManagedScratchRoot(root, startupWorkspaceCwds) {
|
|
17191
|
-
mkdirSync(root, { recursive: true, mode: 448 });
|
|
17192
|
-
const before = lstatSync(root);
|
|
17193
|
-
validateRootStats(before);
|
|
17194
|
-
const canonicalRoot = realpathSync.native(resolve(root));
|
|
17195
|
-
const after = lstatSync(canonicalRoot);
|
|
17196
|
-
validateRootStats(after);
|
|
17197
|
-
if (before.dev !== after.dev || before.ino !== after.ino) {
|
|
17198
|
-
throw new Error("Managed scratch root identity changed during validation");
|
|
17199
|
-
}
|
|
17200
|
-
if (startupWorkspaceCwds.some(
|
|
17201
|
-
(cwd) => !isScratchRootCompatible(cwd, canonicalRoot)
|
|
17202
|
-
)) {
|
|
17203
|
-
throw new Error("Managed scratch root conflicts with a startup workspace");
|
|
17204
|
-
}
|
|
17205
|
-
return {
|
|
17206
|
-
canonicalRoot,
|
|
17207
|
-
device: after.dev,
|
|
17208
|
-
inode: after.ino
|
|
17209
|
-
};
|
|
17210
|
-
}
|
|
17211
|
-
__name(prepareManagedScratchRoot, "prepareManagedScratchRoot");
|
|
17212
|
-
async function revalidateManagedScratchRoot(root) {
|
|
17213
|
-
const stats = await lstat(root.canonicalRoot);
|
|
17214
|
-
validateRootStats(stats);
|
|
17215
|
-
if (stats.dev !== root.device || stats.ino !== root.inode) {
|
|
17216
|
-
throw new Error("Managed scratch root identity changed");
|
|
17217
|
-
}
|
|
17218
|
-
const canonical = realpathSync.native(root.canonicalRoot);
|
|
17219
|
-
if (!isSamePath(canonical, root.canonicalRoot)) {
|
|
17220
|
-
throw new Error("Managed scratch root canonical path changed");
|
|
17221
|
-
}
|
|
17222
|
-
}
|
|
17223
|
-
__name(revalidateManagedScratchRoot, "revalidateManagedScratchRoot");
|
|
17224
|
-
async function createManagedScratchDirectory(root) {
|
|
17225
|
-
await revalidateManagedScratchRoot(root);
|
|
17226
|
-
const created = await mkdtemp(join2(root.canonicalRoot, "scratch-"));
|
|
17227
|
-
const canonical = realpathSync.native(created);
|
|
17228
|
-
if (!isManagedScratchChild(canonical, root.canonicalRoot)) {
|
|
17229
|
-
throw new Error("Managed scratch directory escaped its accepted root");
|
|
17230
|
-
}
|
|
17231
|
-
await revalidateManagedScratchRoot(root);
|
|
17232
|
-
return canonical;
|
|
17233
|
-
}
|
|
17234
|
-
__name(createManagedScratchDirectory, "createManagedScratchDirectory");
|
|
17235
|
-
|
|
17236
|
-
// packages/cli/src/serve/server/request-helpers.ts
|
|
17237
|
-
init_esbuild_shims();
|
|
17238
|
-
function sendJsonBodyParserError(res, err) {
|
|
17239
|
-
if (err instanceof SyntaxError && "status" in err && err.status === 400) {
|
|
17240
|
-
res.status(400).json({ error: "Invalid JSON in request body" });
|
|
17241
|
-
return true;
|
|
17242
|
-
}
|
|
17243
|
-
if (err && typeof err === "object" && "status" in err && err.status === 413) {
|
|
17244
|
-
res.status(413).json({ error: "Request body too large (max 10 MB)" });
|
|
17245
|
-
return true;
|
|
17246
|
-
}
|
|
17247
|
-
return false;
|
|
17248
|
-
}
|
|
17249
|
-
__name(sendJsonBodyParserError, "sendJsonBodyParserError");
|
|
17250
|
-
var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
17251
|
-
"__proto__",
|
|
17252
|
-
"constructor",
|
|
17253
|
-
"prototype"
|
|
17254
|
-
]);
|
|
17255
|
-
var CLIENT_ID_HEADER = "x-qwen-client-id";
|
|
17256
|
-
var MAX_CLIENT_ID_LENGTH = 128;
|
|
17257
|
-
var MAX_TOOL_NAME_LENGTH = 256;
|
|
17258
|
-
var MAX_SERVER_NAME_LENGTH = 256;
|
|
17259
|
-
var CLIENT_ID_RE = /^[A-Za-z0-9._:-]+$/;
|
|
17260
|
-
var INVALID_PERMISSION_OUTCOME_ERROR = '`outcome` must be `{ outcome: "cancelled" }` or `{ outcome: "selected", optionId: string }`';
|
|
17261
|
-
var deferredRuntimeTimingKey = Symbol(
|
|
17262
|
-
"deferredRuntimeRequestTiming"
|
|
17263
|
-
);
|
|
17264
|
-
function setDeferredRuntimeRequestTiming(req, timing) {
|
|
17265
|
-
req[deferredRuntimeTimingKey] = timing;
|
|
17266
|
-
}
|
|
17267
|
-
__name(setDeferredRuntimeRequestTiming, "setDeferredRuntimeRequestTiming");
|
|
17268
|
-
function getDeferredRuntimeRequestTiming(req) {
|
|
17269
|
-
return req[deferredRuntimeTimingKey];
|
|
17270
|
-
}
|
|
17271
|
-
__name(getDeferredRuntimeRequestTiming, "getDeferredRuntimeRequestTiming");
|
|
17272
|
-
function safeBody(req) {
|
|
17273
|
-
const raw = req.body;
|
|
17274
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
17275
|
-
return /* @__PURE__ */ Object.create(null);
|
|
17276
|
-
}
|
|
17277
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
17278
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
17279
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
17280
|
-
out[key] = value;
|
|
17281
|
-
}
|
|
17282
|
-
return out;
|
|
17283
|
-
}
|
|
17284
|
-
__name(safeBody, "safeBody");
|
|
17285
|
-
function parseOptionalWorkspaceCwd(body, boundWorkspace, res) {
|
|
17286
|
-
const hasCwd = "cwd" in body;
|
|
17287
|
-
if (hasCwd && typeof body["cwd"] !== "string") {
|
|
17288
|
-
res.status(400).json({ error: "`cwd` must be a string absolute path when provided" });
|
|
17289
|
-
return void 0;
|
|
17290
|
-
}
|
|
17291
|
-
if (hasCwd && body["cwd"].length > MAX_WORKSPACE_PATH_LENGTH) {
|
|
17292
|
-
res.status(400).json({
|
|
17293
|
-
error: `\`cwd\` exceeds the ${MAX_WORKSPACE_PATH_LENGTH}-character limit`
|
|
17294
|
-
});
|
|
17295
|
-
return void 0;
|
|
17296
|
-
}
|
|
17297
|
-
const cwd = translateAndCheckAbsoluteWorkspacePath(
|
|
17298
|
-
hasCwd ? body["cwd"] : boundWorkspace
|
|
17299
|
-
);
|
|
17300
|
-
if (cwd === null) {
|
|
17301
|
-
res.status(400).json({ error: "`cwd` must be an absolute path when provided" });
|
|
17302
|
-
return void 0;
|
|
17303
|
-
}
|
|
17304
|
-
return cwd;
|
|
17305
|
-
}
|
|
17306
|
-
__name(parseOptionalWorkspaceCwd, "parseOptionalWorkspaceCwd");
|
|
17307
|
-
function requireSessionId(req, res) {
|
|
17308
|
-
const sessionId = req.params["id"];
|
|
17309
|
-
if (!sessionId) {
|
|
17310
|
-
res.status(400).json({ error: "`sessionId` route parameter is required" });
|
|
17311
|
-
return null;
|
|
17312
|
-
}
|
|
17313
|
-
return sessionId;
|
|
17314
|
-
}
|
|
17315
|
-
__name(requireSessionId, "requireSessionId");
|
|
17316
|
-
function parseClientIdHeader(req, res) {
|
|
17317
|
-
const raw = req.get(CLIENT_ID_HEADER);
|
|
17318
|
-
if (raw === void 0 || raw === "") return void 0;
|
|
17319
|
-
if (raw.length > MAX_CLIENT_ID_LENGTH || !CLIENT_ID_RE.test(raw)) {
|
|
17320
|
-
res.status(400).json({
|
|
17321
|
-
error: "`X-Qwen-Client-Id` must be a non-empty token of 128 characters or fewer",
|
|
17322
|
-
code: "invalid_client_id"
|
|
17323
|
-
});
|
|
17324
|
-
return null;
|
|
17325
|
-
}
|
|
17326
|
-
return raw;
|
|
17327
|
-
}
|
|
17328
|
-
__name(parseClientIdHeader, "parseClientIdHeader");
|
|
17329
|
-
function detectFromLoopback(req) {
|
|
17330
|
-
const addr = req.socket?.remoteAddress;
|
|
17331
|
-
if (typeof addr !== "string") return false;
|
|
17332
|
-
if (addr === "::1") return true;
|
|
17333
|
-
if (addr.startsWith("127.")) return true;
|
|
17334
|
-
if (addr.startsWith("::ffff:127.")) return true;
|
|
17335
|
-
return false;
|
|
17336
|
-
}
|
|
17337
|
-
__name(detectFromLoopback, "detectFromLoopback");
|
|
17338
|
-
function validateMcpRuntimeServerName(name, res) {
|
|
17339
|
-
if (typeof name !== "string" || name.length === 0) {
|
|
17340
|
-
res.status(400).json({
|
|
17341
|
-
error: "Server name is required and must be a non-empty string",
|
|
17342
|
-
code: "invalid_server_name"
|
|
17343
|
-
});
|
|
17344
|
-
return false;
|
|
17345
|
-
}
|
|
17346
|
-
if (name.length > MAX_SERVER_NAME_LENGTH) {
|
|
17347
|
-
res.status(400).json({
|
|
17348
|
-
error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
|
|
17349
|
-
code: "invalid_server_name"
|
|
17350
|
-
});
|
|
17351
|
-
return false;
|
|
17352
|
-
}
|
|
17353
|
-
if (!/^[A-Za-z0-9_-]+$/.test(name)) {
|
|
17354
|
-
res.status(400).json({
|
|
17355
|
-
error: "Server name must contain only alphanumeric characters, underscores, and hyphens",
|
|
17356
|
-
code: "invalid_server_name"
|
|
17357
|
-
});
|
|
17358
|
-
return false;
|
|
17359
|
-
}
|
|
17360
|
-
if (name === "__proto__" || name === "constructor" || name === "prototype") {
|
|
17361
|
-
res.status(400).json({
|
|
17362
|
-
error: "Server name must not be a reserved JS property name",
|
|
17363
|
-
code: "invalid_server_name"
|
|
17364
|
-
});
|
|
17365
|
-
return false;
|
|
17366
|
-
}
|
|
17367
|
-
return true;
|
|
17368
|
-
}
|
|
17369
|
-
__name(validateMcpRuntimeServerName, "validateMcpRuntimeServerName");
|
|
17370
|
-
function parseAndValidateWorkspaceClientId(req, res, bridge) {
|
|
17371
|
-
const raw = parseClientIdHeader(req, res);
|
|
17372
|
-
if (raw === null || raw === void 0) return raw;
|
|
17373
|
-
const bridges = Array.isArray(bridge) ? bridge : [bridge];
|
|
17374
|
-
if (!bridges.some((candidate) => candidate.knownClientIds().has(raw))) {
|
|
17375
|
-
res.status(400).json({
|
|
17376
|
-
error: `Client id "${raw}" is not registered for this workspace`,
|
|
17377
|
-
code: "invalid_client_id",
|
|
17378
|
-
clientId: raw
|
|
17379
|
-
});
|
|
17380
|
-
return null;
|
|
17381
|
-
}
|
|
17382
|
-
return raw;
|
|
17383
|
-
}
|
|
17384
|
-
__name(parseAndValidateWorkspaceClientId, "parseAndValidateWorkspaceClientId");
|
|
17385
|
-
function createBuildWorkspaceCtx(boundWorkspace) {
|
|
17386
|
-
return (route, clientId) => ({
|
|
17387
|
-
originatorClientId: clientId,
|
|
17388
|
-
route,
|
|
17389
|
-
workspaceCwd: boundWorkspace
|
|
17390
|
-
});
|
|
17391
|
-
}
|
|
17392
|
-
__name(createBuildWorkspaceCtx, "createBuildWorkspaceCtx");
|
|
17393
|
-
function parsePermissionVoteBody(req, res) {
|
|
17394
|
-
const body = safeBody(req);
|
|
17395
|
-
const outcome = body["outcome"];
|
|
17396
|
-
if (!isValidOutcome(outcome)) {
|
|
17397
|
-
res.status(400).json({ error: INVALID_PERMISSION_OUTCOME_ERROR });
|
|
17398
|
-
return void 0;
|
|
17399
|
-
}
|
|
17400
|
-
return {
|
|
17401
|
-
...body,
|
|
17402
|
-
outcome
|
|
17403
|
-
};
|
|
17404
|
-
}
|
|
17405
|
-
__name(parsePermissionVoteBody, "parsePermissionVoteBody");
|
|
17406
|
-
function isValidOutcome(raw) {
|
|
17407
|
-
if (typeof raw !== "object" || raw === null) return false;
|
|
17408
|
-
const obj = raw;
|
|
17409
|
-
if (obj["outcome"] === "cancelled") return true;
|
|
17410
|
-
return obj["outcome"] === "selected" && typeof obj["optionId"] === "string" && obj["optionId"].length > 0;
|
|
17411
|
-
}
|
|
17412
|
-
__name(isValidOutcome, "isValidOutcome");
|
|
17413
|
-
var MIN_QUERY_MAX_QUEUED = 16;
|
|
17414
|
-
var MAX_QUERY_MAX_QUEUED = 2048;
|
|
17415
|
-
function parseMaxQueuedQuery(raw, res) {
|
|
17416
|
-
if (raw === void 0) return void 0;
|
|
17417
|
-
if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
|
|
17418
|
-
writeStderrLine(
|
|
17419
|
-
`qwen serve: rejected ?maxQueued ${safeLogValue(raw)} (not a decimal integer)`
|
|
17420
|
-
);
|
|
17421
|
-
res.status(400).json({
|
|
17422
|
-
error: "`maxQueued` must be a decimal integer",
|
|
17423
|
-
code: "invalid_max_queued"
|
|
17424
|
-
});
|
|
17425
|
-
return null;
|
|
17426
|
-
}
|
|
17427
|
-
const n = Number.parseInt(raw, 10);
|
|
17428
|
-
if (!Number.isFinite(n) || n < MIN_QUERY_MAX_QUEUED || n > MAX_QUERY_MAX_QUEUED) {
|
|
17429
|
-
writeStderrLine(
|
|
17430
|
-
`qwen serve: rejected ?maxQueued ${safeLogValue(raw)} (outside [${MIN_QUERY_MAX_QUEUED}, ${MAX_QUERY_MAX_QUEUED}])`
|
|
17431
|
-
);
|
|
17432
|
-
res.status(400).json({
|
|
17433
|
-
error: `\`maxQueued\` must be in [${MIN_QUERY_MAX_QUEUED}, ${MAX_QUERY_MAX_QUEUED}]`,
|
|
17434
|
-
code: "invalid_max_queued"
|
|
17435
|
-
});
|
|
17436
|
-
return null;
|
|
17437
|
-
}
|
|
17438
|
-
return n;
|
|
17439
|
-
}
|
|
17440
|
-
__name(parseMaxQueuedQuery, "parseMaxQueuedQuery");
|
|
17441
|
-
function safeLogValue(raw) {
|
|
17442
|
-
return JSON.stringify(String(raw)).slice(0, 82);
|
|
17443
|
-
}
|
|
17444
|
-
__name(safeLogValue, "safeLogValue");
|
|
17445
|
-
function parseLastEventId(raw) {
|
|
17446
|
-
if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
|
|
17447
|
-
if (typeof raw === "string" && raw.length > 0) {
|
|
17448
|
-
writeStderrLine(
|
|
17449
|
-
`qwen serve: rejected Last-Event-ID ${safeLogValue(raw)} (not a decimal integer)`
|
|
17450
|
-
);
|
|
17451
|
-
}
|
|
17452
|
-
return void 0;
|
|
17453
|
-
}
|
|
17454
|
-
const n = Number.parseInt(raw, 10);
|
|
17455
|
-
if (!Number.isFinite(n) || n > Number.MAX_SAFE_INTEGER) {
|
|
17456
|
-
writeStderrLine(
|
|
17457
|
-
`qwen serve: rejected Last-Event-ID ${safeLogValue(raw)} (exceeds Number.MAX_SAFE_INTEGER)`
|
|
17458
|
-
);
|
|
17459
|
-
return void 0;
|
|
17460
|
-
}
|
|
17461
|
-
return n;
|
|
17462
|
-
}
|
|
17463
|
-
__name(parseLastEventId, "parseLastEventId");
|
|
17464
|
-
|
|
17465
|
-
// packages/cli/src/serve/rate-limit.ts
|
|
17466
|
-
init_esbuild_shims();
|
|
17467
|
-
var MAX_BUCKETS = 1e4;
|
|
17468
|
-
var GC_REQUEST_INTERVAL = 1e3;
|
|
17469
|
-
var GC_TIMER_INTERVAL_MS = 5 * 60 * 1e3;
|
|
17470
|
-
var WORKSPACE_QUALIFIED_ACP_PATH = /^\/workspaces\/[^/]+\/acp$/i;
|
|
17471
|
-
function resolveTier(method, path2, workspaceQualifiedAcpEnabled) {
|
|
17472
|
-
const p = path2.endsWith("/") && path2.length > 1 ? path2.slice(0, -1) : path2;
|
|
17473
|
-
if (method === "OPTIONS") return null;
|
|
17474
|
-
if ((method === "GET" || method === "HEAD") && (p === "/health" || p === "/demo"))
|
|
17475
|
-
return null;
|
|
17476
|
-
if (method === "POST" && p.startsWith("/session/") && p.endsWith("/heartbeat"))
|
|
17477
|
-
return null;
|
|
17478
|
-
if (method === "GET" && p.startsWith("/session/") && p.endsWith("/events"))
|
|
17479
|
-
return null;
|
|
17480
|
-
if (p === "/acp" || p.startsWith("/acp/")) return null;
|
|
17481
|
-
if (workspaceQualifiedAcpEnabled && WORKSPACE_QUALIFIED_ACP_PATH.test(p))
|
|
17482
|
-
return null;
|
|
17483
|
-
if (method === "POST" && p.startsWith("/session/") && p.endsWith("/prompt"))
|
|
17484
|
-
return "prompt";
|
|
17485
|
-
if (method !== "GET" && method !== "HEAD") return "mutation";
|
|
17486
|
-
return "read";
|
|
17487
|
-
}
|
|
17488
|
-
__name(resolveTier, "resolveTier");
|
|
17489
|
-
function normalizeIp(raw) {
|
|
17490
|
-
if (raw.startsWith("::ffff:")) {
|
|
17491
|
-
return raw.slice(7);
|
|
17492
|
-
}
|
|
17493
|
-
return raw;
|
|
17494
|
-
}
|
|
17495
|
-
__name(normalizeIp, "normalizeIp");
|
|
17496
|
-
function createKeyExtractor(hostname) {
|
|
17497
|
-
const loopback = isLoopbackBind(hostname);
|
|
17498
|
-
return (req) => {
|
|
17499
|
-
const raw = req.get("x-qwen-client-id");
|
|
17500
|
-
const clientId = raw && raw.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(raw) ? raw : void 0;
|
|
17501
|
-
if (loopback) {
|
|
17502
|
-
return clientId ? `cid:${clientId}` : "anonymous";
|
|
17503
|
-
}
|
|
17504
|
-
const ip = normalizeIp(req.socket?.remoteAddress ?? "unknown");
|
|
17505
|
-
return clientId ? `${ip}:${clientId}` : ip;
|
|
17506
|
-
};
|
|
17507
|
-
}
|
|
17508
|
-
__name(createKeyExtractor, "createKeyExtractor");
|
|
17509
|
-
var LOG_SAMPLE_INTERVAL = 100;
|
|
17510
|
-
function createSampledLogger(onLog) {
|
|
17511
|
-
const state = /* @__PURE__ */ new Map();
|
|
17512
|
-
return {
|
|
17513
|
-
log(tier, key) {
|
|
17514
|
-
const logKey = `${tier}:${key}`;
|
|
17515
|
-
let entry = state.get(logKey);
|
|
17516
|
-
if (!entry) {
|
|
17517
|
-
entry = { count: 0, suppressed: 0 };
|
|
17518
|
-
state.set(logKey, entry);
|
|
17519
|
-
}
|
|
17520
|
-
entry.count++;
|
|
17521
|
-
if (entry.count === 1 || entry.count % LOG_SAMPLE_INTERVAL === 0) {
|
|
17522
|
-
onLog(tier, key, entry.suppressed);
|
|
17523
|
-
entry.suppressed = 0;
|
|
17524
|
-
} else {
|
|
17525
|
-
entry.suppressed++;
|
|
17526
|
-
}
|
|
17527
|
-
},
|
|
17528
|
-
clear() {
|
|
17529
|
-
state.clear();
|
|
17530
|
-
}
|
|
17531
|
-
};
|
|
17532
|
-
}
|
|
17533
|
-
__name(createSampledLogger, "createSampledLogger");
|
|
17534
|
-
function createRateLimiter(config) {
|
|
17535
|
-
for (const [tier, cfg] of Object.entries(config.tiers)) {
|
|
17536
|
-
if (!Number.isFinite(cfg.max) || cfg.max <= 0) {
|
|
17537
|
-
throw new Error(
|
|
17538
|
-
`rate limit: ${tier}.max must be a positive number, got ${cfg.max}`
|
|
17539
|
-
);
|
|
17540
|
-
}
|
|
17541
|
-
if (!Number.isFinite(cfg.windowMs) || cfg.windowMs <= 0) {
|
|
17542
|
-
throw new Error(
|
|
17543
|
-
`rate limit: ${tier}.windowMs must be a positive number, got ${cfg.windowMs}`
|
|
17544
|
-
);
|
|
17545
|
-
}
|
|
17546
|
-
}
|
|
17547
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
17548
|
-
const keyExtractor = createKeyExtractor(config.hostname);
|
|
17549
|
-
const hitCounts = {
|
|
17550
|
-
prompt: 0,
|
|
17551
|
-
mutation: 0,
|
|
17552
|
-
read: 0
|
|
17553
|
-
};
|
|
17554
|
-
const rates = {
|
|
17555
|
-
prompt: config.tiers.prompt.max / config.tiers.prompt.windowMs,
|
|
17556
|
-
mutation: config.tiers.mutation.max / config.tiers.mutation.windowMs,
|
|
17557
|
-
read: config.tiers.read.max / config.tiers.read.windowMs
|
|
17558
|
-
};
|
|
17559
|
-
let draining = false;
|
|
17560
|
-
let requestCount = 0;
|
|
17561
|
-
const sampledLog = config.onLimitReached ? createSampledLogger(config.onLimitReached) : void 0;
|
|
17562
|
-
function sweep() {
|
|
17563
|
-
const now = Date.now();
|
|
17564
|
-
for (const [key, tierMap] of buckets) {
|
|
17565
|
-
let allStale = true;
|
|
17566
|
-
for (const [tier, bucket] of tierMap) {
|
|
17567
|
-
if (now - bucket.lastRefill < config.tiers[tier].windowMs * 2) {
|
|
17568
|
-
allStale = false;
|
|
17569
|
-
break;
|
|
17570
|
-
}
|
|
17571
|
-
}
|
|
17572
|
-
if (allStale) {
|
|
17573
|
-
buckets.delete(key);
|
|
17574
|
-
}
|
|
17575
|
-
}
|
|
17576
|
-
}
|
|
17577
|
-
__name(sweep, "sweep");
|
|
17578
|
-
const gcTimer = setInterval(sweep, GC_TIMER_INTERVAL_MS);
|
|
17579
|
-
gcTimer.unref();
|
|
17580
|
-
const middleware = /* @__PURE__ */ __name((req, res, next) => {
|
|
17581
|
-
try {
|
|
17582
|
-
if (draining) {
|
|
17583
|
-
next();
|
|
17584
|
-
return;
|
|
17585
|
-
}
|
|
17586
|
-
const tier = resolveTier(
|
|
17587
|
-
req.method,
|
|
17588
|
-
req.path,
|
|
17589
|
-
config.workspaceQualifiedAcpEnabled === true
|
|
17590
|
-
);
|
|
17591
|
-
if (tier === null) {
|
|
17592
|
-
next();
|
|
17593
|
-
return;
|
|
17594
|
-
}
|
|
17595
|
-
requestCount++;
|
|
17596
|
-
if (requestCount % GC_REQUEST_INTERVAL === 0) {
|
|
17597
|
-
sweep();
|
|
17598
|
-
}
|
|
17599
|
-
const key = keyExtractor(req);
|
|
17600
|
-
if (tryConsume(key, tier)) {
|
|
17601
|
-
next();
|
|
17602
|
-
} else {
|
|
17603
|
-
const tierConfig = config.tiers[tier];
|
|
17604
|
-
const rate = rates[tier];
|
|
17605
|
-
const bucket = buckets.get(key)?.get(tier);
|
|
17606
|
-
const retryAfterMs = Math.ceil((1 - (bucket?.tokens ?? 0)) / rate);
|
|
17607
|
-
const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
|
|
17608
|
-
res.setHeader("Retry-After", String(retryAfterSec));
|
|
17609
|
-
res.setHeader("X-RateLimit-Limit", String(tierConfig.max));
|
|
17610
|
-
res.setHeader("X-RateLimit-Remaining", "0");
|
|
17611
|
-
res.setHeader(
|
|
17612
|
-
"X-RateLimit-Reset",
|
|
17613
|
-
String(Math.ceil((Date.now() + retryAfterMs) / 1e3))
|
|
17614
|
-
);
|
|
17615
|
-
res.status(429).json({
|
|
17616
|
-
error: "Rate limit exceeded",
|
|
17617
|
-
code: "rate_limit_exceeded",
|
|
17618
|
-
tier,
|
|
17619
|
-
retryAfterMs
|
|
17620
|
-
});
|
|
17621
|
-
}
|
|
17622
|
-
} catch (err) {
|
|
17623
|
-
config.onError?.(err, req.path);
|
|
17624
|
-
next();
|
|
17625
|
-
}
|
|
17626
|
-
}, "middleware");
|
|
17627
|
-
function tryConsume(key, tier) {
|
|
17628
|
-
if (draining) return true;
|
|
17629
|
-
const tierConfig = config.tiers[tier];
|
|
17630
|
-
const rate = rates[tier];
|
|
17631
|
-
const now = Date.now();
|
|
17632
|
-
let tierMap = buckets.get(key);
|
|
17633
|
-
if (!tierMap) {
|
|
17634
|
-
if (buckets.size >= MAX_BUCKETS) {
|
|
17635
|
-
config.onError?.(
|
|
17636
|
-
new Error(`rate limit bucket overflow: ${buckets.size} keys`),
|
|
17637
|
-
`tryConsume:${tier}`
|
|
17638
|
-
);
|
|
17639
|
-
return true;
|
|
17640
|
-
}
|
|
17641
|
-
tierMap = /* @__PURE__ */ new Map();
|
|
17642
|
-
buckets.set(key, tierMap);
|
|
17643
|
-
}
|
|
17644
|
-
let bucket = tierMap.get(tier);
|
|
17645
|
-
if (!bucket) {
|
|
17646
|
-
bucket = { tokens: tierConfig.max, lastRefill: now };
|
|
17647
|
-
tierMap.set(tier, bucket);
|
|
17648
|
-
}
|
|
17649
|
-
const elapsed = Math.max(0, now - bucket.lastRefill);
|
|
17650
|
-
bucket.tokens = Math.min(tierConfig.max, bucket.tokens + elapsed * rate);
|
|
17651
|
-
bucket.lastRefill = now;
|
|
17652
|
-
if (bucket.tokens >= 1) {
|
|
17653
|
-
bucket.tokens -= 1;
|
|
17654
|
-
return true;
|
|
17655
|
-
}
|
|
17656
|
-
hitCounts[tier]++;
|
|
17657
|
-
if (sampledLog) sampledLog.log(tier, key);
|
|
17658
|
-
return false;
|
|
17659
|
-
}
|
|
17660
|
-
__name(tryConsume, "tryConsume");
|
|
17661
|
-
return {
|
|
17662
|
-
middleware,
|
|
17663
|
-
checkRate: tryConsume,
|
|
17664
|
-
reset() {
|
|
17665
|
-
buckets.clear();
|
|
17666
|
-
hitCounts.prompt = 0;
|
|
17667
|
-
hitCounts.mutation = 0;
|
|
17668
|
-
hitCounts.read = 0;
|
|
17669
|
-
requestCount = 0;
|
|
17670
|
-
sampledLog?.clear();
|
|
17671
|
-
},
|
|
17672
|
-
setDraining(v) {
|
|
17673
|
-
draining = v;
|
|
17674
|
-
},
|
|
17675
|
-
dispose() {
|
|
17676
|
-
clearInterval(gcTimer);
|
|
17677
|
-
buckets.clear();
|
|
17678
|
-
sampledLog?.clear();
|
|
17679
|
-
},
|
|
17680
|
-
getHitCounts() {
|
|
17681
|
-
return { ...hitCounts };
|
|
17682
|
-
}
|
|
17683
|
-
};
|
|
17684
|
-
}
|
|
17685
|
-
__name(createRateLimiter, "createRateLimiter");
|
|
17686
|
-
var RATE_LIMITER_KEY = "_rateLimiter";
|
|
17687
|
-
function setRateLimiter(app, limiter) {
|
|
17688
|
-
app.locals[RATE_LIMITER_KEY] = limiter;
|
|
17689
|
-
}
|
|
17690
|
-
__name(setRateLimiter, "setRateLimiter");
|
|
17691
|
-
function getRateLimiter(app) {
|
|
17692
|
-
return app.locals[RATE_LIMITER_KEY];
|
|
17693
|
-
}
|
|
17694
|
-
__name(getRateLimiter, "getRateLimiter");
|
|
17695
|
-
|
|
17696
|
-
// packages/cli/src/serve/acp-http-enabled.ts
|
|
17697
|
-
init_esbuild_shims();
|
|
17698
|
-
function resolveAcpHttpEnabled(env = process.env) {
|
|
17699
|
-
return env["QWEN_SERVE_ACP_HTTP"] !== "0";
|
|
17700
|
-
}
|
|
17701
|
-
__name(resolveAcpHttpEnabled, "resolveAcpHttpEnabled");
|
|
17702
|
-
|
|
17703
|
-
// packages/cli/src/serve/daemon-status.ts
|
|
17704
|
-
init_esbuild_shims();
|
|
17705
|
-
var DEFAULT_LISTENER_MAX_CONNECTIONS = 256;
|
|
17706
|
-
var SECTION_TIMEOUT_MS = 1e3;
|
|
17707
|
-
var CAPACITY_WARNING_RATIO = 0.8;
|
|
17708
|
-
function toDaemonStatusMemoryLimits(budget) {
|
|
17709
|
-
if (!budget) return null;
|
|
17710
|
-
return {
|
|
17711
|
-
enforced: false,
|
|
17712
|
-
configuredBudgetMb: budget.configuredBudgetMb,
|
|
17713
|
-
effectiveBudgetMb: budget.effectiveBudgetMb,
|
|
17714
|
-
budgetSource: budget.budgetSource,
|
|
17715
|
-
availableMemoryMb: budget.availableMemoryMb,
|
|
17716
|
-
availableMemorySource: budget.availableMemorySource,
|
|
17717
|
-
insufficientMemory: budget.insufficientMemory,
|
|
17718
|
-
modeled: {
|
|
17719
|
-
rootReserveMb: budget.rootReserveMb,
|
|
17720
|
-
childPoolMb: budget.childPoolMb,
|
|
17721
|
-
minChildHeapMb: MIN_CHILD_HEAP_MB,
|
|
17722
|
-
maxChildHeapMb: MAX_CHILD_HEAP_MB,
|
|
17723
|
-
legacyChildCeilingMb: budget.legacyChildCeilingMb
|
|
17724
|
-
}
|
|
17725
|
-
};
|
|
17726
|
-
}
|
|
17727
|
-
__name(toDaemonStatusMemoryLimits, "toDaemonStatusMemoryLimits");
|
|
17728
|
-
var SectionTimeoutError = class extends Error {
|
|
17729
|
-
constructor(section, timeoutMs) {
|
|
17730
|
-
super(`${section} status timed out after ${timeoutMs}ms`);
|
|
17731
|
-
this.section = section;
|
|
17732
|
-
this.timeoutMs = timeoutMs;
|
|
17733
|
-
this.name = "SectionTimeoutError";
|
|
17734
|
-
}
|
|
17735
|
-
static {
|
|
17736
|
-
__name(this, "SectionTimeoutError");
|
|
17737
|
-
}
|
|
17738
|
-
};
|
|
17739
|
-
function parseDaemonStatusDetail(raw) {
|
|
17740
|
-
if (raw === void 0) return { ok: true, detail: "summary" };
|
|
17741
|
-
if (raw === "summary" || raw === "full") {
|
|
17742
|
-
return { ok: true, detail: raw };
|
|
17743
|
-
}
|
|
17744
|
-
return { ok: false };
|
|
17745
|
-
}
|
|
17746
|
-
__name(parseDaemonStatusDetail, "parseDaemonStatusDetail");
|
|
17747
|
-
async function buildDaemonStatusResponse(detail, input) {
|
|
17748
|
-
const daemonLogStatus = input.daemonLog?.getStatus();
|
|
17749
|
-
const bridgeSnapshot = input.bridge.getDaemonStatusSnapshot();
|
|
17750
|
-
const lastActivity = input.bridge.lastActivityAt ?? null;
|
|
17751
|
-
const workspaceRuntimes = input.workspaceRegistry?.list();
|
|
17752
|
-
const workspaceSnapshots = workspaceRuntimes?.map((runtime) => ({
|
|
17753
|
-
workspaceCwd: runtime.workspaceCwd,
|
|
17754
|
-
snapshot: runtime.bridge === input.bridge ? bridgeSnapshot : runtime.bridge.getDaemonStatusSnapshot(),
|
|
17755
|
-
lastActivity: runtime.bridge === input.bridge ? lastActivity : runtime.bridge.lastActivityAt ?? null
|
|
17756
|
-
})) ?? [
|
|
17757
|
-
{
|
|
17758
|
-
workspaceCwd: input.boundWorkspace,
|
|
17759
|
-
snapshot: bridgeSnapshot,
|
|
17760
|
-
lastActivity
|
|
17761
|
-
}
|
|
17762
|
-
];
|
|
17763
|
-
const aggregatedSessionCount = workspaceSnapshots.reduce(
|
|
17764
|
-
(sum, item) => sum + item.snapshot.sessionCount,
|
|
17765
|
-
0
|
|
17766
|
-
);
|
|
17767
|
-
const aggregatedPendingPermissionCount = workspaceSnapshots.reduce(
|
|
17768
|
-
(sum, item) => sum + item.snapshot.pendingPermissionCount,
|
|
17769
|
-
0
|
|
17770
|
-
);
|
|
17771
|
-
const aggregatedChannelLive = workspaceSnapshots.some(
|
|
17772
|
-
(item) => item.snapshot.channelLive
|
|
17773
|
-
);
|
|
17774
|
-
const memoryBudget = input.opts.daemonMemoryBudget;
|
|
17775
|
-
let runtimeMemory;
|
|
17776
|
-
if (memoryBudget) {
|
|
17777
|
-
const managedRuntimes = input.workspaceRegistry?.listManaged();
|
|
17778
|
-
const activeAcpChildCount = managedRuntimes ? managedRuntimes.filter((runtime) => runtime.bridge.isChannelLive()).length : workspaceSnapshots.filter((item) => item.snapshot.channelLive).length;
|
|
17779
|
-
const registeredWorkspaceCount = input.workspaceRegistry ? input.workspaceRegistry.listEntries().length : workspaceSnapshots.length;
|
|
17780
|
-
runtimeMemory = {
|
|
17781
|
-
registeredWorkspaces: registeredWorkspaceCount,
|
|
17782
|
-
activeAcpChildren: activeAcpChildCount,
|
|
17783
|
-
childRssCoverage: "primary_only",
|
|
17784
|
-
modeled: {
|
|
17785
|
-
recommendedShareAtRegisteredMb: registeredWorkspaceCount > 0 ? recommendedChildShareMb(memoryBudget, registeredWorkspaceCount) : null,
|
|
17786
|
-
recommendedShareAtActiveMb: activeAcpChildCount > 0 ? recommendedChildShareMb(memoryBudget, activeAcpChildCount) : null
|
|
17787
|
-
}
|
|
17788
|
-
};
|
|
17789
|
-
}
|
|
17790
|
-
const aggregatedLastActivity = workspaceSnapshots.reduce(
|
|
17791
|
-
(latest, item) => item.lastActivity !== null && (latest === null || item.lastActivity > latest) ? item.lastActivity : latest,
|
|
17792
|
-
null
|
|
17793
|
-
);
|
|
17794
|
-
const acpSnapshot = input.acpHandle?.registry.getSnapshot();
|
|
17795
|
-
const acpAggregate = input.acpHandle?.getSnapshot();
|
|
17796
|
-
const rateLimitHits = input.rateLimiter?.getHitCounts() ?? zeroRateHits();
|
|
17797
|
-
let pendingPrompts = 0;
|
|
17798
|
-
let derivedQueuedPrompts = 0;
|
|
17799
|
-
const derivedQueuedPromptsByWorkspace = [];
|
|
17800
|
-
for (const [index, { snapshot }] of workspaceSnapshots.entries()) {
|
|
17801
|
-
let derivedQueuedPromptsForWorkspace = 0;
|
|
17802
|
-
for (const session of snapshot.sessions) {
|
|
17803
|
-
pendingPrompts += session.pendingPromptCount;
|
|
17804
|
-
const sessionQueuedPrompts = Math.max(
|
|
17805
|
-
0,
|
|
17806
|
-
session.pendingPromptCount - (session.hasActivePrompt ? 1 : 0)
|
|
17807
|
-
);
|
|
17808
|
-
derivedQueuedPrompts += sessionQueuedPrompts;
|
|
17809
|
-
derivedQueuedPromptsForWorkspace += sessionQueuedPrompts;
|
|
17810
|
-
}
|
|
17811
|
-
derivedQueuedPromptsByWorkspace[index] = derivedQueuedPromptsForWorkspace;
|
|
17812
|
-
}
|
|
17813
|
-
const queuedPrompts = workspaceRuntimes?.reduce(
|
|
17814
|
-
(sum, runtime, index) => sum + (runtime.bridge.pendingPromptTotal ?? derivedQueuedPromptsByWorkspace[index] ?? 0),
|
|
17815
|
-
0
|
|
17816
|
-
) ?? input.bridge.pendingPromptTotal ?? derivedQueuedPrompts;
|
|
17817
|
-
const channelWorker = input.getChannelWorkerSnapshot?.() ?? {
|
|
17818
|
-
enabled: false,
|
|
17819
|
-
state: "disabled",
|
|
17820
|
-
channels: []
|
|
17821
|
-
};
|
|
17822
|
-
const channelWorkers = (workspaceRuntimes?.length ?? 1) > 1 ? input.getChannelWorkerSnapshots?.() : void 0;
|
|
17823
|
-
const totalAdmissionSnapshot = input.getTotalSessionAdmissionSnapshot?.();
|
|
17824
|
-
const issues = [];
|
|
17825
|
-
let full;
|
|
17826
|
-
pushRuntimeIssues(
|
|
17827
|
-
issues,
|
|
17828
|
-
acpSnapshot,
|
|
17829
|
-
acpAggregate,
|
|
17830
|
-
rateLimitHits,
|
|
17831
|
-
input,
|
|
17832
|
-
channelWorker,
|
|
17833
|
-
channelWorkers,
|
|
17834
|
-
totalAdmissionSnapshot,
|
|
17835
|
-
workspaceSnapshots
|
|
17836
|
-
);
|
|
17837
|
-
if (daemonLogStatus?.health === "degraded") {
|
|
17838
|
-
issues.push({
|
|
17839
|
-
code: "daemon_log_degraded",
|
|
17840
|
-
severity: "warning",
|
|
17841
|
-
message: "Daemon file logging is degraded; inspect full status for details."
|
|
17842
|
-
});
|
|
17843
|
-
}
|
|
17844
|
-
if (detail === "full") {
|
|
17845
|
-
full = await buildFullStatus(
|
|
17846
|
-
input,
|
|
17847
|
-
acpAggregate,
|
|
17848
|
-
workspaceSnapshots.flatMap((item) => item.snapshot.sessions)
|
|
17849
|
-
);
|
|
17850
|
-
pushFullIssues(issues, full);
|
|
17851
|
-
}
|
|
17852
|
-
return {
|
|
17853
|
-
v: 1,
|
|
17854
|
-
detail,
|
|
17855
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17856
|
-
status: rollupStatus(issues),
|
|
17857
|
-
issues,
|
|
17858
|
-
daemon: {
|
|
17859
|
-
pid: process.pid,
|
|
17860
|
-
uptimeMs: Math.round(process.uptime() * 1e3),
|
|
17861
|
-
mode: input.opts.mode,
|
|
17862
|
-
workspaceCwd: input.boundWorkspace,
|
|
17863
|
-
...input.startup ? { startup: cloneStartup(input.startup) } : {},
|
|
17864
|
-
...input.qwenCodeVersion ? { qwenCodeVersion: input.qwenCodeVersion } : {},
|
|
17865
|
-
...input.daemonLog?.getDaemonId() ? { daemonId: input.daemonLog.getDaemonId() } : {},
|
|
17866
|
-
...daemonLogStatus ? {
|
|
17867
|
-
runId: daemonLogStatus.runId,
|
|
17868
|
-
logMode: daemonLogStatus.mode,
|
|
17869
|
-
logHealth: daemonLogStatus.health
|
|
17870
|
-
} : {},
|
|
17871
|
-
...detail === "full" && input.daemonLog?.getLogPath() ? { logPath: input.daemonLog.getLogPath() } : {},
|
|
17872
|
-
...detail === "full" && daemonLogStatus ? {
|
|
17873
|
-
logIssues: daemonLogStatus.issues,
|
|
17874
|
-
logDroppedRecords: daemonLogStatus.droppedRecords,
|
|
17875
|
-
logDroppedBytes: daemonLogStatus.droppedBytes
|
|
17876
|
-
} : {}
|
|
17877
|
-
},
|
|
17878
|
-
security: {
|
|
17879
|
-
tokenConfigured: Boolean(input.opts.token),
|
|
17880
|
-
requireAuth: input.opts.requireAuth === true,
|
|
17881
|
-
loopbackBind: isLoopbackBind(input.opts.hostname),
|
|
17882
|
-
allowOriginConfigured: input.opts.allowOrigins !== void 0 && input.opts.allowOrigins.length > 0,
|
|
17883
|
-
allowOriginMode: allowOriginMode(input.opts.allowOrigins),
|
|
17884
|
-
sessionShellCommandEnabled: input.sessionShellCommandEnabled
|
|
17885
|
-
},
|
|
17886
|
-
limits: {
|
|
17887
|
-
maxSessions: bridgeSnapshot.limits.maxSessions,
|
|
17888
|
-
maxTotalSessions: positiveFiniteOrNull(input.opts.maxTotalSessions),
|
|
17889
|
-
maxPendingPromptsPerSession: bridgeSnapshot.limits.maxPendingPromptsPerSession,
|
|
17890
|
-
listenerMaxConnections: listenerMaxConnections(input.opts.maxConnections),
|
|
17891
|
-
eventRingSize: bridgeSnapshot.limits.eventRingSize,
|
|
17892
|
-
compactedReplayMaxBytes: bridgeSnapshot.limits.compactedReplayMaxBytes,
|
|
17893
|
-
maxJournalEvents: bridgeSnapshot.limits.maxJournalEvents,
|
|
17894
|
-
maxJournalBytes: bridgeSnapshot.limits.maxJournalBytes,
|
|
17895
|
-
promptDeadlineMs: positiveFiniteOrNull(input.opts.promptDeadlineMs),
|
|
17896
|
-
writerIdleTimeoutMs: positiveFiniteOrNull(input.opts.writerIdleTimeoutMs),
|
|
17897
|
-
channelIdleTimeoutMs: bridgeSnapshot.limits.channelIdleTimeoutMs,
|
|
17898
|
-
sessionIdleTimeoutMs: bridgeSnapshot.limits.sessionIdleTimeoutMs,
|
|
17899
|
-
acpConnectionCap: acpSnapshot?.connectionCap ?? null,
|
|
17900
|
-
memory: toDaemonStatusMemoryLimits(memoryBudget)
|
|
17901
|
-
},
|
|
17902
|
-
...workspaceRuntimes && workspaceRuntimes.length > 1 ? {
|
|
17903
|
-
workspaces: workspaceRuntimes.map((runtime) => ({
|
|
17904
|
-
id: runtime.workspaceId,
|
|
17905
|
-
cwd: runtime.workspaceCwd,
|
|
17906
|
-
...runtime.displayName !== void 0 ? { displayName: runtime.displayName } : {},
|
|
17907
|
-
primary: runtime.primary,
|
|
17908
|
-
trusted: runtime.trusted
|
|
17909
|
-
}))
|
|
17910
|
-
} : {},
|
|
17911
|
-
capabilities: {
|
|
17912
|
-
protocolVersions: input.protocolVersions,
|
|
17913
|
-
features: [...input.features]
|
|
17914
|
-
},
|
|
17915
|
-
runtime: {
|
|
17916
|
-
sessions: {
|
|
17917
|
-
active: aggregatedSessionCount,
|
|
17918
|
-
...totalAdmissionSnapshot ? { admissionInFlight: totalAdmissionSnapshot.inFlight } : {}
|
|
17919
|
-
},
|
|
17920
|
-
permissions: {
|
|
17921
|
-
pending: aggregatedPendingPermissionCount,
|
|
17922
|
-
policy: bridgeSnapshot.permissionPolicy
|
|
17923
|
-
},
|
|
17924
|
-
channel: { live: aggregatedChannelLive },
|
|
17925
|
-
channelWorker,
|
|
17926
|
-
...channelWorkers && channelWorkers.length > 0 ? { channelWorkers } : {},
|
|
17927
|
-
transport: {
|
|
17928
|
-
restSseActive: input.getRestSseActive(),
|
|
17929
|
-
acp: {
|
|
17930
|
-
enabled: acpSnapshot !== void 0,
|
|
17931
|
-
connections: acpAggregate?.connectionCount ?? 0,
|
|
17932
|
-
connectionStreams: acpAggregate?.connectionStreams ?? 0,
|
|
17933
|
-
sessionStreams: acpAggregate?.sessionStreams ?? 0,
|
|
17934
|
-
sseStreams: acpAggregate?.sseStreams ?? 0,
|
|
17935
|
-
wsStreams: acpAggregate?.wsStreams ?? 0,
|
|
17936
|
-
pendingClientRequests: acpAggregate?.pendingClientRequests ?? 0
|
|
17937
|
-
}
|
|
17938
|
-
},
|
|
17939
|
-
rateLimit: {
|
|
17940
|
-
enabled: input.opts.rateLimit === true,
|
|
17941
|
-
rejectedSinceStart: rateLimitHits
|
|
17942
|
-
},
|
|
17943
|
-
...input.getPerfSnapshot ? { perf: input.getPerfSnapshot() } : {},
|
|
17944
|
-
...input.getMetricsSeries ? { metrics: { series: input.getMetricsSeries() } } : {},
|
|
17945
|
-
activity: {
|
|
17946
|
-
activePrompts: workspaceRuntimes?.reduce(
|
|
17947
|
-
(sum, runtime) => sum + (runtime.bridge.activePromptCount ?? 0),
|
|
17948
|
-
0
|
|
17949
|
-
) ?? input.bridge.activePromptCount ?? 0,
|
|
17950
|
-
pendingPrompts,
|
|
17951
|
-
queuedPrompts,
|
|
17952
|
-
lastActivityAt: aggregatedLastActivity !== null ? new Date(aggregatedLastActivity).toISOString() : null,
|
|
17953
|
-
idleSinceMs: aggregatedLastActivity !== null ? Date.now() - aggregatedLastActivity : null
|
|
17954
|
-
},
|
|
17955
|
-
...runtimeMemory ? { memory: runtimeMemory } : {},
|
|
17956
|
-
process: process.memoryUsage()
|
|
17957
|
-
},
|
|
17958
|
-
...full ? { full } : {}
|
|
17959
|
-
};
|
|
17960
|
-
}
|
|
17961
|
-
__name(buildDaemonStatusResponse, "buildDaemonStatusResponse");
|
|
17962
|
-
function cloneStartup(startup) {
|
|
17963
|
-
return {
|
|
17964
|
-
processStartedAt: startup.processStartedAt,
|
|
17965
|
-
...startup.listenerReadyAt ? { listenerReadyAt: startup.listenerReadyAt } : {},
|
|
17966
|
-
...startup.processToListenMs !== void 0 ? { processToListenMs: startup.processToListenMs } : {},
|
|
17967
|
-
...startup.runQwenServeToListenMs !== void 0 ? { runQwenServeToListenMs: startup.runQwenServeToListenMs } : {},
|
|
17968
|
-
preheat: {
|
|
17969
|
-
status: startup.preheat.status,
|
|
17970
|
-
...startup.preheat.durationMs !== void 0 ? { durationMs: startup.preheat.durationMs } : {},
|
|
17971
|
-
...startup.preheat.error ? { error: startup.preheat.error } : {}
|
|
17972
|
-
}
|
|
17973
|
-
};
|
|
17974
|
-
}
|
|
17975
|
-
__name(cloneStartup, "cloneStartup");
|
|
17976
|
-
async function buildFullStatus(input, acpSnapshot, sessions) {
|
|
17977
|
-
const ctx = {
|
|
17978
|
-
route: "GET /daemon/status",
|
|
17979
|
-
workspaceCwd: input.boundWorkspace
|
|
17980
|
-
};
|
|
17981
|
-
const [mcp, skills, tools, providers, env, preflight, hooks, extensions] = await Promise.all([
|
|
17982
|
-
collectSection(
|
|
17983
|
-
"workspace.mcp",
|
|
17984
|
-
() => input.workspace.getWorkspaceMcpStatus(ctx)
|
|
17985
|
-
),
|
|
17986
|
-
collectSection(
|
|
17987
|
-
"workspace.skills",
|
|
17988
|
-
() => input.workspace.getWorkspaceSkillsStatus(ctx)
|
|
17989
|
-
),
|
|
17990
|
-
collectSection(
|
|
17991
|
-
"workspace.tools",
|
|
17992
|
-
() => input.bridge.getWorkspaceToolsStatus()
|
|
17993
|
-
),
|
|
17994
|
-
collectSection(
|
|
17995
|
-
"workspace.providers",
|
|
17996
|
-
() => input.workspace.getWorkspaceProvidersStatus(ctx)
|
|
17997
|
-
),
|
|
17998
|
-
collectSection(
|
|
17999
|
-
"workspace.env",
|
|
18000
|
-
() => input.workspace.getWorkspaceEnvStatus(ctx)
|
|
18001
|
-
),
|
|
18002
|
-
collectSection(
|
|
18003
|
-
"workspace.preflight",
|
|
18004
|
-
() => input.workspace.getWorkspacePreflightStatus(ctx)
|
|
18005
|
-
),
|
|
18006
|
-
collectSection(
|
|
18007
|
-
"workspace.hooks",
|
|
18008
|
-
() => input.workspace.getWorkspaceHooksStatus(ctx)
|
|
18009
|
-
),
|
|
18010
|
-
collectSection(
|
|
18011
|
-
"workspace.extensions",
|
|
18012
|
-
() => input.workspace.getWorkspaceExtensionsStatus(ctx)
|
|
18013
|
-
)
|
|
18014
|
-
]);
|
|
18015
|
-
return {
|
|
18016
|
-
sessions,
|
|
18017
|
-
acpConnections: acpSnapshot?.connections ?? [],
|
|
18018
|
-
workspace: {
|
|
18019
|
-
mcp,
|
|
18020
|
-
skills,
|
|
18021
|
-
tools,
|
|
18022
|
-
providers,
|
|
18023
|
-
env,
|
|
18024
|
-
preflight,
|
|
18025
|
-
hooks,
|
|
18026
|
-
extensions
|
|
18027
|
-
},
|
|
18028
|
-
auth: {
|
|
18029
|
-
supportedDeviceFlowProviders: [...input.supportedDeviceFlowProviders],
|
|
18030
|
-
pendingDeviceFlowCount: input.deviceFlowRegistry.listPending().length
|
|
18031
|
-
}
|
|
18032
|
-
};
|
|
18033
|
-
}
|
|
18034
|
-
__name(buildFullStatus, "buildFullStatus");
|
|
18035
|
-
async function collectSection(name, read) {
|
|
18036
|
-
const startMs = Date.now();
|
|
18037
|
-
try {
|
|
18038
|
-
const data = await withTimeout(read(), name, SECTION_TIMEOUT_MS);
|
|
18039
|
-
return {
|
|
18040
|
-
status: inferSectionStatus(data),
|
|
18041
|
-
durationMs: Date.now() - startMs,
|
|
18042
|
-
summary: summarizeStatusData(data),
|
|
18043
|
-
data
|
|
18044
|
-
};
|
|
18045
|
-
} catch (err) {
|
|
18046
|
-
return {
|
|
18047
|
-
status: "unavailable",
|
|
18048
|
-
durationMs: Date.now() - startMs,
|
|
18049
|
-
error: {
|
|
18050
|
-
kind: err instanceof SectionTimeoutError ? "timeout" : "error",
|
|
18051
|
-
message: err instanceof Error ? err.message : String(err)
|
|
18052
|
-
}
|
|
18053
|
-
};
|
|
18054
|
-
}
|
|
18055
|
-
}
|
|
18056
|
-
__name(collectSection, "collectSection");
|
|
18057
|
-
async function withTimeout(promise, section, timeoutMs) {
|
|
18058
|
-
let timer;
|
|
18059
|
-
try {
|
|
18060
|
-
return await Promise.race([
|
|
18061
|
-
promise,
|
|
18062
|
-
new Promise((_resolve, reject) => {
|
|
18063
|
-
timer = setTimeout(
|
|
18064
|
-
() => reject(new SectionTimeoutError(section, timeoutMs)),
|
|
18065
|
-
timeoutMs
|
|
18066
|
-
);
|
|
18067
|
-
})
|
|
18068
|
-
]);
|
|
18069
|
-
} finally {
|
|
18070
|
-
if (timer) clearTimeout(timer);
|
|
18071
|
-
}
|
|
18072
|
-
}
|
|
18073
|
-
__name(withTimeout, "withTimeout");
|
|
18074
|
-
function pushRuntimeIssues(issues, acpSnapshot, acpAggregate, rateLimitHits, input, channelWorker, channelWorkers, totalAdmissionSnapshot, workspaceSnapshots) {
|
|
18075
|
-
for (const { workspaceCwd, snapshot } of workspaceSnapshots) {
|
|
18076
|
-
if (snapshot.limits.maxSessions !== null && snapshot.limits.maxSessions > 0 && snapshot.sessionCount / snapshot.limits.maxSessions >= CAPACITY_WARNING_RATIO) {
|
|
18077
|
-
issues.push({
|
|
18078
|
-
code: "session_capacity_high",
|
|
18079
|
-
severity: "warning",
|
|
18080
|
-
message: workspaceSnapshots.length > 1 ? `Workspace ${workspaceCwd} active sessions are at ${snapshot.sessionCount}/${snapshot.limits.maxSessions}.` : `Active sessions are at ${snapshot.sessionCount}/${snapshot.limits.maxSessions}.`
|
|
18081
|
-
});
|
|
18082
|
-
}
|
|
18083
|
-
}
|
|
18084
|
-
const maxTotalSessions = positiveFiniteOrNull(input.opts.maxTotalSessions);
|
|
18085
|
-
if (maxTotalSessions !== null) {
|
|
18086
|
-
const fallbackLiveCount = workspaceSnapshots.reduce(
|
|
18087
|
-
(sum, item) => sum + item.snapshot.sessionCount,
|
|
18088
|
-
0
|
|
18089
|
-
);
|
|
18090
|
-
const totalActive = (totalAdmissionSnapshot?.liveCount ?? fallbackLiveCount) + (totalAdmissionSnapshot?.inFlight ?? 0);
|
|
18091
|
-
if (totalActive / maxTotalSessions >= CAPACITY_WARNING_RATIO) {
|
|
18092
|
-
issues.push({
|
|
18093
|
-
code: "total_session_capacity_high",
|
|
18094
|
-
severity: "warning",
|
|
18095
|
-
message: `Total active and in-flight sessions are at ${totalActive}/${maxTotalSessions}.`
|
|
18096
|
-
});
|
|
18097
|
-
}
|
|
18098
|
-
}
|
|
18099
|
-
if (acpSnapshot !== void 0 && acpSnapshot.connectionCap !== null && acpSnapshot.connectionCap > 0) {
|
|
18100
|
-
const cap = acpSnapshot.connectionCap;
|
|
18101
|
-
const busiest = (acpAggregate?.mounts ?? []).reduce(
|
|
18102
|
-
(max, m) => Math.max(max, m.connectionCount),
|
|
18103
|
-
acpSnapshot.connectionCount
|
|
18104
|
-
);
|
|
18105
|
-
if (busiest / cap >= CAPACITY_WARNING_RATIO) {
|
|
18106
|
-
issues.push({
|
|
18107
|
-
code: "connection_capacity_high",
|
|
18108
|
-
severity: "warning",
|
|
18109
|
-
message: `ACP connections are at ${busiest}/${cap} on the busiest workspace mount.`
|
|
18110
|
-
});
|
|
18111
|
-
}
|
|
18112
|
-
}
|
|
18113
|
-
const pendingPermissionCount = workspaceSnapshots.reduce(
|
|
18114
|
-
(sum, item) => sum + item.snapshot.pendingPermissionCount,
|
|
18115
|
-
0
|
|
18116
|
-
);
|
|
18117
|
-
if (pendingPermissionCount > 0) {
|
|
18118
|
-
issues.push({
|
|
18119
|
-
code: "pending_permissions",
|
|
18120
|
-
severity: "warning",
|
|
18121
|
-
message: `${pendingPermissionCount} permission request(s) are pending.`
|
|
18122
|
-
});
|
|
18123
|
-
}
|
|
18124
|
-
const downWorkspaces = workspaceSnapshots.filter(
|
|
18125
|
-
(item) => item.snapshot.sessionCount > 0 && !item.snapshot.channelLive
|
|
18126
|
-
);
|
|
18127
|
-
if (downWorkspaces.length > 0) {
|
|
18128
|
-
issues.push({
|
|
18129
|
-
code: "acp_channel_down",
|
|
18130
|
-
severity: "error",
|
|
18131
|
-
message: downWorkspaces.length === 1 ? `Active sessions exist but the ACP channel is not live for ${downWorkspaces[0].workspaceCwd}.` : `Active sessions exist but the ACP channel is not live for ${downWorkspaces.length} workspace(s).`
|
|
18132
|
-
});
|
|
18133
|
-
}
|
|
18134
|
-
if (input.opts.rateLimit === true && sumRateHits(rateLimitHits) > 0) {
|
|
18135
|
-
issues.push({
|
|
18136
|
-
code: "rate_limit_hits",
|
|
18137
|
-
severity: "warning",
|
|
18138
|
-
message: `${sumRateHits(rateLimitHits)} request(s) have been rejected by rate limiting since start.`
|
|
18139
|
-
});
|
|
18140
|
-
}
|
|
18141
|
-
const groupedWorkers = channelWorkers && channelWorkers.length > 0 ? channelWorkers : void 0;
|
|
18142
|
-
const workers = groupedWorkers ?? [channelWorker];
|
|
18143
|
-
for (const worker of workers) {
|
|
18144
|
-
pushChannelWorkerIssues(issues, worker, groupedWorkers !== void 0);
|
|
18145
|
-
}
|
|
18146
|
-
}
|
|
18147
|
-
__name(pushRuntimeIssues, "pushRuntimeIssues");
|
|
18148
|
-
function pushChannelWorkerIssues(issues, channelWorker, grouped) {
|
|
18149
|
-
const workspace = "workspaceCwd" in channelWorker ? ` for workspace ${channelWorker.workspaceCwd}` : "";
|
|
18150
|
-
const section = grouped ? "runtime.channelWorkers" : "runtime.channelWorker";
|
|
18151
|
-
if (channelWorker.enabled && (channelWorker.state === "exited" || channelWorker.state === "failed")) {
|
|
18152
|
-
const detailParts = [
|
|
18153
|
-
channelWorker.pid !== void 0 ? `pid=${channelWorker.pid}` : void 0,
|
|
18154
|
-
channelWorker.exitCode !== void 0 ? `code=${channelWorker.exitCode ?? "null"}` : void 0,
|
|
18155
|
-
channelWorker.signal ? `signal=${channelWorker.signal}` : void 0,
|
|
18156
|
-
channelWorker.restartCount !== void 0 ? `restarts=${channelWorker.restartCount}` : void 0,
|
|
18157
|
-
channelWorker.lastExitAt ? `lastExitAt=${channelWorker.lastExitAt}` : void 0,
|
|
18158
|
-
channelWorker.lastRestartAt ? `lastRestartAt=${channelWorker.lastRestartAt}` : void 0,
|
|
18159
|
-
channelWorker.nextRestartAt ? `nextRestartAt=${channelWorker.nextRestartAt}` : void 0,
|
|
18160
|
-
channelWorker.lastHeartbeatAt ? `lastHeartbeatAt=${channelWorker.lastHeartbeatAt}` : void 0,
|
|
18161
|
-
channelWorker.staleHeartbeatAt ? `staleHeartbeatAt=${channelWorker.staleHeartbeatAt}` : void 0
|
|
18162
|
-
].filter(Boolean);
|
|
18163
|
-
const details = detailParts.length > 0 ? ` (${detailParts.join(", ")})` : "";
|
|
18164
|
-
const error = channelWorker.error ? `: ${channelWorker.error}` : "";
|
|
18165
|
-
const isPermanentFailure = channelWorker.state === "failed" && !channelWorker.nextRestartAt;
|
|
18166
|
-
issues.push({
|
|
18167
|
-
code: "channel_worker_exited",
|
|
18168
|
-
severity: isPermanentFailure ? "error" : "warning",
|
|
18169
|
-
message: `Channel worker${workspace} is ${channelWorker.state}${details}${error}.`,
|
|
18170
|
-
section
|
|
18171
|
-
});
|
|
18172
|
-
}
|
|
18173
|
-
if (channelWorker.enabled && channelWorker.state === "running" && channelWorker.requestedChannels !== void 0) {
|
|
18174
|
-
const connected = new Set(channelWorker.channels);
|
|
18175
|
-
const failed = channelWorker.requestedChannels.filter(
|
|
18176
|
-
(channel) => !connected.has(channel)
|
|
18177
|
-
);
|
|
18178
|
-
if (failed.length > 0) {
|
|
18179
|
-
issues.push({
|
|
18180
|
-
code: "channel_worker_partial_connect",
|
|
18181
|
-
severity: "warning",
|
|
18182
|
-
message: `Channel worker${workspace} connected ${channelWorker.channels.length}/${channelWorker.requestedChannels.length} channel(s). Failed: ${failed.join(", ")}.`,
|
|
18183
|
-
section
|
|
18184
|
-
});
|
|
18185
|
-
}
|
|
18186
|
-
}
|
|
18187
|
-
}
|
|
18188
|
-
__name(pushChannelWorkerIssues, "pushChannelWorkerIssues");
|
|
18189
|
-
function pushFullIssues(issues, full) {
|
|
18190
|
-
for (const [name, section] of Object.entries(full.workspace)) {
|
|
18191
|
-
if (section.status === "unavailable") {
|
|
18192
|
-
issues.push({
|
|
18193
|
-
code: "workspace_status_unavailable",
|
|
18194
|
-
severity: "warning",
|
|
18195
|
-
section: name,
|
|
18196
|
-
message: `${name} status is unavailable.`
|
|
18197
|
-
});
|
|
18198
|
-
}
|
|
18199
|
-
}
|
|
18200
|
-
const preflight = full.workspace["preflight"];
|
|
18201
|
-
if (preflight && sectionHasStatus(preflight, "error")) {
|
|
18202
|
-
issues.push({
|
|
18203
|
-
code: "preflight_error",
|
|
18204
|
-
severity: "error",
|
|
18205
|
-
section: "preflight",
|
|
18206
|
-
message: "Workspace preflight reports an error."
|
|
18207
|
-
});
|
|
18208
|
-
}
|
|
18209
|
-
const mcp = full.workspace["mcp"];
|
|
18210
|
-
const mcpBudget = mcp ? inspectMcpBudget(mcp) : void 0;
|
|
18211
|
-
if (mcpBudget === "exhausted") {
|
|
18212
|
-
issues.push({
|
|
18213
|
-
code: "mcp_budget_exhausted",
|
|
18214
|
-
severity: "error",
|
|
18215
|
-
section: "mcp",
|
|
18216
|
-
message: "MCP client budget is exhausted."
|
|
18217
|
-
});
|
|
18218
|
-
} else if (mcpBudget === "warning") {
|
|
18219
|
-
issues.push({
|
|
18220
|
-
code: "mcp_budget_warning",
|
|
18221
|
-
severity: "warning",
|
|
18222
|
-
section: "mcp",
|
|
18223
|
-
message: "MCP client budget is near capacity."
|
|
18224
|
-
});
|
|
18225
|
-
}
|
|
18226
|
-
}
|
|
18227
|
-
__name(pushFullIssues, "pushFullIssues");
|
|
18228
|
-
function inferSectionStatus(data) {
|
|
18229
|
-
const statuses = collectStatuses(data);
|
|
18230
|
-
if (statuses.includes("error")) return "error";
|
|
18231
|
-
if (statuses.includes("warning")) return "warning";
|
|
18232
|
-
return "ok";
|
|
18233
|
-
}
|
|
18234
|
-
__name(inferSectionStatus, "inferSectionStatus");
|
|
18235
|
-
function summarizeStatusData(data) {
|
|
18236
|
-
const summary = {};
|
|
18237
|
-
if (!isRecord(data)) return summary;
|
|
18238
|
-
copyBoolean(data, summary, "initialized");
|
|
18239
|
-
copyBoolean(data, summary, "acpChannelLive");
|
|
18240
|
-
copyString(data, summary, "discoveryState");
|
|
18241
|
-
copyString(data, summary, "budgetMode");
|
|
18242
|
-
copyNumber(data, summary, "clientCount");
|
|
18243
|
-
copyNumber(data, summary, "clientBudget");
|
|
18244
|
-
for (const key of [
|
|
18245
|
-
"cells",
|
|
18246
|
-
"errors",
|
|
18247
|
-
"servers",
|
|
18248
|
-
"budgets",
|
|
18249
|
-
"skills",
|
|
18250
|
-
"tools",
|
|
18251
|
-
"providers",
|
|
18252
|
-
"hooks",
|
|
18253
|
-
"extensions"
|
|
18254
|
-
]) {
|
|
18255
|
-
const value = data[key];
|
|
18256
|
-
if (Array.isArray(value)) {
|
|
18257
|
-
summary[`${key}Count`] = value.length;
|
|
18258
|
-
}
|
|
18259
|
-
}
|
|
18260
|
-
summarizeMcpServers(data, summary);
|
|
18261
|
-
return summary;
|
|
18262
|
-
}
|
|
18263
|
-
__name(summarizeStatusData, "summarizeStatusData");
|
|
18264
|
-
function summarizeMcpServers(data, summary) {
|
|
18265
|
-
const servers = data["servers"];
|
|
18266
|
-
if (!Array.isArray(servers)) return;
|
|
18267
|
-
let connected = 0;
|
|
18268
|
-
let errored = 0;
|
|
18269
|
-
let disabled = 0;
|
|
18270
|
-
for (const server of servers) {
|
|
18271
|
-
if (!isRecord(server)) continue;
|
|
18272
|
-
if (server["disabled"] === true) {
|
|
18273
|
-
disabled++;
|
|
18274
|
-
} else if (server["status"] === "error") {
|
|
18275
|
-
errored++;
|
|
18276
|
-
} else if (server["mcpStatus"] === "connected") {
|
|
18277
|
-
connected++;
|
|
18278
|
-
}
|
|
18279
|
-
}
|
|
18280
|
-
summary["serversConnected"] = connected;
|
|
18281
|
-
summary["serversErrored"] = errored;
|
|
18282
|
-
summary["serversDisabled"] = disabled;
|
|
18283
|
-
}
|
|
18284
|
-
__name(summarizeMcpServers, "summarizeMcpServers");
|
|
18285
|
-
function collectStatuses(data) {
|
|
18286
|
-
const statuses = [];
|
|
18287
|
-
visitStatusContainers(data, (record) => {
|
|
18288
|
-
const status = record["status"];
|
|
18289
|
-
if (typeof status === "string") statuses.push(status);
|
|
18290
|
-
});
|
|
18291
|
-
return statuses;
|
|
18292
|
-
}
|
|
18293
|
-
__name(collectStatuses, "collectStatuses");
|
|
18294
|
-
function sectionHasStatus(section, status) {
|
|
18295
|
-
return collectStatuses(section.data).includes(status);
|
|
18296
|
-
}
|
|
18297
|
-
__name(sectionHasStatus, "sectionHasStatus");
|
|
18298
|
-
function inspectMcpBudget(section) {
|
|
18299
|
-
const data = section.data;
|
|
18300
|
-
if (!isRecord(data)) return void 0;
|
|
18301
|
-
const budgetIssue = inspectBudgetContainers(data);
|
|
18302
|
-
if (budgetIssue) return budgetIssue;
|
|
18303
|
-
const clientCount = numberValue(data["clientCount"]);
|
|
18304
|
-
const clientBudget = numberValue(data["clientBudget"]);
|
|
18305
|
-
if (clientCount !== void 0 && clientBudget !== void 0 && clientBudget > 0) {
|
|
18306
|
-
const ratio = clientCount / clientBudget;
|
|
18307
|
-
if (ratio >= 1) return "exhausted";
|
|
18308
|
-
if (ratio >= 0.75) return "warning";
|
|
18309
|
-
}
|
|
18310
|
-
return void 0;
|
|
18311
|
-
}
|
|
18312
|
-
__name(inspectMcpBudget, "inspectMcpBudget");
|
|
18313
|
-
function inspectBudgetContainers(data) {
|
|
18314
|
-
let result;
|
|
18315
|
-
visitStatusContainers(data, (record) => {
|
|
18316
|
-
if (result === "exhausted") return;
|
|
18317
|
-
const errorKind = record["errorKind"];
|
|
18318
|
-
const disabledReason = record["disabledReason"];
|
|
18319
|
-
const status = record["status"];
|
|
18320
|
-
const kind = record["kind"];
|
|
18321
|
-
const refusedCount = numberValue(record["refusedCount"]);
|
|
18322
|
-
if (errorKind === "budget_exhausted" || disabledReason === "budget" || kind === "mcp_budget" && status === "error" || refusedCount !== void 0 && refusedCount > 0) {
|
|
18323
|
-
result = "exhausted";
|
|
18324
|
-
return;
|
|
18325
|
-
}
|
|
18326
|
-
if (kind === "mcp_budget" && status === "warning") {
|
|
18327
|
-
result = "warning";
|
|
18328
|
-
}
|
|
18329
|
-
});
|
|
18330
|
-
return result;
|
|
18331
|
-
}
|
|
18332
|
-
__name(inspectBudgetContainers, "inspectBudgetContainers");
|
|
18333
|
-
function visitStatusContainers(data, visit) {
|
|
18334
|
-
if (!isRecord(data)) return;
|
|
18335
|
-
visit(data);
|
|
18336
|
-
for (const key of [
|
|
18337
|
-
"cells",
|
|
18338
|
-
"errors",
|
|
18339
|
-
"servers",
|
|
18340
|
-
"budgets",
|
|
18341
|
-
"skills",
|
|
18342
|
-
"tools",
|
|
18343
|
-
"providers",
|
|
18344
|
-
"hooks",
|
|
18345
|
-
"extensions"
|
|
18346
|
-
]) {
|
|
18347
|
-
const value = data[key];
|
|
18348
|
-
if (!Array.isArray(value)) continue;
|
|
18349
|
-
for (const item of value) visitStatusContainers(item, visit);
|
|
18350
|
-
}
|
|
18351
|
-
}
|
|
18352
|
-
__name(visitStatusContainers, "visitStatusContainers");
|
|
18353
|
-
function rollupStatus(issues) {
|
|
18354
|
-
if (issues.some((issue) => issue.severity === "error")) return "error";
|
|
18355
|
-
if (issues.length > 0) return "warning";
|
|
18356
|
-
return "ok";
|
|
18357
|
-
}
|
|
18358
|
-
__name(rollupStatus, "rollupStatus");
|
|
18359
|
-
function allowOriginMode(allowOrigins) {
|
|
18360
|
-
if (!allowOrigins || allowOrigins.length === 0) return "none";
|
|
18361
|
-
return allowOrigins.includes("*") ? "any" : "specific";
|
|
18362
|
-
}
|
|
18363
|
-
__name(allowOriginMode, "allowOriginMode");
|
|
18364
|
-
function listenerMaxConnections(value) {
|
|
18365
|
-
if (value === void 0) return DEFAULT_LISTENER_MAX_CONNECTIONS;
|
|
18366
|
-
if (value === 0 || value === Infinity) return null;
|
|
18367
|
-
return Number.isFinite(value) && value > 0 ? value : null;
|
|
18368
|
-
}
|
|
18369
|
-
__name(listenerMaxConnections, "listenerMaxConnections");
|
|
18370
|
-
function positiveFiniteOrNull(value) {
|
|
18371
|
-
return value !== void 0 && Number.isFinite(value) && value > 0 ? value : null;
|
|
18372
|
-
}
|
|
18373
|
-
__name(positiveFiniteOrNull, "positiveFiniteOrNull");
|
|
18374
|
-
function zeroRateHits() {
|
|
18375
|
-
return { prompt: 0, mutation: 0, read: 0 };
|
|
18376
|
-
}
|
|
18377
|
-
__name(zeroRateHits, "zeroRateHits");
|
|
18378
|
-
function sumRateHits(hits) {
|
|
18379
|
-
return hits.prompt + hits.mutation + hits.read;
|
|
18380
|
-
}
|
|
18381
|
-
__name(sumRateHits, "sumRateHits");
|
|
18382
|
-
function isRecord(value) {
|
|
18383
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18384
|
-
}
|
|
18385
|
-
__name(isRecord, "isRecord");
|
|
18386
|
-
function numberValue(value) {
|
|
18387
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
18388
|
-
}
|
|
18389
|
-
__name(numberValue, "numberValue");
|
|
18390
|
-
function copyBoolean(from, to, key) {
|
|
18391
|
-
const value = from[key];
|
|
18392
|
-
if (typeof value === "boolean") to[key] = value;
|
|
18393
|
-
}
|
|
18394
|
-
__name(copyBoolean, "copyBoolean");
|
|
18395
|
-
function copyString(from, to, key) {
|
|
18396
|
-
const value = from[key];
|
|
18397
|
-
if (typeof value === "string") to[key] = value;
|
|
18398
|
-
}
|
|
18399
|
-
__name(copyString, "copyString");
|
|
18400
|
-
function copyNumber(from, to, key) {
|
|
18401
|
-
const value = numberValue(from[key]);
|
|
18402
|
-
if (value !== void 0) to[key] = value;
|
|
18403
|
-
}
|
|
18404
|
-
__name(copyNumber, "copyNumber");
|
|
18405
|
-
|
|
18406
|
-
// packages/cli/src/serve/cdp-mcp-command.ts
|
|
18407
|
-
init_esbuild_shims();
|
|
18408
|
-
var QWEN_CDP_MCP_COMMAND_ENV = "QWEN_CDP_MCP_COMMAND";
|
|
18409
|
-
function resolveCdpMcpCommand(env) {
|
|
18410
|
-
const command = env[QWEN_CDP_MCP_COMMAND_ENV]?.trim();
|
|
18411
|
-
return command ? command : void 0;
|
|
18412
|
-
}
|
|
18413
|
-
__name(resolveCdpMcpCommand, "resolveCdpMcpCommand");
|
|
18414
|
-
function isBrowserAutomationMcpAvailable(opts, env) {
|
|
18415
|
-
return opts.cdpTunnelOverWs === true && !opts.token && resolveAcpHttpEnabled(env) && resolveCdpMcpCommand(env) !== void 0;
|
|
18416
|
-
}
|
|
18417
|
-
__name(isBrowserAutomationMcpAvailable, "isBrowserAutomationMcpAvailable");
|
|
18418
|
-
|
|
18419
|
-
// packages/cli/src/serve/voice/workspace-voice-coordinator.ts
|
|
18420
|
-
init_esbuild_shims();
|
|
18421
|
-
var MAX_CONCURRENT_VOICE_SESSIONS = 8;
|
|
18422
|
-
var DISPOSE_WAIT_MS = 5e3;
|
|
18423
|
-
var VoiceLeaseAbortError = class extends Error {
|
|
18424
|
-
constructor(kind, message) {
|
|
18425
|
-
super(message);
|
|
18426
|
-
this.kind = kind;
|
|
18427
|
-
this.name = "VoiceLeaseAbortError";
|
|
18428
|
-
}
|
|
18429
|
-
static {
|
|
18430
|
-
__name(this, "VoiceLeaseAbortError");
|
|
18431
|
-
}
|
|
18432
|
-
};
|
|
18433
|
-
var Lease = class {
|
|
18434
|
-
constructor(onRelease) {
|
|
18435
|
-
this.onRelease = onRelease;
|
|
18436
|
-
}
|
|
18437
|
-
static {
|
|
18438
|
-
__name(this, "Lease");
|
|
18439
|
-
}
|
|
18440
|
-
controller = new AbortController();
|
|
18441
|
-
signal = this.controller.signal;
|
|
18442
|
-
released = false;
|
|
18443
|
-
release() {
|
|
18444
|
-
if (this.released) return;
|
|
18445
|
-
this.released = true;
|
|
18446
|
-
this.onRelease(this);
|
|
18447
|
-
}
|
|
18448
|
-
abort(reason) {
|
|
18449
|
-
if (!this.signal.aborted) this.controller.abort(reason);
|
|
18450
|
-
}
|
|
18451
|
-
};
|
|
18452
|
-
var WorkspaceVoiceCoordinator = class {
|
|
18453
|
-
static {
|
|
18454
|
-
__name(this, "WorkspaceVoiceCoordinator");
|
|
18455
|
-
}
|
|
18456
|
-
states = /* @__PURE__ */ new Map();
|
|
18457
|
-
disposed = /* @__PURE__ */ new WeakSet();
|
|
18458
|
-
active = 0;
|
|
18459
|
-
acquire(runtime) {
|
|
18460
|
-
if (this.disposed.has(runtime)) {
|
|
18461
|
-
return { kind: "rejected", reason: "draining" };
|
|
18462
|
-
}
|
|
18463
|
-
const state = this.stateFor(runtime);
|
|
18464
|
-
if (state.draining) return { kind: "rejected", reason: "draining" };
|
|
18465
|
-
if (this.active >= MAX_CONCURRENT_VOICE_SESSIONS) {
|
|
18466
|
-
return { kind: "rejected", reason: "capacity" };
|
|
18467
|
-
}
|
|
18468
|
-
const lease = new Lease((current) => this.release(runtime, current));
|
|
18469
|
-
state.leases.add(lease);
|
|
18470
|
-
this.active++;
|
|
18471
|
-
return { kind: "admitted", lease };
|
|
18472
|
-
}
|
|
18473
|
-
beginWorkspaceDrain(runtime) {
|
|
18474
|
-
this.stateFor(runtime).draining = true;
|
|
18475
|
-
}
|
|
18476
|
-
cancelWorkspaceDrain(runtime) {
|
|
18477
|
-
const state = this.states.get(runtime);
|
|
18478
|
-
if (!state || state.completed) return;
|
|
18479
|
-
state.draining = false;
|
|
18480
|
-
if (state.leases.size === 0) this.states.delete(runtime);
|
|
18481
|
-
}
|
|
18482
|
-
completeWorkspaceDrain(runtime) {
|
|
18483
|
-
this.disposed.add(runtime);
|
|
18484
|
-
const state = this.states.get(runtime);
|
|
18485
|
-
if (!state) return;
|
|
18486
|
-
state.draining = true;
|
|
18487
|
-
state.completed = true;
|
|
18488
|
-
const abortReason = new VoiceLeaseAbortError(
|
|
18489
|
-
"workspace_removed",
|
|
18490
|
-
"Workspace drain completed"
|
|
18491
|
-
);
|
|
18492
|
-
for (const lease of state.leases) lease.abort(abortReason);
|
|
18493
|
-
this.deleteIfIdle(runtime, state);
|
|
18494
|
-
}
|
|
18495
|
-
getWorkspaceActivity(runtime) {
|
|
18496
|
-
return this.states.get(runtime)?.leases.size ?? 0;
|
|
18497
|
-
}
|
|
18498
|
-
async disposeRuntime(runtime, reason) {
|
|
18499
|
-
this.disposed.add(runtime);
|
|
18500
|
-
const state = this.states.get(runtime);
|
|
18501
|
-
if (!state) return;
|
|
18502
|
-
state.draining = true;
|
|
18503
|
-
state.completed = true;
|
|
18504
|
-
const abortReason = new VoiceLeaseAbortError(
|
|
18505
|
-
reason,
|
|
18506
|
-
reason === "daemon_shutdown" ? "Daemon is shutting down" : reason === "workspace_removed" ? "Workspace runtime was removed" : "Workspace trust was reconfigured"
|
|
18507
|
-
);
|
|
18508
|
-
for (const lease of state.leases) lease.abort(abortReason);
|
|
18509
|
-
if (state.leases.size === 0) {
|
|
18510
|
-
this.deleteIfIdle(runtime, state);
|
|
18511
|
-
return;
|
|
18512
|
-
}
|
|
18513
|
-
let resolveIdle;
|
|
18514
|
-
const idle = new Promise((resolve2) => {
|
|
18515
|
-
resolveIdle = resolve2;
|
|
18516
|
-
state.idleWaiters.add(resolve2);
|
|
18517
|
-
});
|
|
18518
|
-
let timeout;
|
|
18519
|
-
let timedOut = false;
|
|
18520
|
-
try {
|
|
18521
|
-
await Promise.race([
|
|
18522
|
-
idle,
|
|
18523
|
-
new Promise((resolve2) => {
|
|
18524
|
-
timeout = setTimeout(() => {
|
|
18525
|
-
timedOut = true;
|
|
18526
|
-
resolve2();
|
|
18527
|
-
}, DISPOSE_WAIT_MS);
|
|
18528
|
-
timeout.unref?.();
|
|
18529
|
-
})
|
|
18530
|
-
]);
|
|
18531
|
-
} finally {
|
|
18532
|
-
if (timeout) clearTimeout(timeout);
|
|
18533
|
-
if (resolveIdle) state.idleWaiters.delete(resolveIdle);
|
|
18534
|
-
}
|
|
18535
|
-
if (timedOut && state.leases.size > 0) {
|
|
18536
|
-
process.stderr.write(
|
|
18537
|
-
`qwen serve: Voice runtime disposal timed out with ${state.leases.size} active lease(s).
|
|
18538
|
-
`
|
|
18539
|
-
);
|
|
18540
|
-
}
|
|
18541
|
-
}
|
|
18542
|
-
stateFor(runtime) {
|
|
18543
|
-
let state = this.states.get(runtime);
|
|
18544
|
-
if (!state) {
|
|
18545
|
-
state = {
|
|
18546
|
-
draining: false,
|
|
18547
|
-
completed: false,
|
|
18548
|
-
leases: /* @__PURE__ */ new Set(),
|
|
18549
|
-
idleWaiters: /* @__PURE__ */ new Set()
|
|
18550
|
-
};
|
|
18551
|
-
this.states.set(runtime, state);
|
|
18552
|
-
}
|
|
18553
|
-
return state;
|
|
18554
|
-
}
|
|
18555
|
-
release(runtime, lease) {
|
|
18556
|
-
const state = this.states.get(runtime);
|
|
18557
|
-
if (!state || !state.leases.delete(lease)) return;
|
|
18558
|
-
this.active--;
|
|
18559
|
-
if (state.leases.size === 0) {
|
|
18560
|
-
for (const resolve2 of state.idleWaiters) resolve2();
|
|
18561
|
-
state.idleWaiters.clear();
|
|
18562
|
-
}
|
|
18563
|
-
this.deleteIfIdle(runtime, state);
|
|
18564
|
-
}
|
|
18565
|
-
deleteIfIdle(runtime, state) {
|
|
18566
|
-
if (state.completed && state.leases.size === 0) this.states.delete(runtime);
|
|
18567
|
-
}
|
|
18568
|
-
};
|
|
18569
|
-
|
|
18570
|
-
// packages/cli/src/serve/server/access-log.ts
|
|
18571
|
-
init_esbuild_shims();
|
|
18572
|
-
import { performance } from "node:perf_hooks";
|
|
18573
|
-
var SESSION_ID_RE = /\/session\/([^/]+)/;
|
|
18574
|
-
var ACCESS_LOG_BURST = 60;
|
|
18575
|
-
var ACCESS_LOG_REFILL_PER_SECOND = 2;
|
|
18576
|
-
var ROUTE_MAX_BYTES = 2 * 1024;
|
|
18577
|
-
var SESSION_ID_MAX_BYTES = 256;
|
|
18578
|
-
var CLIENT_ID_MAX_BYTES = 256;
|
|
18579
|
-
var ACCESS_LOG_CONTROLLER_LOCAL = "accessLogController";
|
|
18580
|
-
function emptySuppressedCounts() {
|
|
18581
|
-
return {
|
|
18582
|
-
suppressed: 0,
|
|
18583
|
-
status2xx: 0,
|
|
18584
|
-
status3xx: 0,
|
|
18585
|
-
status4xx: 0,
|
|
18586
|
-
status5xx: 0,
|
|
18587
|
-
statusOther: 0
|
|
18588
|
-
};
|
|
18589
|
-
}
|
|
18590
|
-
__name(emptySuppressedCounts, "emptySuppressedCounts");
|
|
18591
|
-
function countSuppressed(counts, status) {
|
|
18592
|
-
counts.suppressed += 1;
|
|
18593
|
-
if (status >= 200 && status < 300) counts.status2xx += 1;
|
|
18594
|
-
else if (status >= 300 && status < 400) counts.status3xx += 1;
|
|
18595
|
-
else if (status >= 400 && status < 500) counts.status4xx += 1;
|
|
18596
|
-
else if (status >= 500 && status < 600) counts.status5xx += 1;
|
|
18597
|
-
else counts.statusOther += 1;
|
|
18598
|
-
}
|
|
18599
|
-
__name(countSuppressed, "countSuppressed");
|
|
18600
|
-
function firstRawHeader(rawHeaders, targetName) {
|
|
18601
|
-
for (let i = 0; i + 1 < rawHeaders.length; i += 2) {
|
|
18602
|
-
if (rawHeaders[i]?.toLowerCase() === targetName) return rawHeaders[i + 1];
|
|
18603
|
-
}
|
|
18604
|
-
return void 0;
|
|
18605
|
-
}
|
|
18606
|
-
__name(firstRawHeader, "firstRawHeader");
|
|
18607
|
-
function truncateUtf8(value, maxBytes) {
|
|
18608
|
-
const bytes = Buffer.from(value, "utf8");
|
|
18609
|
-
if (bytes.length <= maxBytes) return { value };
|
|
18610
|
-
let end = maxBytes;
|
|
18611
|
-
while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
|
|
18612
|
-
return {
|
|
18613
|
-
value: bytes.subarray(0, end).toString("utf8"),
|
|
18614
|
-
originalBytes: bytes.length
|
|
18615
|
-
};
|
|
18616
|
-
}
|
|
18617
|
-
__name(truncateUtf8, "truncateUtf8");
|
|
18618
|
-
function installAccessLogMiddleware(app, daemonLog, monotonicNow = () => performance.now()) {
|
|
18619
|
-
let sealed = false;
|
|
18620
|
-
let tokens = ACCESS_LOG_BURST;
|
|
18621
|
-
let refillBaseline = monotonicNow();
|
|
18622
|
-
let suppressed = emptySuppressedCounts();
|
|
18623
|
-
const refill = /* @__PURE__ */ __name(() => {
|
|
18624
|
-
const now = Math.max(monotonicNow(), refillBaseline);
|
|
18625
|
-
tokens = Math.min(
|
|
18626
|
-
ACCESS_LOG_BURST,
|
|
18627
|
-
tokens + (now - refillBaseline) / 1e3 * ACCESS_LOG_REFILL_PER_SECOND
|
|
18628
|
-
);
|
|
18629
|
-
refillBaseline = now;
|
|
18630
|
-
}, "refill");
|
|
18631
|
-
const flushSuppressed = /* @__PURE__ */ __name(() => {
|
|
18632
|
-
if (!daemonLog || suppressed.suppressed === 0) return false;
|
|
18633
|
-
daemonLog.warn("access logs suppressed", suppressed);
|
|
18634
|
-
suppressed = emptySuppressedCounts();
|
|
18635
|
-
return true;
|
|
18636
|
-
}, "flushSuppressed");
|
|
18637
|
-
const controller = {
|
|
18638
|
-
sealAndFlushSuppressed: /* @__PURE__ */ __name(() => {
|
|
18639
|
-
if (sealed) return;
|
|
18640
|
-
sealed = true;
|
|
18641
|
-
try {
|
|
18642
|
-
flushSuppressed();
|
|
18643
|
-
} catch {
|
|
18644
|
-
}
|
|
18645
|
-
}, "sealAndFlushSuppressed")
|
|
18646
|
-
};
|
|
18647
|
-
app.locals[ACCESS_LOG_CONTROLLER_LOCAL] = controller;
|
|
18648
|
-
if (!daemonLog) return controller;
|
|
18649
|
-
app.use((req, res, next) => {
|
|
18650
|
-
const { method, path: reqPath } = req;
|
|
18651
|
-
if (method === "GET" && reqPath === "/health" || method === "POST" && reqPath.endsWith("/heartbeat")) {
|
|
18652
|
-
return next();
|
|
18653
|
-
}
|
|
18654
|
-
const startMs = monotonicNow();
|
|
18655
|
-
res.on("finish", () => {
|
|
18656
|
-
try {
|
|
18657
|
-
if (sealed) return;
|
|
18658
|
-
const status = res.statusCode;
|
|
18659
|
-
if (method === "GET" && reqPath.endsWith("/events") && status === 200) {
|
|
18660
|
-
return;
|
|
18661
|
-
}
|
|
18662
|
-
refill();
|
|
18663
|
-
if (suppressed.suppressed > 0 && tokens >= 1) {
|
|
18664
|
-
tokens -= 1;
|
|
18665
|
-
flushSuppressed();
|
|
18666
|
-
}
|
|
18667
|
-
if (tokens < 1) {
|
|
18668
|
-
countSuppressed(suppressed, status);
|
|
18669
|
-
return;
|
|
18670
|
-
}
|
|
18671
|
-
tokens -= 1;
|
|
18672
|
-
const route = truncateUtf8(`${method} ${reqPath}`, ROUTE_MAX_BYTES);
|
|
18673
|
-
const sessionMatch = reqPath.match(SESSION_ID_RE);
|
|
18674
|
-
const sessionId = sessionMatch?.[1] ? truncateUtf8(sessionMatch[1], SESSION_ID_MAX_BYTES) : void 0;
|
|
18675
|
-
const rawClientId = firstRawHeader(req.rawHeaders, "x-qwen-client-id");
|
|
18676
|
-
const clientId = rawClientId ? truncateUtf8(rawClientId, CLIENT_ID_MAX_BYTES) : void 0;
|
|
18677
|
-
const ctx = {
|
|
18678
|
-
route: route.value,
|
|
18679
|
-
...route.originalBytes ? { routeOriginalBytes: route.originalBytes } : {},
|
|
18680
|
-
...sessionId ? {
|
|
18681
|
-
sessionId: sessionId.value,
|
|
18682
|
-
...sessionId.originalBytes ? { sessionIdOriginalBytes: sessionId.originalBytes } : {}
|
|
18683
|
-
} : {},
|
|
18684
|
-
...clientId ? {
|
|
18685
|
-
clientId: clientId.value,
|
|
18686
|
-
...clientId.originalBytes ? { clientIdOriginalBytes: clientId.originalBytes } : {}
|
|
18687
|
-
} : {},
|
|
18688
|
-
status,
|
|
18689
|
-
durationMs: Math.max(0, Math.round(monotonicNow() - startMs))
|
|
18690
|
-
};
|
|
18691
|
-
if (status >= 400) daemonLog.warn("request completed", ctx);
|
|
18692
|
-
else daemonLog.info("request completed", ctx);
|
|
18693
|
-
} catch {
|
|
18694
|
-
}
|
|
18695
|
-
});
|
|
18696
|
-
next();
|
|
18697
|
-
});
|
|
18698
|
-
return controller;
|
|
18699
|
-
}
|
|
18700
|
-
__name(installAccessLogMiddleware, "installAccessLogMiddleware");
|
|
18701
|
-
|
|
18702
15408
|
export {
|
|
18703
15409
|
require_express2 as require_express,
|
|
18704
|
-
|
|
18705
|
-
brandSecret,
|
|
18706
|
-
unsafeRevealSecret,
|
|
18707
|
-
UnsupportedDeviceFlowProviderError,
|
|
18708
|
-
TooManyActiveDeviceFlowsError,
|
|
18709
|
-
UpstreamDeviceFlowError,
|
|
18710
|
-
setDeviceFlowRegistry,
|
|
18711
|
-
getDeviceFlowRegistry,
|
|
18712
|
-
DeviceFlowRegistry,
|
|
18713
|
-
createBridgeFileSystemAdapter,
|
|
18714
|
-
PathMutexRegistry,
|
|
18715
|
-
isDeepHealthQuery,
|
|
18716
|
-
resolveWebShellDir,
|
|
18717
|
-
denyBrowserOriginCors,
|
|
18718
|
-
parseAllowOriginPatterns,
|
|
18719
|
-
allowOriginCors,
|
|
18720
|
-
hostAllowlist,
|
|
18721
|
-
bearerAuth,
|
|
18722
|
-
createMutationGate,
|
|
18723
|
-
ClientMcpSenderRegistry,
|
|
18724
|
-
createClientMcpServerProvider,
|
|
18725
|
-
SERVE_CAPABILITY_REGISTRY,
|
|
18726
|
-
getAdvertisedServeFeatures,
|
|
18727
|
-
getServeProtocolVersions,
|
|
18728
|
-
CAPABILITIES_SCHEMA_VERSION,
|
|
18729
|
-
isManagedScratchChild,
|
|
18730
|
-
isScratchRootCompatible,
|
|
18731
|
-
prepareManagedScratchRoot,
|
|
18732
|
-
createManagedScratchDirectory,
|
|
18733
|
-
sendJsonBodyParserError,
|
|
18734
|
-
CLIENT_ID_HEADER,
|
|
18735
|
-
MAX_CLIENT_ID_LENGTH,
|
|
18736
|
-
MAX_TOOL_NAME_LENGTH,
|
|
18737
|
-
MAX_SERVER_NAME_LENGTH,
|
|
18738
|
-
CLIENT_ID_RE,
|
|
18739
|
-
setDeferredRuntimeRequestTiming,
|
|
18740
|
-
getDeferredRuntimeRequestTiming,
|
|
18741
|
-
safeBody,
|
|
18742
|
-
parseOptionalWorkspaceCwd,
|
|
18743
|
-
requireSessionId,
|
|
18744
|
-
parseClientIdHeader,
|
|
18745
|
-
detectFromLoopback,
|
|
18746
|
-
validateMcpRuntimeServerName,
|
|
18747
|
-
parseAndValidateWorkspaceClientId,
|
|
18748
|
-
createBuildWorkspaceCtx,
|
|
18749
|
-
parsePermissionVoteBody,
|
|
18750
|
-
parseMaxQueuedQuery,
|
|
18751
|
-
safeLogValue,
|
|
18752
|
-
parseLastEventId,
|
|
18753
|
-
createRateLimiter,
|
|
18754
|
-
setRateLimiter,
|
|
18755
|
-
getRateLimiter,
|
|
18756
|
-
resolveAcpHttpEnabled,
|
|
18757
|
-
toDaemonStatusMemoryLimits,
|
|
18758
|
-
parseDaemonStatusDetail,
|
|
18759
|
-
buildDaemonStatusResponse,
|
|
18760
|
-
allowOriginMode,
|
|
18761
|
-
listenerMaxConnections,
|
|
18762
|
-
positiveFiniteOrNull,
|
|
18763
|
-
QWEN_CDP_MCP_COMMAND_ENV,
|
|
18764
|
-
resolveCdpMcpCommand,
|
|
18765
|
-
isBrowserAutomationMcpAvailable,
|
|
18766
|
-
MAX_CONCURRENT_VOICE_SESSIONS,
|
|
18767
|
-
VoiceLeaseAbortError,
|
|
18768
|
-
WorkspaceVoiceCoordinator,
|
|
18769
|
-
ACCESS_LOG_CONTROLLER_LOCAL,
|
|
18770
|
-
installAccessLogMiddleware
|
|
15410
|
+
resolveWebShellDir
|
|
18771
15411
|
};
|
|
18772
15412
|
/**
|
|
18773
15413
|
* @license
|
|
18774
15414
|
* Copyright 2025 Qwen Team
|
|
18775
15415
|
* SPDX-License-Identifier: Apache-2.0
|
|
18776
15416
|
*/
|
|
18777
|
-
/**
|
|
18778
|
-
* @license
|
|
18779
|
-
* Copyright 2026 Qwen Team
|
|
18780
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
18781
|
-
*/
|
|
18782
15417
|
/*! Bundled license information:
|
|
18783
15418
|
|
|
18784
15419
|
depd/index.js:
|