@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.
Files changed (288) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +19 -9
  2. package/bundled/qc-helper/docs/features/channels/dingtalk.md +1 -1
  3. package/bundled/qc-helper/docs/features/code-review.md +27 -0
  4. package/bundled/qc-helper/docs/qwen-serve.md +1 -1
  5. package/bundled/review/SKILL.md +48 -16
  6. package/chunks/{MaxSizedBox-2HFKP4LI.js → MaxSizedBox-ANWZBPTS.js} +38 -38
  7. package/chunks/{StandaloneSessionPicker-RBV72M5N.js → StandaloneSessionPicker-XKS3VU3A.js} +54 -53
  8. package/chunks/{acpAgent-WO6JFCVQ.js → acpAgent-KUAZ3GCZ.js} +80 -80
  9. package/chunks/{agent-AHGLYNZ4.js → agent-T6PTGX5R.js} +35 -35
  10. package/chunks/{agent-headless-NTXKKGRO.js → agent-headless-PR7YCB7S.js} +35 -35
  11. package/chunks/{anthropicContentGenerator-3CAHEZBM.js → anthropicContentGenerator-DTMVEL5M.js} +7 -7
  12. package/chunks/{artifact-tool-RROCCJDA.js → artifact-tool-U4VOZIK5.js} +2 -2
  13. package/chunks/{askUserQuestion-2EPPBPUZ.js → askUserQuestion-XOBQLPFA.js} +2 -2
  14. package/chunks/{bridge-RPAU64CA.js → bridge-NU6YEEHK.js} +39 -39
  15. package/chunks/{channel-management-service-IGEUOVRT.js → channel-management-service-FSQ7SHYI.js} +1 -1
  16. package/chunks/{channel-settings-store-ON64MF7G.js → channel-settings-store-35VQVUVV.js} +41 -41
  17. package/chunks/{chunk-XC656O42.js → chunk-22NBYCPD.js} +3 -3
  18. package/chunks/{chunk-T4JSA7KU.js → chunk-23RZCH76.js} +3 -3
  19. package/chunks/{chunk-PX5DNLSA.js → chunk-2U3WZGZG.js} +18 -10
  20. package/chunks/chunk-2VSTGY6M.js +107 -0
  21. package/chunks/{chunk-QZKYZCN3.js → chunk-33ACCIIY.js} +3 -3
  22. package/chunks/{chunk-MBEMTXQT.js → chunk-3K7FH2KQ.js} +1 -32
  23. package/chunks/{chunk-ODPXHGLL.js → chunk-3NB7TSE2.js} +2 -2
  24. package/chunks/{chunk-QYPT3QUD.js → chunk-3ULCB3NE.js} +3 -3
  25. package/chunks/{chunk-C4CBJQKY.js → chunk-55LYDVPU.js} +1 -1
  26. package/chunks/{chunk-NKSF7HYF.js → chunk-5KH532V7.js} +1 -1
  27. package/chunks/{chunk-BOGOR4HB.js → chunk-6EOI4HOK.js} +7 -7
  28. package/chunks/{chunk-CA4UIDFU.js → chunk-6GONZVLS.js} +1 -1
  29. package/chunks/{chunk-3UOLYNTF.js → chunk-6HVUFDS2.js} +6 -6
  30. package/chunks/{chunk-5L6CQ4ZU.js → chunk-6M26GBKX.js} +2 -2
  31. package/chunks/{chunk-XA4TMLFN.js → chunk-6UWFTUQL.js} +530 -388
  32. package/chunks/{chunk-W5DXUTXD.js → chunk-6XJMFJ4D.js} +8 -8
  33. package/chunks/{chunk-ACZRB7AD.js → chunk-6XU3PH66.js} +3 -3
  34. package/chunks/{chunk-NGJOAKJY.js → chunk-72LNFGUY.js} +3 -3
  35. package/chunks/{chunk-33FNCQSY.js → chunk-7MH4J33A.js} +2 -2
  36. package/chunks/{chunk-2AGOQ7QO.js → chunk-7UFKAV7Y.js} +118 -6
  37. package/chunks/{chunk-O7V7N4G7.js → chunk-AE77C5ZU.js} +198 -35
  38. package/chunks/{chunk-JELIWHXD.js → chunk-AOHJKPKL.js} +1 -1
  39. package/chunks/{chunk-GLNBDYUX.js → chunk-AXMWHKXA.js} +6 -2
  40. package/chunks/{chunk-H36SETQS.js → chunk-B2GJNDTV.js} +1 -1
  41. package/chunks/{chunk-QMBEW7EW.js → chunk-BGG4MBYD.js} +2 -2
  42. package/chunks/{chunk-6WBWIPBF.js → chunk-BVWVWGLE.js} +2 -2
  43. package/chunks/{chunk-GK2H5GRC.js → chunk-CB2464BK.js} +5 -5
  44. package/chunks/{chunk-2UISUFHQ.js → chunk-CJXIIILK.js} +3 -3
  45. package/chunks/{chunk-WK4TJUEA.js → chunk-CKASRHZI.js} +1 -1
  46. package/chunks/{chunk-Z7JILXGX.js → chunk-CNIID3TV.js} +3 -3
  47. package/chunks/{chunk-5MJGUQU5.js → chunk-CSZEXZBE.js} +3 -3
  48. package/chunks/{chunk-KKN2IE7Q.js → chunk-CTJLZLB5.js} +1 -1
  49. package/chunks/{chunk-HY25ULVN.js → chunk-CXE7E23H.js} +131 -48
  50. package/chunks/{chunk-4WTAQEHV.js → chunk-CYR3HI5X.js} +3 -3
  51. package/chunks/{chunk-KHUA3NQV.js → chunk-DBOFL3LQ.js} +2 -2
  52. package/chunks/{chunk-4LKVQFHM.js → chunk-DREZZAZM.js} +2 -2
  53. package/chunks/{chunk-SQC6K5HI.js → chunk-E33LOKRA.js} +1 -1
  54. package/chunks/{chunk-2DNKEBXY.js → chunk-EI3HZX6C.js} +1 -1
  55. package/chunks/{chunk-U25CMJYY.js → chunk-EKPKKXY2.js} +1 -1
  56. package/chunks/{chunk-RHTTKJTP.js → chunk-EORNAQDT.js} +1 -1
  57. package/chunks/{chunk-432XCUHF.js → chunk-EQL65XGA.js} +1 -1
  58. package/chunks/{chunk-TYERNYH4.js → chunk-EUNWC2VS.js} +2 -2
  59. package/chunks/{chunk-OWSW7EYP.js → chunk-EUQTXRKZ.js} +6 -6
  60. package/chunks/{chunk-UH7Z7Y45.js → chunk-FOGU3LJX.js} +1 -1
  61. package/chunks/{chunk-536SWZTA.js → chunk-FPM27XN4.js} +4 -4
  62. package/chunks/{chunk-E4WHKETF.js → chunk-FVKHVJZQ.js} +2 -49
  63. package/chunks/{chunk-YMBOEAF4.js → chunk-FZBZSYKU.js} +1 -1
  64. package/chunks/{chunk-YZQTA3ID.js → chunk-GDXET5XU.js} +31 -3396
  65. package/chunks/{chunk-A5YZ3FA2.js → chunk-GM463R2B.js} +1 -1
  66. package/chunks/{chunk-MOHAWIAW.js → chunk-GNPNYXJB.js} +2 -2
  67. package/chunks/{chunk-2POQWYM6.js → chunk-GSS34377.js} +3 -3
  68. package/chunks/{chunk-CJ5WLD5Y.js → chunk-H2UQ2M5Z.js} +2 -2
  69. package/chunks/{chunk-SUKEUUAA.js → chunk-HQB2KUAN.js} +1 -1
  70. package/chunks/{chunk-JE44IPH6.js → chunk-HYRDKNUY.js} +2 -2
  71. package/chunks/{chunk-D7D7RXIJ.js → chunk-IC4Z6LX6.js} +1 -1
  72. package/chunks/{chunk-HU5TUALO.js → chunk-IIJEYBNH.js} +3 -3
  73. package/chunks/{chunk-EMYZIABH.js → chunk-IUFV6FYU.js} +1 -1
  74. package/chunks/{chunk-4S4OEQ6U.js → chunk-J6SZID63.js} +2 -2
  75. package/chunks/{chunk-BFHXDAR7.js → chunk-KKBPOU75.js} +1 -1
  76. package/chunks/{chunk-XM2O73NC.js → chunk-KKJJB5X6.js} +2455 -1314
  77. package/chunks/{chunk-SLSWFHDF.js → chunk-KR4ZSC7K.js} +4 -4
  78. package/chunks/{chunk-RFUHDCNF.js → chunk-KXSDEOFU.js} +1 -1
  79. package/chunks/{chunk-MGSSJT27.js → chunk-L4N6TIP7.js} +5 -3
  80. package/chunks/{chunk-OP2OHZ7K.js → chunk-LA7YLSR6.js} +6 -6
  81. package/chunks/{chunk-EXCEQD22.js → chunk-LEMXGVEP.js} +4 -4
  82. package/chunks/{chunk-WVAESIM6.js → chunk-LZC3B5N2.js} +4 -4
  83. package/chunks/{chunk-6O5DVH27.js → chunk-M6HMMKVG.js} +1 -1
  84. package/chunks/{chunk-75QMRMO7.js → chunk-MAJS7AT4.js} +3 -3
  85. package/chunks/{chunk-T5U6IM4C.js → chunk-MVUQRLOH.js} +3 -3
  86. package/chunks/{chunk-GLWP3FDB.js → chunk-MZSJ43WV.js} +31 -2
  87. package/chunks/{chunk-ZPPP6VCL.js → chunk-NG2R4N5O.js} +1 -1
  88. package/chunks/{chunk-UGTWD35F.js → chunk-NLREQJFK.js} +1 -1
  89. package/chunks/{chunk-N2GJU6KI.js → chunk-OFI7IGST.js} +12 -12
  90. package/chunks/{chunk-VGFPFM4U.js → chunk-OH2VKQXH.js} +42 -16
  91. package/chunks/{chunk-G5NNWEYE.js → chunk-OHJLEW5J.js} +4 -4
  92. package/chunks/{chunk-3RXUYQJI.js → chunk-OIVKILPI.js} +49 -0
  93. package/chunks/{chunk-VNQCPBC2.js → chunk-OMKQH5C5.js} +3 -3
  94. package/chunks/{chunk-LBWNSLJK.js → chunk-ONSZH6VM.js} +2 -2
  95. package/chunks/{chunk-DYA25Y7U.js → chunk-OWMAZHIX.js} +1 -1
  96. package/chunks/{chunk-4JH77A2U.js → chunk-P2AJ7FTI.js} +1 -1
  97. package/chunks/{chunk-WNU7TZTO.js → chunk-P3LEG54R.js} +3 -3
  98. package/chunks/{chunk-VSALQLT7.js → chunk-PJOBIWUG.js} +1 -1
  99. package/chunks/{chunk-PPUOYL77.js → chunk-PQBHU2IA.js} +49 -18
  100. package/chunks/{chunk-WO2B5E5B.js → chunk-Q3JQ7OSU.js} +1 -1
  101. package/chunks/{chunk-IMOQO6YL.js → chunk-QETYKPMG.js} +1 -1
  102. package/chunks/{chunk-DVBEGGLG.js → chunk-RERATVSK.js} +10 -9
  103. package/chunks/chunk-RGSBTHSU.js +3381 -0
  104. package/chunks/{chunk-DIT4V3XH.js → chunk-RMH5OWLD.js} +1 -1
  105. package/chunks/{chunk-TOGZTG4X.js → chunk-RNVQB725.js} +2 -2
  106. package/chunks/{chunk-GESMMPAK.js → chunk-RUL4S3K3.js} +79 -59
  107. package/chunks/{chunk-3LZKY4V4.js → chunk-RZHU3ILK.js} +5 -5
  108. package/chunks/{chunk-SKNE3KQC.js → chunk-SCI7K3GM.js} +4 -4
  109. package/chunks/{chunk-YAVY33G4.js → chunk-SEZ556DZ.js} +1 -1
  110. package/chunks/{chunk-Q5RM32DR.js → chunk-SHZYHGQH.js} +1 -1
  111. package/chunks/chunk-SOORFWKY.js +155 -0
  112. package/chunks/{chunk-K2B3X3E7.js → chunk-SPMY6CX6.js} +4 -4
  113. package/chunks/{chunk-FZFP7RZI.js → chunk-TDXGKHDN.js} +3 -3
  114. package/chunks/{chunk-R5RBXLNC.js → chunk-TLV2IMHR.js} +5 -5
  115. package/chunks/{chunk-U4AF3QLU.js → chunk-TRLYP7OF.js} +8 -8
  116. package/chunks/{chunk-ULPQQSDN.js → chunk-TYKC75AX.js} +3 -3
  117. package/chunks/{chunk-KVUBYJDM.js → chunk-TZEZYNYD.js} +1 -1
  118. package/chunks/{chunk-XC2VAFVZ.js → chunk-UVX4Y6IN.js} +1 -1
  119. package/chunks/{chunk-RK2EE3T7.js → chunk-VOARQPTA.js} +2 -2
  120. package/chunks/{chunk-TGGEZINK.js → chunk-W4B7UBUS.js} +7 -7
  121. package/chunks/{chunk-URN76TKR.js → chunk-WCG5O7IP.js} +2 -2
  122. package/chunks/{chunk-TSERKWCX.js → chunk-XCWO3SMQ.js} +3 -3
  123. package/chunks/{chunk-OXX3FG2N.js → chunk-Y4EWSQF7.js} +3 -3
  124. package/chunks/{chunk-3PRJYMXC.js → chunk-Y4NGLFGJ.js} +1 -1
  125. package/chunks/{chunk-A4VTILZT.js → chunk-Y7E5LNLO.js} +115 -85
  126. package/chunks/{chunk-3LGZEYOF.js → chunk-YHEEE624.js} +2 -2
  127. package/chunks/{chunk-SQOMTWQT.js → chunk-YJQCUTT2.js} +4 -4
  128. package/chunks/{chunk-GWRHVJY2.js → chunk-ZJ3VGYHK.js} +20 -20
  129. package/chunks/{chunk-LAAFLUQV.js → chunk-ZXU5UHMI.js} +1 -1
  130. package/chunks/{computer-use-5HXJ2P4X.js → computer-use-KEAPATUY.js} +35 -35
  131. package/chunks/{config-utils-VXD4SPVR.js → config-utils-K7SCOWHD.js} +2 -2
  132. package/chunks/{contextCommand-7BEHRX2H.js → contextCommand-ZJA6A5K5.js} +37 -37
  133. package/chunks/{core-runtime-U4VJ5TIV.js → core-runtime-QMJPWHCI.js} +35 -35
  134. package/chunks/{create-sub-session-QZNQY332.js → create-sub-session-DJE6XICH.js} +35 -35
  135. package/chunks/{create-sub-session-6EWLAZD5.js → create-sub-session-E4PBYXV5.js} +2 -2
  136. package/chunks/{cron-create-CCRIULJE.js → cron-create-GRBWYMIJ.js} +4 -4
  137. package/chunks/{cron-delete-FCFW6BGP.js → cron-delete-4CMKBR2M.js} +4 -4
  138. package/chunks/{cron-list-QDBVCHRF.js → cron-list-W7K4ASOA.js} +4 -4
  139. package/chunks/{daemon-YI5RY62O.js → daemon-SCTQMM7Z.js} +50 -2
  140. package/chunks/{daemon-status-provider-UJUGTDTL.js → daemon-status-provider-FSWDPE2G.js} +43 -43
  141. package/chunks/{daemon-trust-policy-ZHDD5VYQ.js → daemon-trust-policy-P5L5VFAY.js} +40 -40
  142. package/chunks/{daemon-trust-policy-monitor-5SHYBDGA.js → daemon-trust-policy-monitor-73JYN6ET.js} +40 -40
  143. package/chunks/{deferred-core-runtime-QWAEBTR5.js → deferred-core-runtime-BWRCCFTE.js} +35 -35
  144. package/chunks/{display-image-GUJA352B.js → display-image-O2XFPQTC.js} +4 -4
  145. package/chunks/{dist-A7H2KKUC.js → dist-K4WWQBHS.js} +277 -98
  146. package/chunks/{earlyInputCapture-5BMSGKOI.js → earlyInputCapture-L5ST23A6.js} +36 -36
  147. package/chunks/{edit-IVQQVYYK.js → edit-LHHRFNFK.js} +35 -35
  148. package/chunks/{enter-worktree-P3DF2N6O.js → enter-worktree-6NXCRZPF.js} +35 -35
  149. package/chunks/{enterPlanMode-KKTDVLAD.js → enterPlanMode-ZVQT4U7D.js} +35 -35
  150. package/chunks/{environment-W5TRDM3G.js → environment-GJ2HWJOS.js} +37 -37
  151. package/chunks/{errors-GJJB36IG.js → errors-6RLH3XXC.js} +37 -37
  152. package/chunks/{exit-worktree-LV5RLOCU.js → exit-worktree-I2YLUHHY.js} +35 -35
  153. package/chunks/{exitPlanMode-TUGOB2LL.js → exitPlanMode-7CCWKO7I.js} +35 -35
  154. package/chunks/{fast-path-2FU23PAQ.js → fast-path-DVFD4HQD.js} +2 -2
  155. package/chunks/{gemini-KDULDQPP.js → gemini-7FWGOYD4.js} +72 -72
  156. package/chunks/{geminiContentGenerator-LOCFHNVA.js → geminiContentGenerator-V2WVLIXU.js} +6 -6
  157. package/chunks/{glob-Z4NQFKWS.js → glob-3N6GGI75.js} +35 -35
  158. package/chunks/{goal-tools-3AZKX5WQ.js → goal-tools-APOW6UXL.js} +3 -3
  159. package/chunks/{grep-Z67Z7EN6.js → grep-V43ILZVX.js} +35 -35
  160. package/chunks/{handleAutoUpdate-DFUZE6I6.js → handleAutoUpdate-A5WFVWIA.js} +39 -39
  161. package/chunks/{i18n-R4FMZ7N2.js → i18n-PLVQ4FBZ.js} +36 -36
  162. package/chunks/{image-gen-4CQJPQP5.js → image-gen-7C6IK6DC.js} +9 -9
  163. package/chunks/{initializer-DK672ZVE.js → initializer-CVAIKZYL.js} +41 -41
  164. package/chunks/{installationInfo-PO4Z3ES7.js → installationInfo-J6QDHNTK.js} +36 -36
  165. package/chunks/{keychain-token-storage-7BT4TQ2A.js → keychain-token-storage-TVAMTNP7.js} +2 -2
  166. package/chunks/{list-QXQF6C53.js → list-ZZ4OUUWV.js} +43 -43
  167. package/chunks/{list-agents-GC4BRET7.js → list-agents-KFFIIS2G.js} +2 -2
  168. package/chunks/{loadedSettingsAdapter-4D7GUGW6.js → loadedSettingsAdapter-QRYRJSWL.js} +40 -40
  169. package/chunks/{loggingContentGenerator-2P3MNACM.js → loggingContentGenerator-OJCO75HM.js} +17 -17
  170. package/chunks/{loop-wakeup-SQQSBMEY.js → loop-wakeup-TLMJPMMP.js} +5 -5
  171. package/chunks/{ls-63CC3NNX.js → ls-UVVE2XGE.js} +4 -4
  172. package/chunks/{lsp-P6NVJVNT.js → lsp-QMEA2VLB.js} +2 -2
  173. package/chunks/{managed-npm-update-LWVJQTWO.js → managed-npm-update-PCOSCP2N.js} +36 -36
  174. package/chunks/{mcp-GHETAR6V.js → mcp-M3WKKWKY.js} +40 -40
  175. package/chunks/{monitor-4T7W4M6Q.js → monitor-GN24FH6C.js} +35 -35
  176. package/chunks/{nonInteractiveCli-P4UKCJFM.js → nonInteractiveCli-KPTU3VSQ.js} +72 -72
  177. package/chunks/{notebook-edit-OC2V7U5D.js → notebook-edit-4YM65BBO.js} +35 -35
  178. package/chunks/{openaiContentGenerator-RAJUFWQY.js → openaiContentGenerator-EDQT6GTV.js} +19 -19
  179. package/chunks/{pidfile-HVPYVRLS.js → pidfile-JOIIRN2C.js} +36 -36
  180. package/chunks/{processUtils-VIQ4JHVX.js → processUtils-5SV4MX3Q.js} +2 -2
  181. package/chunks/{qwenContentGenerator-2QLCUFNZ.js → qwenContentGenerator-ODRJSUGR.js} +40 -40
  182. package/chunks/{qwenOAuth2-J2X3EN43.js → qwenOAuth2-NESCMZ47.js} +8 -8
  183. package/chunks/{read-file-ZNAN2B26.js → read-file-HPSODLPK.js} +10 -10
  184. package/chunks/{read-mcp-resource-RTSSEER5.js → read-mcp-resource-2TNSJTC3.js} +2 -2
  185. package/chunks/{record-artifact-E6AWQU2Z.js → record-artifact-RAON52FC.js} +3 -3
  186. package/chunks/{resumeHistoryUtils-RCY7AL5Y.js → resumeHistoryUtils-TXCOFFMQ.js} +41 -40
  187. package/chunks/{ripGrep-C3KMLZM5.js → ripGrep-ZKQGTQAO.js} +35 -35
  188. package/chunks/{run-qwen-serve-3JN75CIQ.js → run-qwen-serve-ZPKPKECF.js} +65 -43
  189. package/chunks/{runtime-VUA4WQYD.js → runtime-2FLNNM36.js} +43 -43
  190. package/chunks/{scheduler-Z4IBXCOY.js → scheduler-365PJHNR.js} +35 -35
  191. package/chunks/{sdk-exporters-http-IL2346MJ.js → sdk-exporters-http-KHAB5JSD.js} +2 -2
  192. package/chunks/{sdk-impl-SYKGVWOZ.js → sdk-impl-KD7FSTC3.js} +2 -2
  193. package/chunks/{send-message-TL6FIMMN.js → send-message-ZM7MRUGP.js} +5 -5
  194. package/chunks/{serve-67T3P7XI.js → serve-QD2RNLKJ.js} +41 -41
  195. package/chunks/{server-46ZO2CLV.js → server-XLJVLTYM.js} +621 -332
  196. package/chunks/{session-B4IFAFC4.js → session-3VTUHH4Q.js} +73 -73
  197. package/chunks/{settings-AI2BTBOZ.js → settings-KULVPCQC.js} +39 -39
  198. package/chunks/{shell-BKII3B6E.js → shell-3SWW22MH.js} +35 -35
  199. package/chunks/{skill-6CFOWVGA.js → skill-KDHWYS7W.js} +14 -14
  200. package/chunks/{skill-settings-6BFQWMW4.js → skill-settings-NMY4JEJC.js} +39 -39
  201. package/chunks/{spawnChannel-FJXW35U7.js → spawnChannel-ZKS4Q5OU.js} +37 -37
  202. package/chunks/{standalone-update-WVINHRNS.js → standalone-update-7CXPGAQ4.js} +37 -37
  203. package/chunks/{startInteractiveUI-YE57CJ57.js → startInteractiveUI-2QQLEKM6.js} +399 -159
  204. package/chunks/{syntheticOutput-BN7E626U.js → syntheticOutput-ZLCUTKO2.js} +3 -3
  205. package/chunks/{task-create-Q727MXHJ.js → task-create-CQP5UEA3.js} +9 -9
  206. package/chunks/{task-list-G6KPQ5JT.js → task-list-XLEZOJXP.js} +5 -5
  207. package/chunks/{task-stop-TNXRNAL2.js → task-stop-7ZQM5CFF.js} +2 -2
  208. package/chunks/{task-update-YB7PXO4R.js → task-update-JA75U2SN.js} +9 -9
  209. package/chunks/{team-create-GNC7NFY6.js → team-create-2WTNGAOX.js} +35 -35
  210. package/chunks/{team-delete-GDO7H2T4.js → team-delete-MHY3UZGY.js} +5 -5
  211. package/chunks/{team-plan-approval-ECALJLIO.js → team-plan-approval-LZTYLEG4.js} +35 -35
  212. package/chunks/{terminal-image-renderer-II74TFVW.js → terminal-image-renderer-R5HHAFJN.js} +41 -36
  213. package/chunks/{theme-manager-2MGEDD22.js → theme-manager-PAUCSJFF.js} +36 -36
  214. package/chunks/{todoWrite-MHWHO5UO.js → todoWrite-UPVOHYGD.js} +4 -4
  215. package/chunks/{tool-search-S6LRRACA.js → tool-search-2BFCGLI6.js} +13 -13
  216. package/chunks/{total-session-admission-7WVAWQPU.js → total-session-admission-MK4SINFK.js} +39 -39
  217. package/chunks/{trustedFolders-NFS4KHO4.js → trustedFolders-TJ5S26N7.js} +36 -36
  218. package/chunks/{update-relaunch-Q5HLZ6UW.js → update-relaunch-N7AYLARF.js} +5 -5
  219. package/chunks/{updateCheck-UOVZSTP7.js → updateCheck-QRZ3DVT5.js} +38 -38
  220. package/chunks/{useAutoAcceptIndicator-PC6MOJCO.js → useAutoAcceptIndicator-DBJ7Y5AF.js} +42 -42
  221. package/chunks/{validateNonInterActiveAuth-YMQP3ZM7.js → validateNonInterActiveAuth-6ZQWUDS3.js} +69 -69
  222. package/chunks/{version-T4AQFZJH.js → version-AJQGQWXC.js} +1 -1
  223. package/chunks/{web-fetch-JQCJHEEX.js → web-fetch-QWJYRXFL.js} +13 -13
  224. package/chunks/{web-search-QGZ2KY4G.js → web-search-2AOXJGHU.js} +9 -9
  225. package/chunks/web-shell-static-O73YPVVC.js +33 -0
  226. package/chunks/{workflow-YIT27YWZ.js → workflow-ERH3SJG2.js} +36 -36
  227. package/chunks/{workspace-providers-status-FIDZQ2PL.js → workspace-providers-status-HZ474RA4.js} +43 -43
  228. package/chunks/{workspace-registration-store-RCSPE4DU.js → workspace-registration-store-6EUP4JXG.js} +1 -1
  229. package/chunks/{workspace-registry-5PEUCY6E.js → workspace-registry-R4APRRNC.js} +39 -39
  230. package/chunks/{workspace-service-LANDVPL7.js → workspace-service-HFIFDMU7.js} +45 -45
  231. package/chunks/{workspace-skills-status-2IQVJNIN.js → workspace-skills-status-HAHEIHGK.js} +41 -41
  232. package/chunks/{workspace-trust-reconciler-CZPZZCZC.js → workspace-trust-reconciler-65GTAGDK.js} +45 -45
  233. package/chunks/{write-file-G5VNGCOG.js → write-file-B34CDELV.js} +35 -35
  234. package/chunks/{zoom-image-6RBLD7X6.js → zoom-image-HPRBZHSS.js} +10 -10
  235. package/cli.js +12 -12
  236. package/package.json +3 -3
  237. package/web-shell/assets/{arc-B-Vtkp1W.js → arc-Ip-FFDJ0.js} +1 -1
  238. package/web-shell/assets/{architectureDiagram-3BPJPVTR-qmTXIj64.js → architectureDiagram-3BPJPVTR-CChqgwIb.js} +1 -1
  239. package/web-shell/assets/{blockDiagram-GPEHLZMM-ZlnXEtdP.js → blockDiagram-GPEHLZMM-BHN-Tfss.js} +1 -1
  240. package/web-shell/assets/{c4Diagram-AAUBKEIU-Wyoirf6Z.js → c4Diagram-AAUBKEIU-BhyjzDjN.js} +1 -1
  241. package/web-shell/assets/channel-Cu8qd6OB.js +1 -0
  242. package/web-shell/assets/{chunk-2J33WTMH-WQwbq9d8.js → chunk-2J33WTMH-BB9eyHrx.js} +1 -1
  243. package/web-shell/assets/{chunk-4BX2VUAB-DQKnAXC4.js → chunk-4BX2VUAB-BeV7ce_J.js} +1 -1
  244. package/web-shell/assets/{chunk-55IACEB6-DIdgXNcO.js → chunk-55IACEB6-DO7kXDiV.js} +1 -1
  245. package/web-shell/assets/{chunk-727SXJPM-CUwGOLEd.js → chunk-727SXJPM-B27MPznD.js} +1 -1
  246. package/web-shell/assets/{chunk-AQP2D5EJ-CCBGZFZ4.js → chunk-AQP2D5EJ-DHIjqjD_.js} +1 -1
  247. package/web-shell/assets/{chunk-FMBD7UC4-DMGXProk.js → chunk-FMBD7UC4-DabMqyN2.js} +1 -1
  248. package/web-shell/assets/{chunk-ND2GUHAM-yejcFqsh.js → chunk-ND2GUHAM-Dw7Nnnxc.js} +1 -1
  249. package/web-shell/assets/{chunk-QZHKN3VN-DxCyqTf7.js → chunk-QZHKN3VN-DdaZew1Q.js} +1 -1
  250. package/web-shell/assets/classDiagram-4FO5ZUOK-BI3KrXhq.js +1 -0
  251. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BI3KrXhq.js +1 -0
  252. package/web-shell/assets/{cose-bilkent-S5V4N54A-DOrF3cg_.js → cose-bilkent-S5V4N54A-CgBpCYH5.js} +1 -1
  253. package/web-shell/assets/{dagre-BM42HDAG-Be6CdYQ_.js → dagre-BM42HDAG-BB4IU8G0.js} +1 -1
  254. package/web-shell/assets/{diagram-2AECGRRQ-B_Rjsft2.js → diagram-2AECGRRQ-BcUTHU37.js} +1 -1
  255. package/web-shell/assets/{diagram-5GNKFQAL-BvQbKSm1.js → diagram-5GNKFQAL-B6Lyihyv.js} +1 -1
  256. package/web-shell/assets/{diagram-KO2AKTUF-BsdmMdpS.js → diagram-KO2AKTUF-CRIJOjpG.js} +1 -1
  257. package/web-shell/assets/{diagram-LMA3HP47-CrQlBaNr.js → diagram-LMA3HP47-DdNiwWWT.js} +1 -1
  258. package/web-shell/assets/{diagram-OG6HWLK6-aB1MD_DL.js → diagram-OG6HWLK6-Dz4b32Do.js} +1 -1
  259. package/web-shell/assets/{erDiagram-TEJ5UH35-F-eUUn9n.js → erDiagram-TEJ5UH35-D-8NGw77.js} +1 -1
  260. package/web-shell/assets/{flowDiagram-I6XJVG4X-DY_d8lUK.js → flowDiagram-I6XJVG4X-Dun26LyM.js} +1 -1
  261. package/web-shell/assets/{ganttDiagram-6RSMTGT7-Dqu7KBPg.js → ganttDiagram-6RSMTGT7-F10Hu1Gc.js} +1 -1
  262. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-2IkU_VEj.js → gitGraphDiagram-PVQCEYII-Bf8Ez6-D.js} +1 -1
  263. package/web-shell/assets/{index-BuSbqggh.js → index-BXqw-nGq.js} +1 -1
  264. package/web-shell/assets/{index-DrfC2e9s.js → index-hcgjV7vS.js} +333 -333
  265. package/web-shell/assets/{infoDiagram-5YYISTIA-C4orwzPw.js → infoDiagram-5YYISTIA-DVgTMLvj.js} +1 -1
  266. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-fXUEfef_.js → ishikawaDiagram-YF4QCWOH-uT-KepvK.js} +1 -1
  267. package/web-shell/assets/{journeyDiagram-JHISSGLW-Bs0I1_J5.js → journeyDiagram-JHISSGLW-Vg_XadQf.js} +1 -1
  268. package/web-shell/assets/{kanban-definition-UN3LZRKU-BPRj1dw_.js → kanban-definition-UN3LZRKU-Dyv1ESBE.js} +1 -1
  269. package/web-shell/assets/{linear-DFS6ZDpW.js → linear-BMIepYX3.js} +1 -1
  270. package/web-shell/assets/{mermaid.core-CZxjWMaO.js → mermaid.core-zA8pJ9ag.js} +5 -5
  271. package/web-shell/assets/{mindmap-definition-RKZ34NQL-Dsr3PmNs.js → mindmap-definition-RKZ34NQL-BCW_BZ_5.js} +1 -1
  272. package/web-shell/assets/{pieDiagram-4H26LBE5-D5Ngpz5Y.js → pieDiagram-4H26LBE5-DseNEGp2.js} +1 -1
  273. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-WJK9I25z.js → quadrantDiagram-W4KKPZXB-6Z7wr1Fi.js} +1 -1
  274. package/web-shell/assets/{requirementDiagram-4Y6WPE33-CmrYcwOH.js → requirementDiagram-4Y6WPE33-CVOlCRPf.js} +1 -1
  275. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-7SjCc4Mv.js → sankeyDiagram-5OEKKPKP-CGhgrUJZ.js} +1 -1
  276. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Dl9E0IRM.js → sequenceDiagram-3UESZ5HK-dlqmpLuQ.js} +1 -1
  277. package/web-shell/assets/{stateDiagram-AJRCARHV-CRRio7yV.js → stateDiagram-AJRCARHV-B9EW10Xb.js} +1 -1
  278. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CJTZJJ5X.js +1 -0
  279. package/web-shell/assets/{timeline-definition-PNZ67QCA-DVyXjjfq.js → timeline-definition-PNZ67QCA-BBRh0mgZ.js} +1 -1
  280. package/web-shell/assets/{vennDiagram-CIIHVFJN-Nb2Lckzq.js → vennDiagram-CIIHVFJN-B8Gs-REP.js} +1 -1
  281. package/web-shell/assets/{wardley-L42UT6IY-DuL_otIP.js → wardley-L42UT6IY-D_y80Ur7.js} +1 -1
  282. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DVI9Q4Aa.js → wardleyDiagram-YWT4CUSO-D-CNfHJ-.js} +1 -1
  283. package/web-shell/assets/{xychartDiagram-2RQKCTM6-BNjAm7KD.js → xychartDiagram-2RQKCTM6-DQ5CvELo.js} +1 -1
  284. package/web-shell/index.html +1 -1
  285. package/web-shell/assets/channel-DXaHnfjS.js +0 -1
  286. package/web-shell/assets/classDiagram-4FO5ZUOK-BigU82A0.js +0 -1
  287. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BigU82A0.js +0 -1
  288. 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 relative2 = __require("path").relative;
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 relative2(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
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(resolve2, reject) {
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
- resolve2(buf);
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 dirname3 = path2.dirname;
10247
- var basename3 = path2.basename;
10228
+ var dirname2 = path2.dirname;
10229
+ var basename2 = path2.basename;
10248
10230
  var extname = path2.extname;
10249
- var join3 = path2.join;
10250
- var resolve2 = path2.resolve;
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 = resolve2(root, name);
10286
- var dir = dirname3(loc);
10287
- var file = basename3(loc);
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 resolve3(dir, file) {
10292
+ View.prototype.resolve = /* @__PURE__ */ __name(function resolve2(dir, file) {
10311
10293
  var ext = this.ext;
10312
- var path3 = join3(dir, file);
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 = join3(dir, basename3(file, ext), "index" + ext);
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 resolve2 = __require("node:path").resolve;
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", resolve2("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 = basename3(filename);
13782
+ var name = basename2(filename);
13801
13783
  var isQuotedString = TEXT_REGEXP.test(name);
13802
- var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename3(fallback);
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 basename3(path2) {
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(basename3, "basename");
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 join3 = path2.join;
14179
+ var join2 = path2.join;
14198
14180
  var normalize = path2.normalize;
14199
- var resolve2 = path2.resolve;
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 ? resolve2(opts.root) : null;
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(join3(root, path3));
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 = resolve2(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 = join3(path3, self._index[i]);
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 resolve2 = path2.resolve;
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 ? resolve2(path3) : path3;
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 resolve2 = __require("path").resolve;
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 = resolve2(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
- sanitizeForStderr,
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: