@qwen-code/qwen-code 0.21.6-preview.0 → 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 (309) 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/channels/github.md +31 -19
  4. package/bundled/qc-helper/docs/features/code-review.md +30 -1
  5. package/bundled/qc-helper/docs/qwen-serve.md +1 -1
  6. package/bundled/review/SKILL.md +59 -22
  7. package/chunks/{MaxSizedBox-A3UYJS6B.js → MaxSizedBox-ANWZBPTS.js} +38 -38
  8. package/chunks/{StandaloneSessionPicker-NGLY26I4.js → StandaloneSessionPicker-XKS3VU3A.js} +56 -55
  9. package/chunks/{acp-startup-profiler-3RICG2YW.js → acp-startup-profiler-ORIQ2CCQ.js} +2 -2
  10. package/chunks/{acpAgent-V54T2EEL.js → acpAgent-KUAZ3GCZ.js} +1091 -150
  11. package/chunks/{agent-2P53VL2L.js → agent-T6PTGX5R.js} +35 -35
  12. package/chunks/{agent-headless-6X4GCFRD.js → agent-headless-PR7YCB7S.js} +35 -35
  13. package/chunks/{anthropicContentGenerator-3CAHEZBM.js → anthropicContentGenerator-DTMVEL5M.js} +7 -7
  14. package/chunks/{artifact-tool-RROCCJDA.js → artifact-tool-U4VOZIK5.js} +2 -2
  15. package/chunks/{askUserQuestion-2EPPBPUZ.js → askUserQuestion-XOBQLPFA.js} +2 -2
  16. package/chunks/{bridge-TCFMLKHR.js → bridge-NU6YEEHK.js} +41 -41
  17. package/chunks/{channel-management-service-5XPPTWUQ.js → channel-management-service-FSQ7SHYI.js} +1 -1
  18. package/chunks/{channel-settings-store-B4HNMNQV.js → channel-settings-store-35VQVUVV.js} +42 -42
  19. package/chunks/{channel-worker-group-SVGPDH2C.js → channel-worker-group-JWQK224X.js} +5 -5
  20. package/chunks/{channel-worker-manager-72ZK2GTS.js → channel-worker-manager-77E3OXRQ.js} +5 -5
  21. package/chunks/{channel-worker-supervisor-NUEDVHX4.js → channel-worker-supervisor-62YWUGP2.js} +3 -3
  22. package/chunks/{chunk-XC656O42.js → chunk-22NBYCPD.js} +3 -3
  23. package/chunks/{chunk-WXS647W5.js → chunk-23RZCH76.js} +3 -3
  24. package/chunks/{chunk-O656RSMC.js → chunk-2U3WZGZG.js} +18 -10
  25. package/chunks/chunk-2VSTGY6M.js +107 -0
  26. package/chunks/{chunk-AU3XNTMV.js → chunk-33ACCIIY.js} +3 -3
  27. package/chunks/{chunk-MBEMTXQT.js → chunk-3K7FH2KQ.js} +1 -32
  28. package/chunks/{chunk-CJBRIJ7W.js → chunk-3NB7TSE2.js} +3 -3
  29. package/chunks/{chunk-QYPT3QUD.js → chunk-3ULCB3NE.js} +3 -3
  30. package/chunks/{chunk-7FUVSN4D.js → chunk-55LYDVPU.js} +1 -1
  31. package/chunks/{chunk-3ZRS2XNY.js → chunk-5KH532V7.js} +1 -1
  32. package/chunks/{chunk-P7LKADIT.js → chunk-6EOI4HOK.js} +7 -7
  33. package/chunks/{chunk-X6DV3WKN.js → chunk-6GONZVLS.js} +1 -1
  34. package/chunks/{chunk-T4XY5CT4.js → chunk-6HVUFDS2.js} +7 -7
  35. package/chunks/{chunk-5L6CQ4ZU.js → chunk-6M26GBKX.js} +2 -2
  36. package/chunks/{chunk-SPUDIAHX.js → chunk-6UWFTUQL.js} +530 -388
  37. package/chunks/{chunk-EKWNNQQD.js → chunk-6XJMFJ4D.js} +16 -10
  38. package/chunks/{chunk-72DZPDH4.js → chunk-6XU3PH66.js} +10 -5
  39. package/chunks/{chunk-WIPQR44Y.js → chunk-72LNFGUY.js} +3 -3
  40. package/chunks/{chunk-33FNCQSY.js → chunk-7MH4J33A.js} +2 -2
  41. package/chunks/{chunk-2AGOQ7QO.js → chunk-7UFKAV7Y.js} +118 -6
  42. package/chunks/{chunk-HKNSU4WX.js → chunk-AE77C5ZU.js} +198 -35
  43. package/chunks/{chunk-JELIWHXD.js → chunk-AOHJKPKL.js} +1 -1
  44. package/chunks/{chunk-GLNBDYUX.js → chunk-AXMWHKXA.js} +6 -2
  45. package/chunks/{chunk-H36SETQS.js → chunk-B2GJNDTV.js} +1 -1
  46. package/chunks/{chunk-QMBEW7EW.js → chunk-BGG4MBYD.js} +2 -2
  47. package/chunks/{chunk-QL4TN4HS.js → chunk-BRVWYMKV.js} +1 -1
  48. package/chunks/{chunk-6WBWIPBF.js → chunk-BVWVWGLE.js} +2 -2
  49. package/chunks/{chunk-WPKIU4PA.js → chunk-CB2464BK.js} +6 -6
  50. package/chunks/{chunk-2UISUFHQ.js → chunk-CJXIIILK.js} +3 -3
  51. package/chunks/{chunk-WK4TJUEA.js → chunk-CKASRHZI.js} +1 -1
  52. package/chunks/{chunk-Z7JILXGX.js → chunk-CNIID3TV.js} +3 -3
  53. package/chunks/{chunk-6KEN3RIC.js → chunk-CSZEXZBE.js} +3 -3
  54. package/chunks/{chunk-KKN2IE7Q.js → chunk-CTJLZLB5.js} +1 -1
  55. package/chunks/{chunk-5CDI4UOY.js → chunk-CXE7E23H.js} +131 -48
  56. package/chunks/{chunk-URJHXOSO.js → chunk-CYR3HI5X.js} +3 -3
  57. package/chunks/{chunk-546CDN6D.js → chunk-DBOFL3LQ.js} +3 -3
  58. package/chunks/{chunk-4LKVQFHM.js → chunk-DREZZAZM.js} +2 -2
  59. package/chunks/{chunk-2U7GPHA6.js → chunk-E33LOKRA.js} +1 -1
  60. package/chunks/{chunk-2DNKEBXY.js → chunk-EI3HZX6C.js} +1 -1
  61. package/chunks/{chunk-U25CMJYY.js → chunk-EKPKKXY2.js} +1 -1
  62. package/chunks/{chunk-QG2ACINA.js → chunk-EORNAQDT.js} +1 -1
  63. package/chunks/{chunk-432XCUHF.js → chunk-EQL65XGA.js} +1 -1
  64. package/chunks/{chunk-TYERNYH4.js → chunk-EUNWC2VS.js} +2 -2
  65. package/chunks/{chunk-CDHNGCJI.js → chunk-EUQTXRKZ.js} +7 -7
  66. package/chunks/{chunk-UH7Z7Y45.js → chunk-FOGU3LJX.js} +1 -1
  67. package/chunks/{chunk-BU4BRCPX.js → chunk-FPM27XN4.js} +5 -5
  68. package/chunks/{chunk-E4WHKETF.js → chunk-FVKHVJZQ.js} +2 -49
  69. package/chunks/{chunk-NKST54XA.js → chunk-FZBZSYKU.js} +1 -1
  70. package/chunks/{chunk-XZCMQUOF.js → chunk-GDXET5XU.js} +31 -3387
  71. package/chunks/{chunk-A5YZ3FA2.js → chunk-GM463R2B.js} +1 -1
  72. package/chunks/{chunk-MOHAWIAW.js → chunk-GNPNYXJB.js} +2 -2
  73. package/chunks/{chunk-AFZTZLFB.js → chunk-GSS34377.js} +3 -3
  74. package/chunks/{chunk-MVLM6PJK.js → chunk-H2UQ2M5Z.js} +6 -2
  75. package/chunks/{chunk-BK2UNYWD.js → chunk-HIBNSEJE.js} +12 -0
  76. package/chunks/{chunk-CXYBX4AL.js → chunk-HQB2KUAN.js} +1 -1
  77. package/chunks/{chunk-ZQL73PPE.js → chunk-HYRDKNUY.js} +2 -2
  78. package/chunks/{chunk-D7D7RXIJ.js → chunk-IC4Z6LX6.js} +1 -1
  79. package/chunks/{chunk-HU5TUALO.js → chunk-IIJEYBNH.js} +3 -3
  80. package/chunks/{chunk-DTAPI453.js → chunk-IUFV6FYU.js} +2 -2
  81. package/chunks/{chunk-MISUEWB4.js → chunk-J6SZID63.js} +12 -8
  82. package/chunks/{chunk-OWAK7LMW.js → chunk-KGJGEEVR.js} +7 -0
  83. package/chunks/{chunk-BFHXDAR7.js → chunk-KKBPOU75.js} +1 -1
  84. package/chunks/{chunk-XOZ2R6XI.js → chunk-KKJJB5X6.js} +3578 -1487
  85. package/chunks/{chunk-PQTK2FVO.js → chunk-KR4ZSC7K.js} +4 -4
  86. package/chunks/{chunk-NAYPZ72V.js → chunk-KXSDEOFU.js} +1 -1
  87. package/chunks/{chunk-MGSSJT27.js → chunk-L4N6TIP7.js} +5 -3
  88. package/chunks/{chunk-4T6QMNX2.js → chunk-LA7YLSR6.js} +185 -12
  89. package/chunks/{chunk-4E5PO6R7.js → chunk-LEMXGVEP.js} +4 -4
  90. package/chunks/{chunk-WVAESIM6.js → chunk-LZC3B5N2.js} +4 -4
  91. package/chunks/{chunk-6O5DVH27.js → chunk-M6HMMKVG.js} +1 -1
  92. package/chunks/{chunk-Z5S6KH2B.js → chunk-MAJS7AT4.js} +6 -6
  93. package/chunks/{chunk-XNZBXQCG.js → chunk-MKVZE4ZJ.js} +1 -1
  94. package/chunks/{chunk-I7JDGNG6.js → chunk-MVUQRLOH.js} +9 -5
  95. package/chunks/{chunk-GLWP3FDB.js → chunk-MZSJ43WV.js} +31 -2
  96. package/chunks/{chunk-ZPPP6VCL.js → chunk-NG2R4N5O.js} +1 -1
  97. package/chunks/chunk-NKLP3DCK.js +22 -0
  98. package/chunks/{chunk-WIRAPZ62.js → chunk-NLREQJFK.js} +7 -1
  99. package/chunks/{chunk-N47ZM2KI.js → chunk-OFI7IGST.js} +12 -12
  100. package/chunks/{chunk-DBIEZC7T.js → chunk-OH2VKQXH.js} +42 -16
  101. package/chunks/{chunk-C4GRKMUL.js → chunk-OHJLEW5J.js} +5 -5
  102. package/chunks/{chunk-3RXUYQJI.js → chunk-OIVKILPI.js} +49 -0
  103. package/chunks/{chunk-VDLXPEI5.js → chunk-OMKQH5C5.js} +3 -3
  104. package/chunks/{chunk-LBWNSLJK.js → chunk-ONSZH6VM.js} +2 -2
  105. package/chunks/{chunk-DYA25Y7U.js → chunk-OWMAZHIX.js} +1 -1
  106. package/chunks/{chunk-WAOLM6SQ.js → chunk-P2AJ7FTI.js} +1 -1
  107. package/chunks/{chunk-MFBUJMQ3.js → chunk-P3LEG54R.js} +4 -4
  108. package/chunks/{chunk-WVRJKSIG.js → chunk-PJOBIWUG.js} +1 -1
  109. package/chunks/{chunk-YUVTZ4UH.js → chunk-PQBHU2IA.js} +114 -18
  110. package/chunks/{chunk-GTM6IHBB.js → chunk-Q3JQ7OSU.js} +2 -1
  111. package/chunks/{chunk-IISDPZ5X.js → chunk-QETYKPMG.js} +1 -1
  112. package/chunks/{chunk-UKZEXS6K.js → chunk-R574ATRM.js} +11 -0
  113. package/chunks/{chunk-DOICUTPO.js → chunk-RERATVSK.js} +11 -10
  114. package/chunks/chunk-RGSBTHSU.js +3381 -0
  115. package/chunks/{chunk-6D6V55ZQ.js → chunk-RMH5OWLD.js} +1 -1
  116. package/chunks/{chunk-AVDEBBMI.js → chunk-RNVQB725.js} +2 -2
  117. package/chunks/{chunk-QWUYFMAR.js → chunk-RUL4S3K3.js} +87 -67
  118. package/chunks/{chunk-RTWF7FF7.js → chunk-RZHU3ILK.js} +5 -5
  119. package/chunks/{chunk-3DTM3RPU.js → chunk-S7CV4KO6.js} +3 -3
  120. package/chunks/{chunk-ZH7G3A4U.js → chunk-SCI7K3GM.js} +4 -4
  121. package/chunks/{chunk-YAVY33G4.js → chunk-SEZ556DZ.js} +1 -1
  122. package/chunks/{chunk-6KZP75GT.js → chunk-SHZYHGQH.js} +1 -1
  123. package/chunks/chunk-SOORFWKY.js +155 -0
  124. package/chunks/{chunk-NYZAL2W2.js → chunk-SPMY6CX6.js} +4 -4
  125. package/chunks/{chunk-EHMN6SEG.js → chunk-TDXGKHDN.js} +4 -4
  126. package/chunks/{chunk-XKTCA6BW.js → chunk-TLV2IMHR.js} +5 -5
  127. package/chunks/{chunk-XSGQKR37.js → chunk-TRLYP7OF.js} +358 -20
  128. package/chunks/{chunk-DG563ZCS.js → chunk-TYKC75AX.js} +3 -3
  129. package/chunks/{chunk-KVUBYJDM.js → chunk-TZEZYNYD.js} +1 -1
  130. package/chunks/{chunk-XC2VAFVZ.js → chunk-UVX4Y6IN.js} +1 -1
  131. package/chunks/{chunk-IKXE3AAC.js → chunk-VOARQPTA.js} +2 -2
  132. package/chunks/{chunk-TVFC3XF5.js → chunk-W4B7UBUS.js} +7 -7
  133. package/chunks/{chunk-M744OFVE.js → chunk-W7U4ESW2.js} +1 -1
  134. package/chunks/{chunk-URN76TKR.js → chunk-WCG5O7IP.js} +2 -2
  135. package/chunks/{chunk-A6SX5TAH.js → chunk-XCWO3SMQ.js} +9 -5
  136. package/chunks/{chunk-FDV3CXTD.js → chunk-Y4EWSQF7.js} +3 -3
  137. package/chunks/{chunk-3PRJYMXC.js → chunk-Y4NGLFGJ.js} +1 -1
  138. package/chunks/{chunk-DOA7MCDY.js → chunk-Y7E5LNLO.js} +973 -892
  139. package/chunks/{chunk-3LGZEYOF.js → chunk-YHEEE624.js} +2 -2
  140. package/chunks/{chunk-Y5HIDFKZ.js → chunk-YJQCUTT2.js} +4 -4
  141. package/chunks/{chunk-QONSRAEV.js → chunk-YP5KH5Y7.js} +1 -1
  142. package/chunks/{chunk-SQ3ZJOYL.js → chunk-ZJ3VGYHK.js} +20 -20
  143. package/chunks/{chunk-7GYP5COR.js → chunk-ZXU5UHMI.js} +1 -1
  144. package/chunks/{computer-use-GTOP2CVH.js → computer-use-KEAPATUY.js} +35 -35
  145. package/chunks/{config-utils-LIRO7NSU.js → config-utils-K7SCOWHD.js} +2 -2
  146. package/chunks/{contextCommand-K6T6RRF3.js → contextCommand-ZJA6A5K5.js} +38 -38
  147. package/chunks/{core-runtime-D2626QQU.js → core-runtime-QMJPWHCI.js} +35 -35
  148. package/chunks/create-sub-session-DJE6XICH.js +772 -0
  149. package/chunks/{create-sub-session-6EWLAZD5.js → create-sub-session-E4PBYXV5.js} +2 -2
  150. package/chunks/{cron-create-CCRIULJE.js → cron-create-GRBWYMIJ.js} +4 -4
  151. package/chunks/{cron-delete-FCFW6BGP.js → cron-delete-4CMKBR2M.js} +4 -4
  152. package/chunks/{cron-list-QDBVCHRF.js → cron-list-W7K4ASOA.js} +4 -4
  153. package/chunks/{daemon-CCIX4BMG.js → daemon-SCTQMM7Z.js} +165 -5
  154. package/chunks/{daemon-status-provider-2EHRZZVF.js → daemon-status-provider-FSWDPE2G.js} +45 -45
  155. package/chunks/{daemon-trust-policy-B3EPDJDR.js → daemon-trust-policy-P5L5VFAY.js} +41 -41
  156. package/chunks/{daemon-trust-policy-monitor-MFM5X26G.js → daemon-trust-policy-monitor-73JYN6ET.js} +41 -41
  157. package/chunks/{deferred-core-runtime-KXN7HLN7.js → deferred-core-runtime-BWRCCFTE.js} +35 -35
  158. package/chunks/discovery-XLHITE74.js +220 -0
  159. package/chunks/{display-image-GUJA352B.js → display-image-O2XFPQTC.js} +4 -4
  160. package/chunks/{dist-A7H2KKUC.js → dist-K4WWQBHS.js} +277 -98
  161. package/chunks/{dist-IKGVITP3.js → dist-KS4O2NKE.js} +99 -6
  162. package/chunks/{earlyInputCapture-WUNYYFOS.js → earlyInputCapture-L5ST23A6.js} +36 -36
  163. package/chunks/{edit-DMFCBP2D.js → edit-LHHRFNFK.js} +35 -35
  164. package/chunks/{enter-worktree-5SVBCXFC.js → enter-worktree-6NXCRZPF.js} +35 -35
  165. package/chunks/{enterPlanMode-QUIZFQ4M.js → enterPlanMode-ZVQT4U7D.js} +35 -35
  166. package/chunks/{environment-U6QP23VJ.js → environment-GJ2HWJOS.js} +38 -38
  167. package/chunks/{errors-GZWFA6ON.js → errors-6RLH3XXC.js} +38 -38
  168. package/chunks/{exit-worktree-FUB44KAF.js → exit-worktree-I2YLUHHY.js} +35 -35
  169. package/chunks/{exitPlanMode-XSHHYR73.js → exitPlanMode-7CCWKO7I.js} +35 -35
  170. package/chunks/{fast-path-JHQJSIMZ.js → fast-path-DVFD4HQD.js} +2 -2
  171. package/chunks/{gemini-O44ETOKT.js → gemini-7FWGOYD4.js} +78 -78
  172. package/chunks/{geminiContentGenerator-TTM4TZJM.js → geminiContentGenerator-V2WVLIXU.js} +6 -6
  173. package/chunks/{glob-YECOWTRW.js → glob-3N6GGI75.js} +35 -35
  174. package/chunks/{goal-tools-3AZKX5WQ.js → goal-tools-APOW6UXL.js} +3 -3
  175. package/chunks/{grep-BZO4LOTA.js → grep-V43ILZVX.js} +35 -35
  176. package/chunks/{handleAutoUpdate-TOO52D6H.js → handleAutoUpdate-A5WFVWIA.js} +40 -40
  177. package/chunks/{i18n-ARJYXZQ5.js → i18n-PLVQ4FBZ.js} +37 -37
  178. package/chunks/{image-gen-J6Q2YBEU.js → image-gen-7C6IK6DC.js} +9 -9
  179. package/chunks/{initializer-CEZ4RPQV.js → initializer-CVAIKZYL.js} +42 -42
  180. package/chunks/{installationInfo-AEGIIQAT.js → installationInfo-J6QDHNTK.js} +36 -36
  181. package/chunks/{keychain-token-storage-7BT4TQ2A.js → keychain-token-storage-TVAMTNP7.js} +2 -2
  182. package/chunks/{list-7XLPV5GC.js → list-ZZ4OUUWV.js} +44 -44
  183. package/chunks/{list-agents-GC4BRET7.js → list-agents-KFFIIS2G.js} +2 -2
  184. package/chunks/{loadedSettingsAdapter-YK5SBHOT.js → loadedSettingsAdapter-QRYRJSWL.js} +41 -41
  185. package/chunks/{loggingContentGenerator-I7QDXS43.js → loggingContentGenerator-OJCO75HM.js} +17 -17
  186. package/chunks/{loop-wakeup-SQQSBMEY.js → loop-wakeup-TLMJPMMP.js} +5 -5
  187. package/chunks/{ls-63CC3NNX.js → ls-UVVE2XGE.js} +4 -4
  188. package/chunks/{lsp-P6NVJVNT.js → lsp-QMEA2VLB.js} +2 -2
  189. package/chunks/{managed-npm-update-YSL7PA7G.js → managed-npm-update-PCOSCP2N.js} +48 -39
  190. package/chunks/{mcp-RKFQZXP6.js → mcp-M3WKKWKY.js} +41 -41
  191. package/chunks/{monitor-QHHJIKGL.js → monitor-GN24FH6C.js} +35 -35
  192. package/chunks/{nonInteractiveCli-XEBIE7MG.js → nonInteractiveCli-KPTU3VSQ.js} +75 -75
  193. package/chunks/{notebook-edit-SG7X2EQA.js → notebook-edit-4YM65BBO.js} +35 -35
  194. package/chunks/{openaiContentGenerator-63CVR33K.js → openaiContentGenerator-EDQT6GTV.js} +19 -19
  195. package/chunks/{pidfile-I3CXJGQD.js → pidfile-JOIIRN2C.js} +36 -36
  196. package/chunks/{processUtils-LJ3DSANX.js → processUtils-5SV4MX3Q.js} +2 -2
  197. package/chunks/{qwenContentGenerator-JINBVCH6.js → qwenContentGenerator-ODRJSUGR.js} +40 -40
  198. package/chunks/{qwenOAuth2-SY2O3NRR.js → qwenOAuth2-NESCMZ47.js} +8 -8
  199. package/chunks/{read-file-PGTXF25I.js → read-file-HPSODLPK.js} +10 -10
  200. package/chunks/{read-mcp-resource-RTSSEER5.js → read-mcp-resource-2TNSJTC3.js} +2 -2
  201. package/chunks/{record-artifact-E6AWQU2Z.js → record-artifact-RAON52FC.js} +3 -3
  202. package/chunks/{resumeHistoryUtils-FB2NFVZ2.js → resumeHistoryUtils-TXCOFFMQ.js} +42 -41
  203. package/chunks/{ripGrep-IY3KGPM6.js → ripGrep-ZKQGTQAO.js} +35 -35
  204. package/chunks/{run-qwen-serve-UYEMGPKA.js → run-qwen-serve-ZPKPKECF.js} +516 -87
  205. package/chunks/{runtime-CG3BKRFE.js → runtime-2FLNNM36.js} +44 -44
  206. package/chunks/{scheduler-VFUVGDE5.js → scheduler-365PJHNR.js} +35 -35
  207. package/chunks/{sdk-exporters-http-IL2346MJ.js → sdk-exporters-http-KHAB5JSD.js} +2 -2
  208. package/chunks/{sdk-impl-SYKGVWOZ.js → sdk-impl-KD7FSTC3.js} +2 -2
  209. package/chunks/{send-message-TL6FIMMN.js → send-message-ZM7MRUGP.js} +5 -5
  210. package/chunks/{serve-ZCCQ2G2L.js → serve-QD2RNLKJ.js} +42 -42
  211. package/chunks/{server-WS7LWXPP.js → server-XLJVLTYM.js} +7952 -611
  212. package/chunks/{session-IQLCT32T.js → session-3VTUHH4Q.js} +78 -78
  213. package/chunks/{settings-NX4EUIE6.js → settings-KULVPCQC.js} +40 -40
  214. package/chunks/{shell-THDYM4CS.js → shell-3SWW22MH.js} +35 -35
  215. package/chunks/{skill-OXXZBNQB.js → skill-KDHWYS7W.js} +14 -14
  216. package/chunks/{skill-settings-T3JZ33T2.js → skill-settings-NMY4JEJC.js} +40 -40
  217. package/chunks/{spawnChannel-COLJMSA7.js → spawnChannel-ZKS4Q5OU.js} +37 -37
  218. package/chunks/{standalone-update-ICW7H5IF.js → standalone-update-7CXPGAQ4.js} +38 -38
  219. package/chunks/{startInteractiveUI-6BDRXSMF.js → startInteractiveUI-2QQLEKM6.js} +473 -233
  220. package/chunks/{stdioHelpers-HGIYTWAY.js → stdioHelpers-7A2EE64Z.js} +5 -3
  221. package/chunks/{syntheticOutput-BN7E626U.js → syntheticOutput-ZLCUTKO2.js} +3 -3
  222. package/chunks/{task-create-2X4UP4F3.js → task-create-CQP5UEA3.js} +9 -9
  223. package/chunks/{task-list-WOLJPZ64.js → task-list-XLEZOJXP.js} +5 -5
  224. package/chunks/{task-stop-TNXRNAL2.js → task-stop-7ZQM5CFF.js} +2 -2
  225. package/chunks/{task-update-VDUSN36E.js → task-update-JA75U2SN.js} +9 -9
  226. package/chunks/{team-create-BCSMDK4Z.js → team-create-2WTNGAOX.js} +35 -35
  227. package/chunks/{team-delete-D2VMSDOT.js → team-delete-MHY3UZGY.js} +5 -5
  228. package/chunks/{team-plan-approval-B5YRLJFV.js → team-plan-approval-LZTYLEG4.js} +35 -35
  229. package/chunks/{terminal-image-renderer-PW6Z6VFY.js → terminal-image-renderer-R5HHAFJN.js} +41 -36
  230. package/chunks/{theme-manager-CMVKD2JH.js → theme-manager-PAUCSJFF.js} +36 -36
  231. package/chunks/{todoWrite-MHWHO5UO.js → todoWrite-UPVOHYGD.js} +4 -4
  232. package/chunks/{tool-search-SQTUSG6P.js → tool-search-2BFCGLI6.js} +13 -13
  233. package/chunks/{total-session-admission-XAXOSE46.js → total-session-admission-MK4SINFK.js} +41 -41
  234. package/chunks/{trustedFolders-Z2UAEG2V.js → trustedFolders-TJ5S26N7.js} +37 -37
  235. package/chunks/{update-relaunch-JBLI6LJF.js → update-relaunch-N7AYLARF.js} +6 -6
  236. package/chunks/{updateCheck-FDAVGOFT.js → updateCheck-QRZ3DVT5.js} +39 -39
  237. package/chunks/{useAutoAcceptIndicator-PDZFIYRM.js → useAutoAcceptIndicator-DBJ7Y5AF.js} +44 -44
  238. package/chunks/{validateNonInterActiveAuth-Q4763ONQ.js → validateNonInterActiveAuth-6ZQWUDS3.js} +72 -72
  239. package/chunks/{version-C7MPCGWD.js → version-AJQGQWXC.js} +1 -1
  240. package/chunks/{web-fetch-L7RAPOJB.js → web-fetch-QWJYRXFL.js} +13 -13
  241. package/chunks/{web-search-IDOYUOWX.js → web-search-2AOXJGHU.js} +9 -9
  242. package/chunks/web-shell-static-O73YPVVC.js +33 -0
  243. package/chunks/{workflow-HXURUJB5.js → workflow-ERH3SJG2.js} +36 -36
  244. package/chunks/{workspace-providers-status-JZ3GPWNU.js → workspace-providers-status-HZ474RA4.js} +44 -44
  245. package/chunks/{workspace-registration-store-SM2PWB6L.js → workspace-registration-store-6EUP4JXG.js} +1 -1
  246. package/chunks/{workspace-registry-6W4ES7BJ.js → workspace-registry-R4APRRNC.js} +41 -41
  247. package/chunks/{workspace-service-V47ZFJRX.js → workspace-service-HFIFDMU7.js} +46 -46
  248. package/chunks/{workspace-skills-status-6ZQ45XKA.js → workspace-skills-status-HAHEIHGK.js} +42 -42
  249. package/chunks/{workspace-trust-reconciler-6V25LMCN.js → workspace-trust-reconciler-65GTAGDK.js} +50 -50
  250. package/chunks/{write-file-H2QOF25K.js → write-file-B34CDELV.js} +35 -35
  251. package/chunks/{zoom-image-JTUSS43N.js → zoom-image-HPRBZHSS.js} +10 -10
  252. package/cli.js +15 -15
  253. package/package.json +3 -3
  254. package/web-shell/assets/{arc-DtI1rZcQ.js → arc-Ip-FFDJ0.js} +1 -1
  255. package/web-shell/assets/{architectureDiagram-3BPJPVTR-BPZwSVct.js → architectureDiagram-3BPJPVTR-CChqgwIb.js} +1 -1
  256. package/web-shell/assets/{blockDiagram-GPEHLZMM-BIQ03j4C.js → blockDiagram-GPEHLZMM-BHN-Tfss.js} +1 -1
  257. package/web-shell/assets/{c4Diagram-AAUBKEIU-CB0RyFGm.js → c4Diagram-AAUBKEIU-BhyjzDjN.js} +1 -1
  258. package/web-shell/assets/channel-Cu8qd6OB.js +1 -0
  259. package/web-shell/assets/{chunk-2J33WTMH-B3WoS-4H.js → chunk-2J33WTMH-BB9eyHrx.js} +1 -1
  260. package/web-shell/assets/{chunk-4BX2VUAB-C98xYZ1X.js → chunk-4BX2VUAB-BeV7ce_J.js} +1 -1
  261. package/web-shell/assets/{chunk-55IACEB6-CbTIWRwQ.js → chunk-55IACEB6-DO7kXDiV.js} +1 -1
  262. package/web-shell/assets/{chunk-727SXJPM-DWGnKTPu.js → chunk-727SXJPM-B27MPznD.js} +1 -1
  263. package/web-shell/assets/{chunk-AQP2D5EJ-4__QyCEX.js → chunk-AQP2D5EJ-DHIjqjD_.js} +1 -1
  264. package/web-shell/assets/{chunk-FMBD7UC4-DFtG1YCW.js → chunk-FMBD7UC4-DabMqyN2.js} +1 -1
  265. package/web-shell/assets/{chunk-ND2GUHAM-rvAhW51N.js → chunk-ND2GUHAM-Dw7Nnnxc.js} +1 -1
  266. package/web-shell/assets/{chunk-QZHKN3VN-DfiU9pwZ.js → chunk-QZHKN3VN-DdaZew1Q.js} +1 -1
  267. package/web-shell/assets/classDiagram-4FO5ZUOK-BI3KrXhq.js +1 -0
  268. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BI3KrXhq.js +1 -0
  269. package/web-shell/assets/{cose-bilkent-S5V4N54A-Cc4EIdxu.js → cose-bilkent-S5V4N54A-CgBpCYH5.js} +1 -1
  270. package/web-shell/assets/{dagre-BM42HDAG-CZNj4dGW.js → dagre-BM42HDAG-BB4IU8G0.js} +1 -1
  271. package/web-shell/assets/{diagram-2AECGRRQ-D5tiqQCr.js → diagram-2AECGRRQ-BcUTHU37.js} +1 -1
  272. package/web-shell/assets/{diagram-5GNKFQAL-pwzw0jVO.js → diagram-5GNKFQAL-B6Lyihyv.js} +1 -1
  273. package/web-shell/assets/{diagram-KO2AKTUF-4CWiM00x.js → diagram-KO2AKTUF-CRIJOjpG.js} +1 -1
  274. package/web-shell/assets/{diagram-LMA3HP47-Dd7rc7Qf.js → diagram-LMA3HP47-DdNiwWWT.js} +1 -1
  275. package/web-shell/assets/{diagram-OG6HWLK6-_vzV52ml.js → diagram-OG6HWLK6-Dz4b32Do.js} +1 -1
  276. package/web-shell/assets/{erDiagram-TEJ5UH35-CqbE2bx_.js → erDiagram-TEJ5UH35-D-8NGw77.js} +1 -1
  277. package/web-shell/assets/{flowDiagram-I6XJVG4X-CaxNr7w4.js → flowDiagram-I6XJVG4X-Dun26LyM.js} +1 -1
  278. package/web-shell/assets/{ganttDiagram-6RSMTGT7-AGjrtfUA.js → ganttDiagram-6RSMTGT7-F10Hu1Gc.js} +1 -1
  279. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-ADG2zSkc.js → gitGraphDiagram-PVQCEYII-Bf8Ez6-D.js} +1 -1
  280. package/web-shell/assets/{index-BOryKCqt.js → index-BXqw-nGq.js} +1 -1
  281. package/web-shell/assets/index-QQlUCJGg.css +5 -0
  282. package/web-shell/assets/index-hcgjV7vS.js +1768 -0
  283. package/web-shell/assets/{infoDiagram-5YYISTIA-CE3B_IoZ.js → infoDiagram-5YYISTIA-DVgTMLvj.js} +1 -1
  284. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-CN-Frhoi.js → ishikawaDiagram-YF4QCWOH-uT-KepvK.js} +1 -1
  285. package/web-shell/assets/{journeyDiagram-JHISSGLW-CGgezS93.js → journeyDiagram-JHISSGLW-Vg_XadQf.js} +1 -1
  286. package/web-shell/assets/{kanban-definition-UN3LZRKU-BqWijky5.js → kanban-definition-UN3LZRKU-Dyv1ESBE.js} +1 -1
  287. package/web-shell/assets/{linear-D8sfpZf6.js → linear-BMIepYX3.js} +1 -1
  288. package/web-shell/assets/{mermaid.core-3PA4QrzM.js → mermaid.core-zA8pJ9ag.js} +5 -5
  289. package/web-shell/assets/{mindmap-definition-RKZ34NQL-D9QxwiCY.js → mindmap-definition-RKZ34NQL-BCW_BZ_5.js} +1 -1
  290. package/web-shell/assets/{pieDiagram-4H26LBE5-Bioaf6uE.js → pieDiagram-4H26LBE5-DseNEGp2.js} +1 -1
  291. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DBb2AS73.js → quadrantDiagram-W4KKPZXB-6Z7wr1Fi.js} +1 -1
  292. package/web-shell/assets/{requirementDiagram-4Y6WPE33-DXOvZ3KB.js → requirementDiagram-4Y6WPE33-CVOlCRPf.js} +1 -1
  293. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-D9qg09Dm.js → sankeyDiagram-5OEKKPKP-CGhgrUJZ.js} +1 -1
  294. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DSAUIfpw.js → sequenceDiagram-3UESZ5HK-dlqmpLuQ.js} +1 -1
  295. package/web-shell/assets/{stateDiagram-AJRCARHV-BPZkzfQ3.js → stateDiagram-AJRCARHV-B9EW10Xb.js} +1 -1
  296. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CJTZJJ5X.js +1 -0
  297. package/web-shell/assets/{timeline-definition-PNZ67QCA-UDaQp9sP.js → timeline-definition-PNZ67QCA-BBRh0mgZ.js} +1 -1
  298. package/web-shell/assets/{vennDiagram-CIIHVFJN-2ouhAnHO.js → vennDiagram-CIIHVFJN-B8Gs-REP.js} +1 -1
  299. package/web-shell/assets/{wardley-L42UT6IY-Cq_WEvTK.js → wardley-L42UT6IY-D_y80Ur7.js} +1 -1
  300. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-Dt24aXOM.js → wardleyDiagram-YWT4CUSO-D-CNfHJ-.js} +1 -1
  301. package/web-shell/assets/{xychartDiagram-2RQKCTM6-A5Xk2WCH.js → xychartDiagram-2RQKCTM6-DQ5CvELo.js} +1 -1
  302. package/web-shell/index.html +2 -2
  303. package/chunks/create-sub-session-MWUWNBM6.js +0 -401
  304. package/web-shell/assets/channel-vfbVFuM1.js +0 -1
  305. package/web-shell/assets/classDiagram-4FO5ZUOK-Cvr8atq3.js +0 -1
  306. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-Cvr8atq3.js +0 -1
  307. package/web-shell/assets/index-Cb1LjsCv.css +0 -5
  308. package/web-shell/assets/index-CvJDaIy8.js +0 -1756
  309. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DInOqnhZ.js +0 -1
@@ -0,0 +1,3381 @@
1
+ // Force strict mode and setup for ESM
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
+ import {
12
+ CLIENT_MCP_OVER_WS_CONFIG_FLAG
13
+ } from "./chunk-R574ATRM.js";
14
+ import {
15
+ MAX_WORKSPACE_PATH_LENGTH,
16
+ translateAndCheckAbsoluteWorkspacePath
17
+ } from "./chunk-6PLDPT2C.js";
18
+ import {
19
+ writeStderrLine
20
+ } from "./chunk-KGJGEEVR.js";
21
+ import {
22
+ init_esbuild_shims
23
+ } from "./chunk-5O2XNYP6.js";
24
+ import {
25
+ __name
26
+ } from "./chunk-J2S4EL5Y.js";
27
+
28
+ // packages/cli/src/serve/auth/device-flow.ts
29
+ init_esbuild_shims();
30
+ import { randomUUID } from "node:crypto";
31
+ var SANITIZE_FOR_STDERR_RE = new RegExp(
32
+ // ASCII C0 (0x00–0x1f), DEL (0x7f), C1 (0x80–0x9f), plus Unicode
33
+ // lookalike controls:
34
+ // \u200b–\u200f: zero-width chars + LRM/RLM
35
+ // \u2028–\u2029: LINE / PARAGRAPH SEPARATOR (terminal newline equivalents)
36
+ // \u202a–\u202e: bidirectional EMBEDDING / OVERRIDE controls
37
+ // \u2066–\u2069: bidirectional ISOLATE controls (CVE-2021-42574)
38
+ // \ufeff: BOM / ZWNBSP
39
+ String.raw`[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]`,
40
+ "g"
41
+ );
42
+ function sanitizeForStderr(value) {
43
+ return value.replace(SANITIZE_FOR_STDERR_RE, "?");
44
+ }
45
+ __name(sanitizeForStderr, "sanitizeForStderr");
46
+ var DEVICE_FLOW_DEFAULT_INTERVAL_MS = 5e3;
47
+ var DEVICE_FLOW_TERMINAL_GRACE_MS = 5 * 6e4;
48
+ var DEVICE_FLOW_SWEEP_INTERVAL_MS = 3e4;
49
+ var DEVICE_FLOW_MAX_CONCURRENT = 4;
50
+ var DEVICE_FLOW_SLOW_DOWN_BUMP_MS = 5e3;
51
+ var DEVICE_FLOW_PERSIST_TIMEOUT_MS = 3e4;
52
+ var DEVICE_FLOW_START_TIMEOUT_MS = 3e4;
53
+ var DEVICE_FLOW_POLL_TIMEOUT_MS = 3e4;
54
+ var DEVICE_FLOW_MAX_EXPIRES_IN_SEC = 60 * 60;
55
+ var DEVICE_FLOW_MIN_EXPIRES_IN_SEC = 30;
56
+ var DEVICE_FLOW_MAX_INTERVAL_MS = 6e4;
57
+ var SECRET_BRAND = Symbol("DeviceFlowSecret");
58
+ var SECRETS = /* @__PURE__ */ new WeakMap();
59
+ function brandSecret(value) {
60
+ const wrapper = Object.freeze({
61
+ [SECRET_BRAND]: true,
62
+ toString: /* @__PURE__ */ __name(() => "[redacted]", "toString"),
63
+ toJSON: /* @__PURE__ */ __name(() => "[redacted]", "toJSON"),
64
+ [Symbol.toPrimitive]: () => "[redacted]"
65
+ });
66
+ SECRETS.set(wrapper, value);
67
+ return wrapper;
68
+ }
69
+ __name(brandSecret, "brandSecret");
70
+ function unsafeRevealSecret(secret) {
71
+ const value = SECRETS.get(secret);
72
+ if (value === void 0) {
73
+ throw new Error(
74
+ "unsafeRevealSecret: argument is not a BrandedSecret (was never registered, or its WeakMap binding was lost via serialization)"
75
+ );
76
+ }
77
+ return value;
78
+ }
79
+ __name(unsafeRevealSecret, "unsafeRevealSecret");
80
+ var UnsupportedDeviceFlowProviderError = class extends Error {
81
+ static {
82
+ __name(this, "UnsupportedDeviceFlowProviderError");
83
+ }
84
+ code = "unsupported_provider";
85
+ constructor(providerId) {
86
+ super(
87
+ `Unsupported device-flow provider (internal: declared but not registered): ${providerId}`
88
+ );
89
+ this.name = "UnsupportedDeviceFlowProviderError";
90
+ }
91
+ };
92
+ var TooManyActiveDeviceFlowsError = class extends Error {
93
+ static {
94
+ __name(this, "TooManyActiveDeviceFlowsError");
95
+ }
96
+ code = "too_many_active_flows";
97
+ constructor() {
98
+ super(
99
+ `Too many active device-flow attempts. Cancel one of the existing flows or wait for them to expire.`
100
+ );
101
+ this.name = "TooManyActiveDeviceFlowsError";
102
+ }
103
+ };
104
+ var UpstreamDeviceFlowError = class extends Error {
105
+ static {
106
+ __name(this, "UpstreamDeviceFlowError");
107
+ }
108
+ code = "upstream_error";
109
+ constructor(message) {
110
+ super(message);
111
+ this.name = "UpstreamDeviceFlowError";
112
+ }
113
+ };
114
+ var DeviceFlowPollTimeoutError = class extends Error {
115
+ static {
116
+ __name(this, "DeviceFlowPollTimeoutError");
117
+ }
118
+ code = "poll_timeout";
119
+ timeoutMs;
120
+ /**
121
+ * Runtime brand the registry sets ONLY on instances it constructed
122
+ * inside its own timer callback. Default `false` for any
123
+ * `new DeviceFlowPollTimeoutError(...)` call from outside the registry
124
+ * — prevents a provider from spoofing the timeout signal.
125
+ */
126
+ _isRegistryTimeout = false;
127
+ constructor(timeoutMs) {
128
+ super(`device-flow poll timeout after ${timeoutMs}ms`);
129
+ this.name = "DeviceFlowPollTimeoutError";
130
+ this.timeoutMs = timeoutMs;
131
+ }
132
+ };
133
+ function makeRegistryPollTimeoutError(timeoutMs) {
134
+ const err = new DeviceFlowPollTimeoutError(timeoutMs);
135
+ err._isRegistryTimeout = true;
136
+ return err;
137
+ }
138
+ __name(makeRegistryPollTimeoutError, "makeRegistryPollTimeoutError");
139
+ var DEVICE_FLOW_REGISTRY_LOCAL = "deviceFlowRegistry";
140
+ function setDeviceFlowRegistry(app, registry) {
141
+ app.locals[DEVICE_FLOW_REGISTRY_LOCAL] = registry;
142
+ }
143
+ __name(setDeviceFlowRegistry, "setDeviceFlowRegistry");
144
+ function getDeviceFlowRegistry(app) {
145
+ return app.locals[DEVICE_FLOW_REGISTRY_LOCAL];
146
+ }
147
+ __name(getDeviceFlowRegistry, "getDeviceFlowRegistry");
148
+ var DeviceFlowRegistry = class {
149
+ constructor(deps) {
150
+ this.deps = deps;
151
+ this.now = deps.now ?? (() => Date.now());
152
+ this.schedule = deps.schedule ?? ((ms, cb) => setTimeout(cb, ms));
153
+ this.scheduleInterval = deps.scheduleInterval ?? ((ms, cb) => setInterval(cb, ms));
154
+ this.clearScheduled = deps.clearScheduled ?? ((h) => clearTimeout(h));
155
+ this.clearScheduledInterval = deps.clearScheduledInterval ?? ((h) => clearInterval(h));
156
+ this.sweeperHandle = this.scheduleInterval(
157
+ DEVICE_FLOW_SWEEP_INTERVAL_MS,
158
+ () => this.sweep()
159
+ );
160
+ if (this.sweeperHandle && typeof this.sweeperHandle.unref === "function") {
161
+ this.sweeperHandle.unref();
162
+ }
163
+ }
164
+ static {
165
+ __name(this, "DeviceFlowRegistry");
166
+ }
167
+ byId = /* @__PURE__ */ new Map();
168
+ byProvider = /* @__PURE__ */ new Map();
169
+ /**
170
+ * Coalesces concurrent `start()` calls for the same `providerId`. Two
171
+ * SDK clients posting `POST /workspace/auth/device-flow` in parallel
172
+ * would otherwise both pass the "no existing pending entry" check,
173
+ * each call `provider.start()` (a real IdP round-trip), and one's
174
+ * write to `byProvider` would clobber the other — leaving an orphan
175
+ * `byId` entry with a still-running poll timer that consumes IdP
176
+ * quota for nothing. Mirrors `SharedTokenManager`'s in-flight refresh
177
+ * coalescing pattern.
178
+ */
179
+ inFlightStarts = /* @__PURE__ */ new Map();
180
+ sweeperHandle;
181
+ disposed = false;
182
+ now;
183
+ schedule;
184
+ scheduleInterval;
185
+ clearScheduled;
186
+ clearScheduledInterval;
187
+ /**
188
+ * Start a new device flow OR — under per-provider singleton semantics —
189
+ * return the existing pending entry (`attached: true`). The take-over
190
+ * branch deliberately does NOT re-call `provider.start()`; making the
191
+ * second POST a no-op (rather than a fresh IdP request) is the property
192
+ * that lets a reconnecting SDK pick up an in-flight login without
193
+ * burning IdP quota.
194
+ */
195
+ async start(params) {
196
+ if (this.disposed) {
197
+ throw new Error("DeviceFlowRegistry disposed");
198
+ }
199
+ const provider = this.deps.resolveProvider(params.providerId);
200
+ if (!provider) {
201
+ throw new UnsupportedDeviceFlowProviderError(params.providerId);
202
+ }
203
+ const existing = this.byProvider.get(params.providerId);
204
+ if (existing && existing.status === "pending") {
205
+ this.recordTakeover(existing, params.initiatorClientId);
206
+ return { view: toPublicView(existing), attached: true };
207
+ }
208
+ const inFlight = this.inFlightStarts.get(params.providerId);
209
+ if (inFlight) {
210
+ const result = await inFlight;
211
+ const justCreated = this.byProvider.get(params.providerId);
212
+ if (justCreated) {
213
+ this.recordTakeover(justCreated, params.initiatorClientId);
214
+ }
215
+ return { view: result.view, attached: true };
216
+ }
217
+ if (this.countActive() >= DEVICE_FLOW_MAX_CONCURRENT) {
218
+ throw new TooManyActiveDeviceFlowsError();
219
+ }
220
+ const promise = this.doStart(params, provider);
221
+ this.inFlightStarts.set(params.providerId, promise);
222
+ try {
223
+ return await promise;
224
+ } finally {
225
+ if (this.inFlightStarts.get(params.providerId) === promise) {
226
+ this.inFlightStarts.delete(params.providerId);
227
+ }
228
+ }
229
+ }
230
+ async doStart(params, provider) {
231
+ const cancelController = new AbortController();
232
+ let startResult;
233
+ let startTimer;
234
+ try {
235
+ startResult = await new Promise(
236
+ (resolve2, reject) => {
237
+ startTimer = this.schedule(DEVICE_FLOW_START_TIMEOUT_MS, () => {
238
+ try {
239
+ cancelController.abort(new Error("device-flow start timeout"));
240
+ } catch {
241
+ }
242
+ reject(
243
+ new UpstreamDeviceFlowError(
244
+ "device-flow start timeout (upstream IdP unresponsive)"
245
+ )
246
+ );
247
+ });
248
+ provider.start({ signal: cancelController.signal }).then(resolve2, reject);
249
+ }
250
+ );
251
+ } finally {
252
+ if (startTimer !== void 0) this.clearScheduled(startTimer);
253
+ }
254
+ if (this.disposed) {
255
+ throw new Error("DeviceFlowRegistry disposed during start");
256
+ }
257
+ const expiresInSec = Number.isFinite(startResult.expiresIn) && startResult.expiresIn > 0 ? Math.min(
258
+ DEVICE_FLOW_MAX_EXPIRES_IN_SEC,
259
+ Math.max(DEVICE_FLOW_MIN_EXPIRES_IN_SEC, startResult.expiresIn)
260
+ ) : 600;
261
+ const expiresAt = this.now() + expiresInSec * 1e3;
262
+ const intervalSec = Number.isFinite(startResult.interval) && startResult.interval > 0 ? startResult.interval : DEVICE_FLOW_DEFAULT_INTERVAL_MS / 1e3;
263
+ const intervalMs = Math.min(
264
+ DEVICE_FLOW_MAX_INTERVAL_MS,
265
+ Math.max(1e3, intervalSec * 1e3)
266
+ );
267
+ const entry = {
268
+ deviceFlowId: randomUUID(),
269
+ providerId: params.providerId,
270
+ deviceCode: startResult.deviceCode,
271
+ pkceVerifier: startResult.pkceVerifier,
272
+ userCode: startResult.userCode,
273
+ verificationUri: startResult.verificationUri,
274
+ verificationUriComplete: startResult.verificationUriComplete,
275
+ intervalMs,
276
+ expiresAt,
277
+ status: "pending",
278
+ initiatorClientId: params.initiatorClientId,
279
+ createdAt: this.now(),
280
+ cancelController
281
+ };
282
+ this.byId.set(entry.deviceFlowId, entry);
283
+ this.byProvider.set(entry.providerId, entry);
284
+ this.deps.events.publish(
285
+ {
286
+ type: "started",
287
+ data: {
288
+ deviceFlowId: entry.deviceFlowId,
289
+ providerId: entry.providerId,
290
+ expiresAt: entry.expiresAt
291
+ }
292
+ },
293
+ entry.initiatorClientId
294
+ );
295
+ this.deps.audit?.record({
296
+ deviceFlowId: entry.deviceFlowId,
297
+ providerId: entry.providerId,
298
+ clientId: entry.initiatorClientId,
299
+ status: "started",
300
+ expiresInMs: entry.expiresAt - this.now()
301
+ });
302
+ this.schedulePoll(entry, provider);
303
+ return { view: toPublicView(entry), attached: false };
304
+ }
305
+ get(deviceFlowId) {
306
+ const entry = this.byId.get(deviceFlowId);
307
+ if (!entry) return void 0;
308
+ return toPublicView(entry);
309
+ }
310
+ /**
311
+ * Cancel a pending flow. Idempotent on terminal entries (returns
312
+ * `{ alreadyTerminal: true }` and does NOT re-emit `cancelled` —
313
+ * RFC 7231 §4.3.5: DELETE may still be a 204 even when nothing was
314
+ * removed). Returns `undefined` for unknown ids so the route layer
315
+ * can map it to 404.
316
+ */
317
+ cancel(deviceFlowId, cancellerClientId) {
318
+ const entry = this.byId.get(deviceFlowId);
319
+ if (!entry) return void 0;
320
+ if (entry.persistInFlight) {
321
+ entry.cancelRequestedDuringPersist = true;
322
+ if (!entry.cancellerRecorded) {
323
+ entry.cancellerRecorded = true;
324
+ if (cancellerClientId) {
325
+ entry.cancellerClientId = cancellerClientId;
326
+ }
327
+ }
328
+ try {
329
+ entry.cancelController.abort(new Error("cancel during persist"));
330
+ } catch {
331
+ }
332
+ this.deps.audit?.record({
333
+ deviceFlowId: entry.deviceFlowId,
334
+ providerId: entry.providerId,
335
+ clientId: cancellerClientId,
336
+ status: "cancelled",
337
+ hint: "deferred (persist in flight; final state decided by persist resolution)"
338
+ });
339
+ return { alreadyTerminal: false };
340
+ }
341
+ if (!this.transitionTerminal(entry, "cancelled")) {
342
+ return { alreadyTerminal: true };
343
+ }
344
+ this.deps.events.publish(
345
+ {
346
+ type: "cancelled",
347
+ data: { deviceFlowId: entry.deviceFlowId }
348
+ },
349
+ cancellerClientId
350
+ );
351
+ this.deps.audit?.record({
352
+ deviceFlowId: entry.deviceFlowId,
353
+ providerId: entry.providerId,
354
+ clientId: cancellerClientId,
355
+ status: "cancelled"
356
+ });
357
+ return { alreadyTerminal: false };
358
+ }
359
+ /**
360
+ * Active = pending entries already installed in `byProvider` PLUS
361
+ * in-flight starts that haven't yet completed `provider.start()`.
362
+ * Terminal entries in grace don't count toward the cap. Including
363
+ * `inFlightStarts.size` prevents concurrent starts for distinct
364
+ * providers from bypassing the workspace-wide cap.
365
+ */
366
+ countActive() {
367
+ let n = 0;
368
+ for (const entry of this.byProvider.values()) {
369
+ if (entry.status === "pending") n += 1;
370
+ }
371
+ return n + this.inFlightStarts.size;
372
+ }
373
+ schedulePoll(entry, provider) {
374
+ if (entry.status !== "pending") return;
375
+ if (entry.deviceCode === void 0) return;
376
+ if (this.disposed) return;
377
+ entry.pollHandle = this.schedule(entry.intervalMs, () => {
378
+ void this.runPollTick(entry, provider);
379
+ });
380
+ if (entry.pollHandle && typeof entry.pollHandle.unref === "function") {
381
+ entry.pollHandle.unref();
382
+ }
383
+ }
384
+ async runPollTick(entry, provider) {
385
+ if (entry.status !== "pending") return;
386
+ if (this.disposed) return;
387
+ if (entry.deviceCode === void 0) return;
388
+ const now = this.now();
389
+ if (now >= entry.expiresAt) {
390
+ this.expireEntry(entry);
391
+ return;
392
+ }
393
+ entry.lastPolledAt = now;
394
+ let result;
395
+ let rawProviderError;
396
+ let pollTimedOut = false;
397
+ let pollTimer;
398
+ let providerPollPromise;
399
+ try {
400
+ result = await new Promise((resolve2, reject) => {
401
+ pollTimer = this.schedule(DEVICE_FLOW_POLL_TIMEOUT_MS, () => {
402
+ const timeoutError = makeRegistryPollTimeoutError(
403
+ DEVICE_FLOW_POLL_TIMEOUT_MS
404
+ );
405
+ try {
406
+ entry.cancelController.abort(timeoutError);
407
+ } catch {
408
+ }
409
+ reject(timeoutError);
410
+ });
411
+ providerPollPromise = provider.poll(
412
+ {
413
+ deviceCode: entry.deviceCode,
414
+ pkceVerifier: entry.pkceVerifier
415
+ },
416
+ { signal: entry.cancelController.signal }
417
+ );
418
+ providerPollPromise.then(resolve2, reject);
419
+ });
420
+ } catch (err) {
421
+ if (err instanceof DeviceFlowPollTimeoutError && err._isRegistryTimeout === true) {
422
+ pollTimedOut = true;
423
+ result = {
424
+ kind: "error",
425
+ errorKind: "upstream_error",
426
+ hint: `provider.poll() timed out after ${err.timeoutMs}ms; check IdP connectivity`
427
+ };
428
+ } else {
429
+ rawProviderError = err instanceof Error ? err.message : String(err);
430
+ result = {
431
+ kind: "error",
432
+ errorKind: "upstream_error",
433
+ hint: "provider.poll() failed; see daemon audit log for details"
434
+ };
435
+ }
436
+ } finally {
437
+ if (pollTimer !== void 0) this.clearScheduled(pollTimer);
438
+ }
439
+ if (pollTimedOut && providerPollPromise !== void 0) {
440
+ const tracked = providerPollPromise;
441
+ const auditDeviceFlowId = entry.deviceFlowId;
442
+ const auditProviderId = entry.providerId;
443
+ const auditClientId = entry.initiatorClientId;
444
+ const audit = this.deps.audit;
445
+ void tracked.then(
446
+ (latePollResult) => {
447
+ audit?.record({
448
+ deviceFlowId: auditDeviceFlowId,
449
+ providerId: auditProviderId,
450
+ clientId: auditClientId,
451
+ status: "failed",
452
+ errorKind: "upstream_error",
453
+ // Distinguish "real late response" (pending/slow_down/
454
+ // success) from "provider's abort cooperation" (error).
455
+ // `latePollResult.kind` is provider-controlled — sanitize
456
+ // before interpolation to prevent log injection.
457
+ 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`
458
+ });
459
+ },
460
+ (lateErr) => {
461
+ if (lateErr instanceof DeviceFlowPollTimeoutError && lateErr._isRegistryTimeout === true)
462
+ return;
463
+ const safeDetail = lateErr instanceof Error ? `${sanitizeForStderr(lateErr.name)} (message ${lateErr.message.length} bytes; raw suppressed)` : `<non-Error throw: ${typeof lateErr}>`;
464
+ audit?.record({
465
+ deviceFlowId: auditDeviceFlowId,
466
+ providerId: auditProviderId,
467
+ clientId: auditClientId,
468
+ status: "failed",
469
+ errorKind: "upstream_error",
470
+ hint: `lost_late_poll_after_timeout: provider.poll() rejected after ${DEVICE_FLOW_POLL_TIMEOUT_MS}ms ceiling: ${safeDetail}`
471
+ });
472
+ }
473
+ // Terminal `.catch` so a throw inside either handler doesn't
474
+ // surface as an unhandled rejection (audit is best-effort).
475
+ ).catch(() => {
476
+ });
477
+ }
478
+ if (this.disposed) return;
479
+ if (entry.status !== "pending") return;
480
+ switch (result.kind) {
481
+ case "pending":
482
+ this.schedulePoll(entry, provider);
483
+ return;
484
+ case "slow_down":
485
+ entry.intervalMs = Math.min(
486
+ DEVICE_FLOW_MAX_INTERVAL_MS,
487
+ entry.intervalMs + DEVICE_FLOW_SLOW_DOWN_BUMP_MS
488
+ );
489
+ this.deps.events.publish(
490
+ {
491
+ type: "throttled",
492
+ data: {
493
+ deviceFlowId: entry.deviceFlowId,
494
+ intervalMs: entry.intervalMs
495
+ }
496
+ },
497
+ entry.initiatorClientId
498
+ );
499
+ this.schedulePoll(entry, provider);
500
+ return;
501
+ case "success": {
502
+ let metadata = {};
503
+ let persistError;
504
+ let persistTimer;
505
+ let persistTimedOut = false;
506
+ entry.persistInFlight = true;
507
+ let persistTracker;
508
+ try {
509
+ persistTracker = result.persist({
510
+ signal: entry.cancelController.signal
511
+ });
512
+ } catch (syncErr) {
513
+ persistError = syncErr;
514
+ persistTracker = Promise.reject(syncErr);
515
+ persistTracker.catch(() => {
516
+ });
517
+ }
518
+ persistTracker.then(
519
+ (lateMeta) => {
520
+ if (!persistTimedOut) return;
521
+ this.deps.audit?.record({
522
+ deviceFlowId: entry.deviceFlowId,
523
+ providerId: entry.providerId,
524
+ clientId: entry.initiatorClientId,
525
+ status: "authorized",
526
+ hint: `lost_success_after_timeout (provider.persist ignored timeout signal; credentials on disk but registry already published persist_failed; expiresAt=${lateMeta.expiresAt ?? "unknown"})`
527
+ });
528
+ },
529
+ () => {
530
+ }
531
+ );
532
+ if (persistError === void 0) {
533
+ try {
534
+ metadata = await new Promise((resolve2, reject) => {
535
+ persistTimer = this.schedule(
536
+ DEVICE_FLOW_PERSIST_TIMEOUT_MS,
537
+ () => {
538
+ persistTimedOut = true;
539
+ try {
540
+ entry.cancelController.abort(new Error("persist timeout"));
541
+ } catch {
542
+ }
543
+ reject(new Error("persist timeout"));
544
+ }
545
+ );
546
+ persistTracker.then(resolve2, reject);
547
+ });
548
+ } catch (err) {
549
+ persistError = err;
550
+ } finally {
551
+ if (persistTimer !== void 0) this.clearScheduled(persistTimer);
552
+ entry.persistInFlight = false;
553
+ }
554
+ } else {
555
+ entry.persistInFlight = false;
556
+ }
557
+ if (this.disposed) return;
558
+ const cancelDuringPersist = entry.cancelRequestedDuringPersist === true;
559
+ if (persistError) {
560
+ if (cancelDuringPersist) {
561
+ if (this.transitionTerminal(entry, "cancelled")) {
562
+ this.deps.events.publish(
563
+ {
564
+ type: "cancelled",
565
+ data: { deviceFlowId: entry.deviceFlowId }
566
+ },
567
+ entry.cancellerClientId ?? entry.initiatorClientId
568
+ );
569
+ }
570
+ } else if (this.transitionTerminal(entry, "error", "persist_failed")) {
571
+ const pastExpiry = this.now() >= entry.expiresAt;
572
+ this.deps.events.publish(
573
+ {
574
+ type: "failed",
575
+ data: {
576
+ deviceFlowId: entry.deviceFlowId,
577
+ errorKind: "persist_failed",
578
+ hint: "credentials could not be written to the daemon filesystem \u2014 check disk space and permissions"
579
+ }
580
+ },
581
+ entry.initiatorClientId
582
+ );
583
+ this.deps.audit?.record({
584
+ deviceFlowId: entry.deviceFlowId,
585
+ providerId: entry.providerId,
586
+ clientId: entry.initiatorClientId,
587
+ status: "failed",
588
+ errorKind: "persist_failed",
589
+ ...pastExpiry ? {
590
+ hint: "persist_also_failed_past_expiry (root cause is disk I/O; entry was past expiresAt by the time persist resolved)"
591
+ } : {}
592
+ });
593
+ }
594
+ return;
595
+ }
596
+ if (this.transitionTerminal(entry, "authorized")) {
597
+ this.deps.events.publish(
598
+ {
599
+ type: "authorized",
600
+ data: {
601
+ deviceFlowId: entry.deviceFlowId,
602
+ providerId: entry.providerId,
603
+ expiresAt: metadata.expiresAt,
604
+ accountAlias: metadata.accountAlias
605
+ }
606
+ },
607
+ entry.initiatorClientId
608
+ );
609
+ this.deps.audit?.record({
610
+ deviceFlowId: entry.deviceFlowId,
611
+ providerId: entry.providerId,
612
+ clientId: entry.initiatorClientId,
613
+ status: "authorized",
614
+ ...cancelDuringPersist ? {
615
+ hint: "lost_success_kept (cancel during persist; credentials kept per IdP approval)"
616
+ } : {}
617
+ });
618
+ }
619
+ return;
620
+ }
621
+ case "error":
622
+ entry.hint = result.hint;
623
+ if (this.transitionTerminal(entry, "error", result.errorKind)) {
624
+ this.deps.events.publish(
625
+ {
626
+ type: "failed",
627
+ data: {
628
+ deviceFlowId: entry.deviceFlowId,
629
+ errorKind: result.errorKind,
630
+ hint: result.hint
631
+ }
632
+ },
633
+ entry.initiatorClientId
634
+ );
635
+ this.deps.audit?.record({
636
+ deviceFlowId: entry.deviceFlowId,
637
+ providerId: entry.providerId,
638
+ clientId: entry.initiatorClientId,
639
+ status: "failed",
640
+ errorKind: result.errorKind,
641
+ // When a provider threw, include the full raw message in
642
+ // the audit hint. When `rawProviderError` is undefined,
643
+ // use the structured `result.hint` so the audit trail
644
+ // matches the SSE event.
645
+ ...rawProviderError !== void 0 ? {
646
+ hint: `provider.poll() threw (raw): ${sanitizeForStderr(rawProviderError)}`
647
+ } : result.hint !== void 0 ? { hint: result.hint } : {}
648
+ });
649
+ }
650
+ return;
651
+ default: {
652
+ const _exhaustive = result;
653
+ void _exhaustive;
654
+ }
655
+ }
656
+ }
657
+ /**
658
+ * Record a take-over: a second SDK client posted for a provider that
659
+ * already has a pending entry. When the caller differs from the
660
+ * entry's initiator, stamp it on `lastOriginatorClientId` and emit
661
+ * an audit breadcrumb. No event publish — the `attached: true` HTTP
662
+ * response is the second caller's signal.
663
+ */
664
+ recordTakeover(entry, takeoverClientId) {
665
+ if (!takeoverClientId) return;
666
+ if (takeoverClientId === entry.initiatorClientId) return;
667
+ if (takeoverClientId === entry.lastOriginatorClientId) return;
668
+ entry.lastOriginatorClientId = takeoverClientId;
669
+ this.deps.audit?.record({
670
+ deviceFlowId: entry.deviceFlowId,
671
+ providerId: entry.providerId,
672
+ clientId: takeoverClientId,
673
+ status: "started",
674
+ hint: `take-over (per-provider singleton; original initiator=${entry.initiatorClientId ?? "(none)"})`
675
+ });
676
+ }
677
+ /**
678
+ * Drive a pending entry to the time-based `expired` terminal.
679
+ * Extracted so the poll-tick and sweeper share a single event shape.
680
+ * No-op if the entry has already transitioned.
681
+ */
682
+ expireEntry(entry) {
683
+ if (!this.transitionTerminal(entry, "expired", "expired_token")) return;
684
+ this.deps.events.publish(
685
+ {
686
+ type: "failed",
687
+ data: {
688
+ deviceFlowId: entry.deviceFlowId,
689
+ errorKind: "expired_token",
690
+ hint: "device-flow expired without authorization"
691
+ }
692
+ },
693
+ entry.initiatorClientId
694
+ );
695
+ this.deps.audit?.record({
696
+ deviceFlowId: entry.deviceFlowId,
697
+ providerId: entry.providerId,
698
+ clientId: entry.initiatorClientId,
699
+ status: "expired",
700
+ errorKind: "expired_token"
701
+ });
702
+ }
703
+ /**
704
+ * Move a pending entry to terminal state. Returns **`true` exactly once**
705
+ * — the call site that successfully drove the transition. Subsequent
706
+ * calls (sweeper × poll-tick race, double cancel, etc.) return `false`
707
+ * so the caller can suppress duplicate event publish + audit log.
708
+ *
709
+ * On a successful transition:
710
+ * 1. clears any pending poll timer
711
+ * 2. wipes the secret material from `entry.deviceCode` /
712
+ * `entry.pkceVerifier`. The PRIMARY guard against secret leaks
713
+ * is the `entry.status !== 'pending'` check at the top of
714
+ * `runPollTick` — a stale timer that managed to fire post-clear
715
+ * bails out before touching the entry. Secret-clearing here is
716
+ * DEFENSE IN DEPTH: even if a future refactor weakens the
717
+ * status guard, the registry's in-memory state can no longer
718
+ * hand out the upstream `device_code` to a late-arriving
719
+ * logger / serializer.
720
+ * 3. records `terminalAt` for the sweeper to evict after grace
721
+ * 4. removes the per-provider singleton index so a new POST creates
722
+ * a fresh flow instead of taking over the terminal one
723
+ */
724
+ transitionTerminal(entry, status, errorKind) {
725
+ if (entry.status !== "pending") return false;
726
+ entry.status = status;
727
+ if (errorKind) entry.errorKind = errorKind;
728
+ entry.terminalAt = this.now();
729
+ if (entry.pollHandle) {
730
+ this.clearScheduled(entry.pollHandle);
731
+ entry.pollHandle = void 0;
732
+ }
733
+ entry.deviceCode = void 0;
734
+ entry.pkceVerifier = void 0;
735
+ try {
736
+ entry.cancelController.abort();
737
+ } catch {
738
+ }
739
+ if (this.byProvider.get(entry.providerId) === entry) {
740
+ this.byProvider.delete(entry.providerId);
741
+ }
742
+ return true;
743
+ }
744
+ /**
745
+ * Periodic sweeper:
746
+ * (a) pending entries past `expiresAt` get a synthetic timeout event
747
+ * (the polling loop also handles this on its next tick, but a
748
+ * wedged poll path should not block expiry)
749
+ * (b) terminal entries past their grace window get evicted entirely
750
+ */
751
+ sweep() {
752
+ if (this.disposed) return;
753
+ const now = this.now();
754
+ for (const entry of [...this.byId.values()]) {
755
+ if (entry.persistInFlight) continue;
756
+ if (entry.status === "pending" && now >= entry.expiresAt) {
757
+ this.expireEntry(entry);
758
+ continue;
759
+ }
760
+ if (entry.status !== "pending" && entry.terminalAt !== void 0 && now - entry.terminalAt >= DEVICE_FLOW_TERMINAL_GRACE_MS) {
761
+ this.byId.delete(entry.deviceFlowId);
762
+ }
763
+ }
764
+ }
765
+ /**
766
+ * For diagnostics / GET /workspace/auth/status: report only pending
767
+ * flows. Terminal entries are an implementation detail of the SDK
768
+ * reconnect path and shouldn't be enumerated to all bearer-token
769
+ * holders.
770
+ */
771
+ listPending() {
772
+ const out = [];
773
+ for (const entry of this.byId.values()) {
774
+ if (entry.status === "pending") out.push(toPublicView(entry));
775
+ }
776
+ return out;
777
+ }
778
+ dispose() {
779
+ if (this.disposed) return;
780
+ this.disposed = true;
781
+ if (this.sweeperHandle) {
782
+ this.clearScheduledInterval(this.sweeperHandle);
783
+ this.sweeperHandle = void 0;
784
+ }
785
+ for (const entry of this.byId.values()) {
786
+ if (entry.pollHandle) {
787
+ this.clearScheduled(entry.pollHandle);
788
+ entry.pollHandle = void 0;
789
+ }
790
+ try {
791
+ entry.cancelController.abort();
792
+ } catch {
793
+ }
794
+ entry.deviceCode = void 0;
795
+ entry.pkceVerifier = void 0;
796
+ }
797
+ this.byId.clear();
798
+ this.byProvider.clear();
799
+ }
800
+ };
801
+ function toPublicView(entry) {
802
+ const base = {
803
+ deviceFlowId: entry.deviceFlowId,
804
+ providerId: entry.providerId,
805
+ status: entry.status,
806
+ createdAt: entry.createdAt,
807
+ initiatorClientId: entry.initiatorClientId
808
+ };
809
+ if (entry.errorKind) base.errorKind = entry.errorKind;
810
+ if (entry.hint) base.hint = entry.hint;
811
+ if (entry.lastPolledAt !== void 0) base.lastPolledAt = entry.lastPolledAt;
812
+ if (entry.status === "pending") {
813
+ base.userCode = entry.userCode;
814
+ base.verificationUri = entry.verificationUri;
815
+ base.verificationUriComplete = entry.verificationUriComplete;
816
+ base.expiresAt = entry.expiresAt;
817
+ base.intervalMs = entry.intervalMs;
818
+ }
819
+ return base;
820
+ }
821
+ __name(toPublicView, "toPublicView");
822
+
823
+ // packages/cli/src/serve/bridge-file-system-adapter.ts
824
+ init_esbuild_shims();
825
+ var ACP_WRITE_ROUTE = "ACP writeTextFile";
826
+ var ACP_READ_ROUTE = "ACP readTextFile";
827
+ function buildAuditContext(params, route) {
828
+ return {
829
+ route,
830
+ ...params.sessionId ? { sessionId: params.sessionId } : {}
831
+ };
832
+ }
833
+ __name(buildAuditContext, "buildAuditContext");
834
+ function createBridgeFileSystemAdapter(factory) {
835
+ return {
836
+ async writeText(params) {
837
+ const wfs = factory.forRequest(
838
+ buildAuditContext(params, ACP_WRITE_ROUTE)
839
+ );
840
+ const resolved = await wfs.resolve(params.path, "write");
841
+ await wfs.writeTextOverwrite(resolved, params.content);
842
+ return {};
843
+ },
844
+ async readText(params) {
845
+ const wfs = factory.forRequest(buildAuditContext(params, ACP_READ_ROUTE));
846
+ const resolved = await wfs.resolve(params.path, "read");
847
+ const opts = {};
848
+ if (typeof params.line === "number" && params.line > 0) {
849
+ opts.line = params.line;
850
+ }
851
+ if (typeof params.limit === "number" && params.limit > 0) {
852
+ opts.limit = params.limit;
853
+ }
854
+ const { content } = await wfs.readText(resolved, opts);
855
+ return { content };
856
+ }
857
+ };
858
+ }
859
+ __name(createBridgeFileSystemAdapter, "createBridgeFileSystemAdapter");
860
+
861
+ // packages/cli/src/serve/fs/path-mutex-registry.ts
862
+ init_esbuild_shims();
863
+ var PathMutexRegistry = class {
864
+ static {
865
+ __name(this, "PathMutexRegistry");
866
+ }
867
+ tails = /* @__PURE__ */ new Map();
868
+ async runExclusive(key, fn) {
869
+ const previous = this.tails.get(key) ?? Promise.resolve();
870
+ let release;
871
+ const current = new Promise((resolve2) => {
872
+ release = resolve2;
873
+ });
874
+ const tail = previous.catch(() => void 0).then(() => current);
875
+ this.tails.set(key, tail);
876
+ await previous.catch(() => void 0);
877
+ try {
878
+ return await fn();
879
+ } finally {
880
+ release();
881
+ if (this.tails.get(key) === tail) this.tails.delete(key);
882
+ }
883
+ }
884
+ };
885
+
886
+ // packages/cli/src/serve/health-query.ts
887
+ init_esbuild_shims();
888
+ function isDeepHealthQuery(raw) {
889
+ return raw === "1" || raw === "true" || raw === "";
890
+ }
891
+ __name(isDeepHealthQuery, "isDeepHealthQuery");
892
+
893
+ // packages/cli/src/serve/auth.ts
894
+ init_esbuild_shims();
895
+ import { createHash, timingSafeEqual } from "node:crypto";
896
+ var denyBrowserOriginCors = /* @__PURE__ */ __name((req, res, next) => {
897
+ if (req.headers.origin) {
898
+ res.setHeader("Vary", "Origin");
899
+ res.status(403).json({ error: "Request denied by CORS policy" });
900
+ return;
901
+ }
902
+ next();
903
+ }, "denyBrowserOriginCors");
904
+ var InvalidAllowOriginPatternError = class extends Error {
905
+ static {
906
+ __name(this, "InvalidAllowOriginPatternError");
907
+ }
908
+ pattern;
909
+ constructor(pattern, reason) {
910
+ super(
911
+ `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).`
912
+ );
913
+ this.name = "InvalidAllowOriginPatternError";
914
+ this.pattern = pattern;
915
+ }
916
+ };
917
+ function parseAllowOriginPatterns(raw) {
918
+ const origins = /* @__PURE__ */ new Set();
919
+ let allowAny = false;
920
+ for (const entry of raw) {
921
+ if (entry === "*") {
922
+ allowAny = true;
923
+ continue;
924
+ }
925
+ let parsed;
926
+ try {
927
+ parsed = new URL(entry);
928
+ } catch {
929
+ throw new InvalidAllowOriginPatternError(entry, "not a parseable URL");
930
+ }
931
+ const canonical = parsed.origin === "null" ? `${parsed.protocol}//${parsed.host}` : parsed.origin;
932
+ if (canonical !== entry) {
933
+ throw new InvalidAllowOriginPatternError(
934
+ entry,
935
+ `expected the canonical origin ${JSON.stringify(canonical)} without trailing slash, path, userinfo, or query`
936
+ );
937
+ }
938
+ origins.add(canonical.toLowerCase());
939
+ }
940
+ return { allowAny, origins };
941
+ }
942
+ __name(parseAllowOriginPatterns, "parseAllowOriginPatterns");
943
+ function allowOriginCors(patterns) {
944
+ const allowedMethods = "GET, POST, PATCH, DELETE, OPTIONS";
945
+ const allowedHeaders = "Authorization, Content-Type, X-Qwen-Client-Id, Last-Event-ID, X-Qwen-Event-Epoch";
946
+ const maxAgeSeconds = "86400";
947
+ const exposedHeaders = "Retry-After, X-Qwen-Event-Epoch, X-Qwen-SSE-Stream-Id";
948
+ return (req, res, next) => {
949
+ const origin = req.headers.origin;
950
+ if (!origin) {
951
+ next();
952
+ return;
953
+ }
954
+ if (origin === "null") {
955
+ res.setHeader("Vary", "Origin");
956
+ res.status(403).json({ error: "Request denied by CORS policy" });
957
+ return;
958
+ }
959
+ const matched = patterns.allowAny || patterns.origins.has(origin.toLowerCase());
960
+ if (matched) {
961
+ res.setHeader("Access-Control-Allow-Origin", origin);
962
+ res.setHeader("Vary", "Origin");
963
+ res.setHeader("Access-Control-Allow-Methods", allowedMethods);
964
+ res.setHeader("Access-Control-Allow-Headers", allowedHeaders);
965
+ res.setHeader("Access-Control-Max-Age", maxAgeSeconds);
966
+ res.setHeader("Access-Control-Expose-Headers", exposedHeaders);
967
+ if (req.method === "OPTIONS" && (req.headers["access-control-request-method"] || req.headers["access-control-request-headers"])) {
968
+ res.status(204).end();
969
+ return;
970
+ }
971
+ next();
972
+ return;
973
+ }
974
+ res.setHeader("Vary", "Origin");
975
+ res.status(403).json({ error: "Request denied by CORS policy" });
976
+ };
977
+ }
978
+ __name(allowOriginCors, "allowOriginCors");
979
+ function hostAllowlist(bind, getPort) {
980
+ if (!isLoopbackBind(bind)) {
981
+ return (_req, _res, next) => next();
982
+ }
983
+ let cachedPort = -1;
984
+ let cachedAllowed = /* @__PURE__ */ new Set();
985
+ const allowedFor = /* @__PURE__ */ __name((port) => {
986
+ if (port === cachedPort) return cachedAllowed;
987
+ cachedPort = port;
988
+ cachedAllowed = /* @__PURE__ */ new Set([
989
+ `localhost:${port}`,
990
+ `127.0.0.1:${port}`,
991
+ `[::1]:${port}`,
992
+ `host.docker.internal:${port}`
993
+ ]);
994
+ if (port === 80 || port === 443) {
995
+ cachedAllowed.add("localhost");
996
+ cachedAllowed.add("127.0.0.1");
997
+ cachedAllowed.add("[::1]");
998
+ cachedAllowed.add("host.docker.internal");
999
+ }
1000
+ return cachedAllowed;
1001
+ }, "allowedFor");
1002
+ return (req, res, next) => {
1003
+ const port = getPort();
1004
+ const host = (req.headers.host || "").toLowerCase();
1005
+ if (!allowedFor(port).has(host)) {
1006
+ res.status(403).json({ error: "Invalid Host header" });
1007
+ return;
1008
+ }
1009
+ next();
1010
+ };
1011
+ }
1012
+ __name(hostAllowlist, "hostAllowlist");
1013
+ function bearerAuth(token) {
1014
+ if (!token) {
1015
+ return (_req, _res, next) => next();
1016
+ }
1017
+ const expected = createHash("sha256").update(token, "utf8").digest();
1018
+ return (req, res, next) => {
1019
+ const header = req.headers.authorization;
1020
+ if (!header) {
1021
+ res.status(401).json({ error: "Unauthorized" });
1022
+ return;
1023
+ }
1024
+ const schemeEnd = header.indexOf(" ");
1025
+ if (schemeEnd <= 0) {
1026
+ res.status(401).json({ error: "Unauthorized" });
1027
+ return;
1028
+ }
1029
+ const scheme = header.slice(0, schemeEnd).toLowerCase();
1030
+ if (scheme !== "bearer") {
1031
+ res.status(401).json({ error: "Unauthorized" });
1032
+ return;
1033
+ }
1034
+ let credStart = schemeEnd + 1;
1035
+ while (credStart < header.length && (header.charCodeAt(credStart) === 32 || header.charCodeAt(credStart) === 9)) {
1036
+ credStart++;
1037
+ }
1038
+ const credentials = header.slice(credStart);
1039
+ if (credentials.length === 0) {
1040
+ res.status(401).json({ error: "Unauthorized" });
1041
+ return;
1042
+ }
1043
+ const candidate = createHash("sha256").update(credentials, "utf8").digest();
1044
+ if (!timingSafeEqual(candidate, expected)) {
1045
+ res.status(401).json({ error: "Unauthorized" });
1046
+ return;
1047
+ }
1048
+ next();
1049
+ };
1050
+ }
1051
+ __name(bearerAuth, "bearerAuth");
1052
+ function createMutationGate(deps) {
1053
+ const passthrough = /* @__PURE__ */ __name((_req, _res, next) => next(), "passthrough");
1054
+ if (deps.requireAuth || deps.tokenConfigured) {
1055
+ return () => passthrough;
1056
+ }
1057
+ const strictDenier = /* @__PURE__ */ __name((_req, res) => {
1058
+ res.status(401).json({
1059
+ error: "This route requires the daemon to be configured with a bearer token. Set QWEN_SERVER_TOKEN or pass --token to enable bearer auth.",
1060
+ code: "token_required"
1061
+ });
1062
+ }, "strictDenier");
1063
+ return (opts = {}) => opts.strict ? strictDenier : passthrough;
1064
+ }
1065
+ __name(createMutationGate, "createMutationGate");
1066
+
1067
+ // packages/cli/src/serve/acp-http/client-mcp-sender-registry.ts
1068
+ init_esbuild_shims();
1069
+ var ClientMcpSenderRegistry = class {
1070
+ static {
1071
+ __name(this, "ClientMcpSenderRegistry");
1072
+ }
1073
+ senders = /* @__PURE__ */ new Map();
1074
+ sessionSenders = /* @__PURE__ */ new Map();
1075
+ sessionScopedServerNames = /* @__PURE__ */ new Set();
1076
+ /**
1077
+ * Record a server's WS sender, owned by `owner` (the registering
1078
+ * connection's stable client id). Idempotent; last writer wins and takes
1079
+ * ownership, so the new owner's `delete` is the one that takes effect.
1080
+ */
1081
+ set(serverName, sender, owner) {
1082
+ this.senders.set(serverName, { sender, owner });
1083
+ }
1084
+ /**
1085
+ * Forget a server's WS sender — but only when `owner` still owns the entry.
1086
+ * Idempotent. The ownership guard stops a disconnecting connection from
1087
+ * clobbering an entry a later connection re-registered under the same name.
1088
+ */
1089
+ delete(serverName, owner) {
1090
+ if (this.senders.get(serverName)?.owner === owner) {
1091
+ this.senders.delete(serverName);
1092
+ }
1093
+ }
1094
+ /** Whether `owner` currently owns the entry for `serverName`. */
1095
+ owns(serverName, owner) {
1096
+ return this.senders.get(serverName)?.owner === owner;
1097
+ }
1098
+ setSession(serverName, sessionId, sender, owner) {
1099
+ let bySession = this.sessionSenders.get(serverName);
1100
+ if (!bySession) {
1101
+ bySession = /* @__PURE__ */ new Map();
1102
+ this.sessionSenders.set(serverName, bySession);
1103
+ }
1104
+ bySession.set(sessionId, { sender, owner });
1105
+ this.sessionScopedServerNames.add(serverName);
1106
+ }
1107
+ ownsSession(serverName, sessionId, owner) {
1108
+ return this.sessionSenders.get(serverName)?.get(sessionId)?.owner === owner;
1109
+ }
1110
+ deleteSession(serverName, sessionId, owner) {
1111
+ const bySession = this.sessionSenders.get(serverName);
1112
+ if (bySession?.get(sessionId)?.owner !== owner) return false;
1113
+ bySession.delete(sessionId);
1114
+ if (bySession.size === 0) {
1115
+ this.sessionSenders.delete(serverName);
1116
+ }
1117
+ return true;
1118
+ }
1119
+ /** Currently-registered server names (tests / accounting). */
1120
+ serverNames() {
1121
+ return [...this.senders.keys()];
1122
+ }
1123
+ /**
1124
+ * The {@link ClientMcpMessageSender} the bridge consumes. Returns a
1125
+ * `(payload) => Promise<payload>` bound to the named server, or `undefined`
1126
+ * when no client currently hosts it. The bridge passes a `JSONRPCMessage` as
1127
+ * `payload`; we keep the public type `unknown` to match the bridge's
1128
+ * SDK-free contract.
1129
+ */
1130
+ lookup = /* @__PURE__ */ __name((serverName) => {
1131
+ const entry = this.senders.get(serverName);
1132
+ const sessionEntries = this.sessionSenders.get(serverName);
1133
+ if (!entry && !sessionEntries) return void 0;
1134
+ return (payload, context) => {
1135
+ if (context?.sessionId) {
1136
+ const sessionEntry = sessionEntries?.get(context.sessionId);
1137
+ if (sessionEntry) return sessionEntry.sender(payload);
1138
+ if (this.sessionScopedServerNames.has(serverName)) {
1139
+ return Promise.reject(
1140
+ new Error(
1141
+ `No session-scoped MCP sender for '${serverName}' in session '${context.sessionId}'.`
1142
+ )
1143
+ );
1144
+ }
1145
+ }
1146
+ if (this.sessionScopedServerNames.has(serverName)) {
1147
+ return Promise.reject(
1148
+ new Error(
1149
+ `Session-scoped MCP server '${serverName}' requires a session context.`
1150
+ )
1151
+ );
1152
+ }
1153
+ if (!entry) {
1154
+ return Promise.reject(
1155
+ new Error(`No client MCP sender for '${serverName}'.`)
1156
+ );
1157
+ }
1158
+ return entry.sender(
1159
+ serverName,
1160
+ payload
1161
+ );
1162
+ };
1163
+ }, "lookup");
1164
+ };
1165
+ function createClientMcpServerProvider(registry, bridge, originatorClientId) {
1166
+ return {
1167
+ async registerClientMcpServer(serverName, sendSdkMcpMessage) {
1168
+ registry.set(serverName, sendSdkMcpMessage, originatorClientId);
1169
+ try {
1170
+ const runtimeConfig = {
1171
+ // SDK-type so the child binds `SdkControlClientTransport`
1172
+ // (`isSdkMcpServerConfig`); the flag tells the child to KEEP the
1173
+ // type and bind `sendSdkMcpMessage` to the reverse ext-method.
1174
+ type: "sdk",
1175
+ [CLIENT_MCP_OVER_WS_CONFIG_FLAG]: true
1176
+ };
1177
+ const result = await bridge.addRuntimeMcpServer(
1178
+ serverName,
1179
+ runtimeConfig,
1180
+ originatorClientId
1181
+ );
1182
+ if (result.skipped) {
1183
+ registry.delete(serverName, originatorClientId);
1184
+ throw new Error(
1185
+ `runtime MCP add skipped: ${result.reason ?? "unknown"}`
1186
+ );
1187
+ }
1188
+ if (result.shadowedSettings) {
1189
+ await bridge.removeRuntimeMcpServer(serverName, originatorClientId).catch(() => {
1190
+ });
1191
+ throw new Error(
1192
+ `client MCP server '${serverName}' conflicts with a configured MCP server`
1193
+ );
1194
+ }
1195
+ return { toolCount: result.toolCount };
1196
+ } catch (err) {
1197
+ registry.delete(serverName, originatorClientId);
1198
+ throw err;
1199
+ }
1200
+ },
1201
+ async unregisterClientMcpServer(serverName) {
1202
+ if (!registry.owns(serverName, originatorClientId)) return;
1203
+ registry.delete(serverName, originatorClientId);
1204
+ await bridge.removeRuntimeMcpServer(serverName, originatorClientId).catch(() => {
1205
+ });
1206
+ }
1207
+ };
1208
+ }
1209
+ __name(createClientMcpServerProvider, "createClientMcpServerProvider");
1210
+
1211
+ // packages/cli/src/serve/capabilities.ts
1212
+ init_esbuild_shims();
1213
+ var SERVE_PROTOCOL_VERSION = "v1";
1214
+ var SUPPORTED_SERVE_PROTOCOL_VERSIONS = [
1215
+ SERVE_PROTOCOL_VERSION
1216
+ ];
1217
+ var SERVE_CAPABILITY_REGISTRY = {
1218
+ health: { since: "v1" },
1219
+ daemon_status: { since: "v1" },
1220
+ capabilities: { since: "v1" },
1221
+ session_create: { since: "v1" },
1222
+ session_scope_override: { since: "v1" },
1223
+ session_load: { since: "v1" },
1224
+ session_resume: { since: "v1" },
1225
+ // Deprecated alias — kept until @agentclientprotocol/sdk graduates
1226
+ // the underlying ACP method from unstable_resumeSession to resumeSession.
1227
+ unstable_session_resume: { since: "v1" },
1228
+ session_list: { since: "v1" },
1229
+ // Aggregate persisted session counts via
1230
+ // `GET /workspace/:id/session-info` (and the plural
1231
+ // `/workspaces/:workspace/session-info` twin). Performs a disk scan of
1232
+ // local JSONL files — advertised so clients can discover it, but the
1233
+ // response itself marks `expensive: true` / `cost: "disk_scan"` and
1234
+ // must not be polled in a tight loop.
1235
+ session_info: { since: "v1" },
1236
+ session_source_metadata: { since: "v1" },
1237
+ session_side_task: { since: "v1" },
1238
+ session_prompt: { since: "v1" },
1239
+ session_mid_turn_message_mutation: { since: "v1" },
1240
+ session_cancel: { since: "v1" },
1241
+ session_events: { since: "v1" },
1242
+ session_artifacts: { since: "v1" },
1243
+ session_artifacts_persistence: { since: "v1" },
1244
+ // Daemon emits `slow_client_warning` synthetic frames at 75% queue
1245
+ // fill and honors `?maxQueued=N` (range [16, 2048]) on
1246
+ // `GET /session/:id/events`. Old daemons silently lack both — SDK
1247
+ // clients pre-flight this tag before opting in.
1248
+ slow_client_warning: { since: "v1" },
1249
+ // SDK consumers can detect `KnownDaemonEvent` schema support without
1250
+ // pinning against this SDK release — `narrowDaemonEvent` falls back
1251
+ // to `kind: 'unknown'` for daemons that don't advertise the tag,
1252
+ // so the tag is purely informational.
1253
+ typed_event_schema: { since: "v1" },
1254
+ session_set_model: { since: "v1" },
1255
+ client_identity: { since: "v1" },
1256
+ client_heartbeat: { since: "v1" },
1257
+ session_permission_vote: { since: "v1" },
1258
+ permission_vote: { since: "v1" },
1259
+ workspace_mcp: { since: "v1" },
1260
+ workspace_skills: { since: "v1" },
1261
+ workspace_providers: { since: "v1" },
1262
+ workspace_acp_preheat: { since: "v1" },
1263
+ workspace_acp_status: { since: "v1" },
1264
+ auth_provider_install: { since: "v1" },
1265
+ // Workspace memory CRUD (`GET/POST /workspace/memory`). Daemon exposes
1266
+ // hierarchical QWEN.md state and accepts append/replace writes scoped
1267
+ // to either the bound workspace or the global ~/.qwen directory.
1268
+ workspace_memory: { since: "v1" },
1269
+ workspace_memory_remember: {
1270
+ since: "v1",
1271
+ modes: ["workspace", "clean"]
1272
+ },
1273
+ workspace_memory_forget: { since: "v1" },
1274
+ workspace_memory_dream: { since: "v1" },
1275
+ // Workspace agents CRUD (`GET/POST /workspace/agents` +
1276
+ // `GET/POST/DELETE /workspace/agents/:agentType`). Wraps
1277
+ // `SubagentManager` over HTTP so remote clients can list / read /
1278
+ // create / update / delete project- and user-level subagent
1279
+ // definitions. Built-in / extension agents stay read-only.
1280
+ workspace_agents: { since: "v1" },
1281
+ workspace_agent_generate: { since: "v1" },
1282
+ workspace_env: { since: "v1" },
1283
+ workspace_preflight: { since: "v1" },
1284
+ session_context: { since: "v1" },
1285
+ session_context_usage: { since: "v1" },
1286
+ session_supported_commands: { since: "v1" },
1287
+ session_tasks: { since: "v1" },
1288
+ session_monitor_tool_correlation: { since: "v1" },
1289
+ session_stats: { since: "v1" },
1290
+ session_lsp: { since: "v1" },
1291
+ session_status: { since: "v1" },
1292
+ session_close: { since: "v1" },
1293
+ session_archive: { since: "v1" },
1294
+ session_metadata: { since: "v1" },
1295
+ session_organization: { since: "v1" },
1296
+ session_export: { since: "v1" },
1297
+ session_transcript: { since: "v1" },
1298
+ session_transcript_pagination: { since: "v1" },
1299
+ // Daemon supports the MCP client guardrail surface: an in-process
1300
+ // counter exposed on `GET /workspace/mcp`, a `--mcp-client-budget=N`
1301
+ // flag with `--mcp-budget-mode={enforce, warn, off}`, and a
1302
+ // `disabledReason: 'budget'` tag on per-server cells when refused at
1303
+ // discovery. `modes` enumerates the implemented behaviors.
1304
+ mcp_guardrails: { since: "v1", modes: ["warn", "enforce"] },
1305
+ workspace_mcp_manage: { since: "v1" },
1306
+ // Daemon emits typed push events for MCP budget state crossings:
1307
+ // `mcp_budget_warning` and `mcp_child_refused_batch`. Always-on;
1308
+ // orthogonal to `mcp_guardrails` (the snapshot surface).
1309
+ mcp_guardrail_events: { since: "v1" },
1310
+ // Managed ACP invokes an authenticated external policy provider exactly once
1311
+ // at the final tool-execution boundary. Advertised only after the required
1312
+ // provider completed its startup handshake.
1313
+ external_tool_guard: { since: "v1", modes: ["required"] },
1314
+ // Always-on. Daemon supports runtime MCP server mutation via
1315
+ // `POST /workspace/mcp/servers` (add) and
1316
+ // `DELETE /workspace/mcp/servers/:name` (remove). SDK clients
1317
+ // pre-flight this tag before calling those routes.
1318
+ mcp_server_runtime_mutation: { since: "v1" },
1319
+ // Daemon supports the read-only workspace file surface:
1320
+ // `GET /file`, `GET /list`, `GET /glob`, `GET /stat`. The four
1321
+ // routes are gated as a single feature because they share the same
1322
+ // backing `WorkspaceFileSystem` boundary and failure shape.
1323
+ workspace_file_read: { since: "v1" },
1324
+ // Daemon supports bounded raw byte reads via `GET /file/bytes`.
1325
+ // Separate from `workspace_file_read` because older daemons may
1326
+ // advertise the text/list/stat/glob surface without byte-window
1327
+ // support.
1328
+ workspace_file_bytes: { since: "v1" },
1329
+ // Daemon supports byte-cursor paging on `GET /file`: responses carry
1330
+ // `nextCursor`/`hasMore` and requests accept `cursor`. A separate tag from
1331
+ // `workspace_file_read` because the convention here is that new behavior
1332
+ // gets a new tag — a client that preflighted the old one must not silently
1333
+ // receive a surface it cannot recognise. Same split as
1334
+ // `workspace_file_bytes` from `workspace_file_read`, and
1335
+ // `session_transcript_pagination` from `session_transcript`.
1336
+ workspace_file_read_cursor: { since: "v1" },
1337
+ // Daemon supports hash-aware text mutation routes
1338
+ // (`POST /file/write`, `POST /file/edit`) behind the strict mutation
1339
+ // gate. Clients should still pre-flight `require_auth` separately for
1340
+ // deployment posture; this tag only means the route contract exists.
1341
+ workspace_file_write: { since: "v1" },
1342
+ // Daemon hosts the session-level approval-mode
1343
+ // control route `POST /session/:id/approval-mode` (gated by the
1344
+ // mutation gate, strict). The route accepts `{mode, persist?}` —
1345
+ // `persist:true` also writes `tools.approvalMode` to workspace
1346
+ // settings via the daemon's `loadedSettings` handle. SDK helper:
1347
+ // `DaemonClient.setSessionApprovalMode`.
1348
+ session_approval_mode_control: { since: "v1" },
1349
+ // `POST /workspace/tools/:name/enable` toggles a
1350
+ // tool name in the workspace's `tools.disabled` settings list. The
1351
+ // bridge writes the settings file directly (no ACP roundtrip) and
1352
+ // fan-outs a `tool_toggled` event to all live session SSE buses.
1353
+ // Already-registered tools in active sessions are NOT retroactively
1354
+ // unregistered — the toggle takes effect on the next ACP child spawn
1355
+ // (`tools.disabled` is consulted at `Config` construction time).
1356
+ workspace_tool_toggle: { since: "v1" },
1357
+ workspace_skill_toggle: { since: "v1" },
1358
+ workspace_skill_manage: { since: "v1" },
1359
+ workspace_settings: { since: "v1" },
1360
+ // `GET /workspace/permissions` is always available when this tag is
1361
+ // advertised. `POST /workspace/permissions` updates the active ACP
1362
+ // child and returns `permission_session_required` when no live ACP
1363
+ // session exists; the tag means the route contract exists, not that
1364
+ // the current daemon state can accept a write.
1365
+ workspace_permissions: { since: "v1" },
1366
+ workspace_voice: { since: "v1" },
1367
+ workspace_voice_transcription: { since: "v1", modes: ["batch"] },
1368
+ // Inspect bound workspace trust and request local operator action.
1369
+ // Remote clients cannot directly write trustedFolders.json.
1370
+ workspace_trust: { since: "v1" },
1371
+ // Workspace trust policy changes rebuild the affected runtime generation
1372
+ // without restarting the daemon. V2 trust status exposes convergence.
1373
+ workspace_trust_hot_reload: { since: "v1" },
1374
+ // `POST /workspace/init` scaffolds an empty
1375
+ // `QWEN.md` (or whatever `getCurrentGeminiMdFilename()` returns) at
1376
+ // the bound workspace root. Body: `{force?: boolean}`. Default
1377
+ // refuses with 409 when the file already exists; `force: true`
1378
+ // overwrites. Mechanical only — does NOT call the LLM. To AI-fill
1379
+ // the file, the caller should follow up with
1380
+ // `POST /session/:id/prompt`.
1381
+ workspace_init: { since: "v1" },
1382
+ // `POST /workspace/setup-github` installs the fixed
1383
+ // qwen-code-action workflow set into the bound workspace after
1384
+ // explicit consent. The route reuses the interactive `/setup-github`
1385
+ // release lookup, workflow download, and `.gitignore` update logic.
1386
+ workspace_github_setup: { since: "v1" },
1387
+ // `GET /workspaces/:workspace/github/prs` lists the open pull requests
1388
+ // of the workspace's GitHub remote via the `gh` CLI (read-only). The
1389
+ // tag means the route contract exists; runtime availability of `gh`
1390
+ // and its auth is reported per-request through the
1391
+ // `github_cli_unavailable` / `github_prs_failed` error codes.
1392
+ workspace_github_prs: { since: "v1" },
1393
+ // `POST /workspace/mcp/:server/restart` performs
1394
+ // a single-server MCP restart (disconnect + reconnect + rediscover)
1395
+ // through the ACP child's `McpClientManager`. Pre-checks the live
1396
+ // budget snapshot: when the target server is not
1397
+ // already in `reservedSlots` AND the live count would exceed the
1398
+ // configured budget under `enforce` mode, returns 200 with
1399
+ // `{restarted:false, skipped:true, reason:'budget_would_exceed'}`
1400
+ // rather than triggering a refusal cascade. Other skip reasons:
1401
+ // `'in_flight'` (concurrent discovery in progress), `'disabled'`
1402
+ // (server is configured but explicitly disabled).
1403
+ workspace_mcp_restart: { since: "v1" },
1404
+ // Daemon hosts `POST /session/:id/recap`, which
1405
+ // generates a one-sentence "where did I leave off" summary by
1406
+ // running `generateSessionRecap` (`core/services/sessionRecap.ts`) as
1407
+ // a side-query against the fast model. Non-strict mutation gate —
1408
+ // posture mirrors `/session/:id/prompt` (token cost, not state
1409
+ // mutation). The route returns `{sessionId, recap}` where `recap`
1410
+ // may be `null` for too-short histories or transient model failures
1411
+ // (best-effort, never throws). SDK helper: `DaemonClient.recapSession`.
1412
+ session_recap: { since: "v1" },
1413
+ // `POST /session/:id/generate` streams a stateless, tool-free model call.
1414
+ // The ACP child prefers fastModel and falls back to the main session model.
1415
+ session_generation: { since: "v1" },
1416
+ // `POST /workspace/generate` runs the same stateless, tool-free generation
1417
+ // protocol against the resolved workspace runtime without a live session.
1418
+ workspace_generation: { since: "v1" },
1419
+ // Side question (/btw) against the session's conversation context.
1420
+ // Single-turn, tool-free LLM call via runForkedAgent (cache path).
1421
+ session_btw: { since: "v1" },
1422
+ // Direct daemon-side shell execution for an existing session.
1423
+ // Advertised CONDITIONALLY: operators must explicitly enable it and
1424
+ // configure bearer auth. Clients must still send a session-bound
1425
+ // X-Qwen-Client-Id when calling the route.
1426
+ session_shell_command: { since: "v1" },
1427
+ // Daemon hosts a workspace-shared MCP transport
1428
+ // pool (`QwenAgent.mcpPool`); `GET /workspace/mcp` reflects pool-level
1429
+ // accounting (`entryCount`, `entrySummary` on each per-server cell).
1430
+ // Advertised CONDITIONALLY — the kill switch
1431
+ // `QWEN_SERVE_NO_MCP_POOL=1` env var falls back to per-session MCP
1432
+ // clients and the tag is omitted so SDK consumers
1433
+ // pre-flighting on the tag get accurate "pool is on" semantics.
1434
+ mcp_workspace_pool: { since: "v1" },
1435
+ // `POST /workspace/mcp/:server/restart`
1436
+ // accepts an optional `?entryIndex=N` (or `*`) query parameter
1437
+ // and may return the new `{entries: RestartResult[]}` shape when
1438
+ // the pool holds multiple entries for the same server name (e.g.
1439
+ // sessions injected divergent OAuth headers). Single-entry
1440
+ // restarts continue to return the legacy `{restarted, durationMs}`
1441
+ // shape for compatibility with pre-F2 SDK clients. Advertised
1442
+ // CONDITIONALLY in lockstep with `mcp_workspace_pool`: pool
1443
+ // off → both tags absent, pool on → both tags present. Operators
1444
+ // pre-flighting on this tag can branch on whether the response
1445
+ // shape may include `entries[]`.
1446
+ mcp_pool_restart: { since: "v1" },
1447
+ // Daemon was booted with `--require-auth` (or
1448
+ // `requireAuth: true`), so even loopback callers must carry a bearer
1449
+ // token. Advertised CONDITIONALLY — only when the flag is on — so
1450
+ // SDK clients can branch on its presence to surface a clear "this
1451
+ // deployment requires auth" hint instead of speculatively trying
1452
+ // requests and parsing the resulting 401 body. Loopback developer
1453
+ // defaults (no flag) omit the tag, preserving the bit-for-bit shape
1454
+ // older clients expect.
1455
+ require_auth: { since: "v1" },
1456
+ // Daemon was booted with `--allow-origin <pattern>`
1457
+ // (at least one entry, including the `*` literal). Advertised
1458
+ // CONDITIONALLY — only when the flag is set — so browser SDK clients
1459
+ // can pre-flight whether the daemon will honor their cross-origin
1460
+ // request before issuing it (and parsing a 403). The configured
1461
+ // pattern list is intentionally NOT echoed in the capabilities
1462
+ // envelope — browser webui knows its own origin, and surfacing the
1463
+ // list would let an unauthenticated `/capabilities` reader
1464
+ // enumerate every trusted origin, which is useful recon for a
1465
+ // misconfigured deployment.
1466
+ allow_origin: { since: "v1" },
1467
+ // Daemon exposes the device-flow auth surface
1468
+ // (`POST /workspace/auth/device-flow`, GET/DELETE on `/:id`, and
1469
+ // `GET /workspace/auth/status`). Advertised UNCONDITIONALLY: the
1470
+ // routes themselves return `400 unsupported_provider` if the daemon
1471
+ // can't satisfy a specific provider, so clients always probe via the
1472
+ // route. The list of supported providers is surfaced through the
1473
+ // status route (extension data on `/capabilities` would inflate the
1474
+ // descriptor shape; we keep the registry uniform).
1475
+ auth_device_flow: { since: "v1" },
1476
+ permission_mediation: {
1477
+ since: "v1",
1478
+ modes: ["first-responder", "designated", "consensus", "local-only"]
1479
+ },
1480
+ prompt_absolute_deadline: { since: "v1" },
1481
+ writer_idle_timeout: { since: "v1" },
1482
+ non_blocking_prompt: { since: "v1" },
1483
+ session_language: { since: "v1" },
1484
+ session_rewind: { since: "v1" },
1485
+ workspace_hooks: { since: "v1" },
1486
+ session_hooks: { since: "v1" },
1487
+ workspace_extensions: { since: "v1" },
1488
+ session_branch: { since: "v1" },
1489
+ rate_limit: { since: "v1" },
1490
+ workspace_reload: { since: "v1" },
1491
+ // Immediate best-effort channel delivery for prompt/scheduled finals and
1492
+ // direct workspace notifications. Presence describes the route/event
1493
+ // contract; current worker availability is reported per delivery.
1494
+ channel_delivery: { since: "v1" },
1495
+ // Daemon supports reloading its daemon-managed channel worker via
1496
+ // `POST /workspace/channel/reload`. The worker is stopped and relaunched;
1497
+ // on relaunch it re-reads settings.json (channels / proxy / per-channel
1498
+ // model), so channel settings changes apply without a full daemon restart.
1499
+ // Advertised CONDITIONALLY while the runtime manager has a committed or
1500
+ // recoverable channel worker selection.
1501
+ channel_reload: { since: "v1" },
1502
+ // Runtime GET/PUT/DELETE control for daemon-managed channel selection.
1503
+ // The route exists even when no selection was supplied at daemon boot.
1504
+ channel_control: { since: "v1" },
1505
+ // Sanitized workspace Channel configuration, lifecycle, and pairing.
1506
+ channel_management: { since: "v1" },
1507
+ // Read-only workspace graph of recently observed channel contacts.
1508
+ workspace_channel_observed_contacts: { since: "v1" },
1509
+ // Multi-workspace session routing. Advertised only when one daemon hosts
1510
+ // more than one registered workspace runtime.
1511
+ multi_workspace_sessions: { since: "v1" },
1512
+ // Singular session rewind routes resolve the owning live workspace runtime.
1513
+ multi_workspace_session_rewind: { since: "v1" },
1514
+ // Singular session shell routes resolve the owning live workspace runtime.
1515
+ multi_workspace_session_shell: { since: "v1" },
1516
+ dynamic_workspace_registration: { since: "v1" },
1517
+ persistent_workspace_registration: { since: "v1" },
1518
+ // Optional presentation-only names and updates to those names for workspace
1519
+ // runtimes. Workspace ids and canonical paths remain the routing identities.
1520
+ workspace_display_name: { since: "v1" },
1521
+ scratch_workspace_registration: { since: "v1" },
1522
+ workspace_runtime_removal: { since: "v1" },
1523
+ // Workspace-qualified core REST routes under `/workspaces/:workspace/...`.
1524
+ // Covers core file/status/permissions/trust/lifecycle/MCP/tool, memory,
1525
+ // workspace agent CRUD, and persisted session organization surfaces.
1526
+ // Workspace-qualified settings also require the existing
1527
+ // `workspace_settings` tag because that surface depends on settings
1528
+ // persistence. ACP/WebSocket and auth stay outside this core tag;
1529
+ // workspace-qualified Voice REST/WebSocket routes use their separate
1530
+ // `workspace_qualified_voice` capability below. V2 extension management
1531
+ // is advertised separately via `extension_management_v2`.
1532
+ workspace_qualified_rest_core: { since: "v1" },
1533
+ // Workspace-qualified Voice REST and WebSocket routes. This tag is enough
1534
+ // to discover plural modalities because legacy Voice tags describe only
1535
+ // the primary runtime and may be absent for a secondary-only setup.
1536
+ workspace_qualified_voice: { since: "v1" },
1537
+ // Workspace-qualified managed-memory routes
1538
+ // (`/workspaces/:workspace/memory/{remember,forget,dream}`). Each
1539
+ // registered workspace gets its own task lane; the primary lane is the
1540
+ // same instance as the singular `/workspace/memory` surface.
1541
+ workspace_qualified_memory: { since: "v1" },
1542
+ // Global extension catalog/mutations plus workspace-qualified activation
1543
+ // projections. This is additive to the legacy primary-workspace
1544
+ // `workspace_extensions` contract.
1545
+ extension_management_v2: { since: "v1" },
1546
+ // Workspace-qualified, daemon-local persisted transcript paging. The tag is
1547
+ // unconditional because the route also serves a trusted single-workspace
1548
+ // primary; authorization is evaluated for the selected runtime per request.
1549
+ workspace_persisted_transcript: { since: "v1" },
1550
+ // Workspace-qualified full session export from active persisted storage.
1551
+ // This is separate from `session_export` so clients do not infer the plural
1552
+ // route from the legacy primary-workspace export capability.
1553
+ workspace_session_export: { since: "v1" },
1554
+ // Workspace-qualified full session export from archived persisted storage.
1555
+ // This remains independent from active export so older daemons cannot ignore
1556
+ // archive intent and return an active transcript with the same session id.
1557
+ workspace_archived_session_export: { since: "v1" },
1558
+ // Workspace-qualified ACP transport (issue #6378 Phase 4):
1559
+ // `/workspaces/:workspace/acp` mounts a per-runtime ACP dispatcher (HTTP +
1560
+ // WebSocket) for each registered workspace, with per-runtime device-flow and
1561
+ // reverse client-MCP. Legacy `/acp` stays bound to the primary runtime.
1562
+ // Advertised only when the daemon hosts more than one workspace runtime.
1563
+ workspace_qualified_acp: { since: "v1" },
1564
+ // Phase 2 "reverse tool channel" (issue #5626). A connected WS client (e.g.
1565
+ // the Chrome extension) can host an MCP server that the daemon's agent
1566
+ // calls by carrying `mcp_message` JSON-RPC frames over the daemon WS,
1567
+ // reusing the SDK-MCP-server control-plane pattern. Inbound WS frame types:
1568
+ // `mcp_register` { server }, `mcp_message` { id, server, payload }
1569
+ // (bidirectional, request/response correlated by `id`), `mcp_unregister`
1570
+ // { server }. Advertised CONDITIONALLY so clients pre-flight this tag before
1571
+ // attempting to register a client-hosted server. `runQwenServe` enables it
1572
+ // only when explicitly requested by option or env.
1573
+ client_mcp_over_ws: { since: "v1" },
1574
+ // Plan C "CDP tunnel" (issue #5626): the daemon exposes a `/cdp` WebSocket
1575
+ // where a loopback CDP client drives ONE real tab
1576
+ // via the extension's `chrome.debugger`, tunneled over `/acp` as `cdp_*`
1577
+ // frames. Advertised when explicitly enabled or when the daemon is serving a
1578
+ // Chrome extension origin.
1579
+ cdp_tunnel_over_ws: { since: "v1" },
1580
+ // Browser automation MCP tools are available only when the CDP tunnel is on
1581
+ // and the operator has configured an external stdio adapter via
1582
+ // QWEN_CDP_MCP_COMMAND. This is separate from `cdp_tunnel_over_ws`: a daemon
1583
+ // may expose the tunnel while intentionally not bundling/registering a
1584
+ // chrome-devtools MCP adapter.
1585
+ browser_automation_mcp: { since: "v1" },
1586
+ // Daemon hosts the `/voice/stream` WebSocket: the browser captures audio and
1587
+ // streams raw PCM, the daemon transcribes server-side via the configured
1588
+ // `voiceModel` (credentials never reach the client). Advertised
1589
+ // UNCONDITIONALLY (like `auth_device_flow`): presence means the endpoint
1590
+ // exists, not that a voice model is configured. The WS returns an `error`
1591
+ // frame when no transcribable `voiceModel` is set, so clients probe by
1592
+ // connecting rather than reading ambient settings into `/capabilities` (which
1593
+ // would make the envelope depend on the user's home config). `modes`
1594
+ // enumerates the two transcription paths (realtime vs. on-stop batch).
1595
+ voice_transcribe: { since: "v1", modes: ["streaming", "batch"] },
1596
+ // Process-global Live Voice control plane. Advertisement requires a macOS
1597
+ // WebShell daemon with native Host integration and the hot-applied enabled
1598
+ // gate. `/live/status` remains the dynamic readiness surface for the Host,
1599
+ // permissions, self-checks, and provider reachability.
1600
+ realtime_voice: { since: "v1" }
1601
+ };
1602
+ var CONDITIONAL_SERVE_FEATURES = /* @__PURE__ */ new Map([
1603
+ ["require_auth", (toggles) => toggles.requireAuth === true],
1604
+ ["mcp_workspace_pool", (toggles) => toggles.mcpPoolActive === true],
1605
+ ["mcp_pool_restart", (toggles) => toggles.mcpPoolActive === true],
1606
+ [
1607
+ "external_tool_guard",
1608
+ (toggles) => toggles.externalToolGuardActive === true
1609
+ ],
1610
+ ["allow_origin", (toggles) => toggles.allowOriginActive === true],
1611
+ [
1612
+ "prompt_absolute_deadline",
1613
+ (toggles) => typeof toggles.promptDeadlineMs === "number" && toggles.promptDeadlineMs > 0
1614
+ ],
1615
+ [
1616
+ "writer_idle_timeout",
1617
+ (toggles) => typeof toggles.writerIdleTimeoutMs === "number" && toggles.writerIdleTimeoutMs > 0
1618
+ ],
1619
+ ["workspace_settings", (toggles) => toggles.persistSettingAvailable === true],
1620
+ ["workspace_voice", (toggles) => toggles.persistSettingAvailable === true],
1621
+ [
1622
+ "workspace_voice_transcription",
1623
+ (toggles) => toggles.voiceTranscriptionAvailable === true
1624
+ ],
1625
+ [
1626
+ "session_shell_command",
1627
+ (toggles) => toggles.sessionShellCommandEnabled === true
1628
+ ],
1629
+ [
1630
+ "session_artifacts_persistence",
1631
+ (toggles) => toggles.sessionArtifactsPersistenceAvailable === true
1632
+ ],
1633
+ [
1634
+ "session_generation",
1635
+ (toggles) => toggles.sessionGenerationAvailable === true
1636
+ ],
1637
+ [
1638
+ "workspace_generation",
1639
+ (toggles) => toggles.workspaceGenerationAvailable === true
1640
+ ],
1641
+ ["rate_limit", (toggles) => toggles.rateLimit === true],
1642
+ ["workspace_reload", (toggles) => toggles.reloadAvailable === true],
1643
+ [
1644
+ "workspace_trust_hot_reload",
1645
+ (toggles) => toggles.workspaceTrustHotReloadAvailable === true
1646
+ ],
1647
+ ["channel_reload", (toggles) => toggles.channelReloadAvailable === true],
1648
+ ["channel_control", (toggles) => toggles.channelControlAvailable === true],
1649
+ [
1650
+ "channel_management",
1651
+ (toggles) => toggles.channelManagementAvailable === true
1652
+ ],
1653
+ [
1654
+ "multi_workspace_sessions",
1655
+ (toggles) => toggles.multiWorkspaceSessionsEnabled === true
1656
+ ],
1657
+ [
1658
+ "multi_workspace_session_rewind",
1659
+ (toggles) => toggles.multiWorkspaceSessionsEnabled === true
1660
+ ],
1661
+ [
1662
+ "multi_workspace_session_shell",
1663
+ (toggles) => toggles.multiWorkspaceSessionsEnabled === true && toggles.sessionShellCommandEnabled === true
1664
+ ],
1665
+ [
1666
+ "dynamic_workspace_registration",
1667
+ (toggles) => toggles.dynamicWorkspaceRegistrationAvailable === true
1668
+ ],
1669
+ [
1670
+ "persistent_workspace_registration",
1671
+ (toggles) => toggles.persistentWorkspaceRegistrationAvailable === true
1672
+ ],
1673
+ [
1674
+ "scratch_workspace_registration",
1675
+ (toggles) => toggles.scratchWorkspaceRegistrationAvailable === true
1676
+ ],
1677
+ [
1678
+ "workspace_runtime_removal",
1679
+ (toggles) => toggles.workspaceRuntimeRemovalAvailable === true
1680
+ ],
1681
+ [
1682
+ "workspace_qualified_acp",
1683
+ // The plural routes are pre-mounted for workspaces registered after app
1684
+ // creation, but the capability becomes meaningful only once a secondary
1685
+ // runtime exists. Until then the qualified primary route is only an alias
1686
+ // for the always-available legacy `/acp` surface.
1687
+ (toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
1688
+ ],
1689
+ [
1690
+ "workspace_qualified_voice",
1691
+ // Like qualified ACP, the plural Voice surface is mounted ahead of time
1692
+ // but only becomes useful once the daemon has a secondary runtime.
1693
+ (toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
1694
+ ],
1695
+ [
1696
+ "workspace_qualified_memory",
1697
+ (toggles) => toggles.acpHttpEnabled === true && toggles.multiWorkspaceSessionsEnabled === true
1698
+ ],
1699
+ ["client_mcp_over_ws", (toggles) => toggles.clientMcpOverWsEnabled === true],
1700
+ ["cdp_tunnel_over_ws", (toggles) => toggles.cdpTunnelOverWsEnabled === true],
1701
+ [
1702
+ "browser_automation_mcp",
1703
+ (toggles) => toggles.browserAutomationMcpAvailable === true
1704
+ ],
1705
+ [
1706
+ // Advertised whenever the `/voice/stream` WS endpoint exists. A configured
1707
+ // token (or `--require-auth`) no longer suppresses it: browsers can't set
1708
+ // an `Authorization` header on a WebSocket, so the Web Shell carries the
1709
+ // bearer token in the `Sec-WebSocket-Protocol` subprotocol, which the ACP
1710
+ // upgrade listener verifies (see acp-http/index.ts).
1711
+ "voice_transcribe",
1712
+ (toggles) => toggles.voiceWsAvailable !== false
1713
+ ],
1714
+ [
1715
+ "realtime_voice",
1716
+ (toggles) => toggles.acpHttpEnabled === true && toggles.realtimeVoiceEnabled === true
1717
+ ]
1718
+ ]);
1719
+ var SERVE_FEATURES = Object.freeze(
1720
+ Object.keys(SERVE_CAPABILITY_REGISTRY)
1721
+ );
1722
+ function serveProtocolVersionIndex(version) {
1723
+ return SUPPORTED_SERVE_PROTOCOL_VERSIONS.indexOf(version);
1724
+ }
1725
+ __name(serveProtocolVersionIndex, "serveProtocolVersionIndex");
1726
+ function isFeatureAvailableInProtocol(feature, protocolVersion) {
1727
+ return serveProtocolVersionIndex(SERVE_CAPABILITY_REGISTRY[feature].since) <= serveProtocolVersionIndex(protocolVersion);
1728
+ }
1729
+ __name(isFeatureAvailableInProtocol, "isFeatureAvailableInProtocol");
1730
+ function getAdvertisedServeFeatures(protocolVersion = SERVE_PROTOCOL_VERSION, toggles = {}) {
1731
+ return SERVE_FEATURES.filter((feature) => {
1732
+ if (!isFeatureAvailableInProtocol(feature, protocolVersion)) return false;
1733
+ const predicate = CONDITIONAL_SERVE_FEATURES.get(feature);
1734
+ if (predicate !== void 0) return predicate(toggles);
1735
+ return true;
1736
+ });
1737
+ }
1738
+ __name(getAdvertisedServeFeatures, "getAdvertisedServeFeatures");
1739
+ function getServeProtocolVersions() {
1740
+ return {
1741
+ current: SERVE_PROTOCOL_VERSION,
1742
+ supported: [...SUPPORTED_SERVE_PROTOCOL_VERSIONS]
1743
+ };
1744
+ }
1745
+ __name(getServeProtocolVersions, "getServeProtocolVersions");
1746
+
1747
+ // packages/cli/src/serve/types.ts
1748
+ init_esbuild_shims();
1749
+ var CAPABILITIES_SCHEMA_VERSION = 1;
1750
+
1751
+ // packages/cli/src/serve/managed-scratch-workspace.ts
1752
+ init_esbuild_shims();
1753
+ import { lstatSync, mkdirSync, realpathSync } from "node:fs";
1754
+ import { lstat, mkdtemp } from "node:fs/promises";
1755
+ import {
1756
+ basename,
1757
+ dirname,
1758
+ isAbsolute,
1759
+ join,
1760
+ relative,
1761
+ resolve
1762
+ } from "node:path";
1763
+ var isSamePath = /* @__PURE__ */ __name((left, right) => process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right, "isSamePath");
1764
+ var isWithinRoot = /* @__PURE__ */ __name((candidate, root) => {
1765
+ const rel = relative(root, candidate);
1766
+ return rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
1767
+ }, "isWithinRoot");
1768
+ function isManagedScratchChild(canonicalCwd, canonicalRoot) {
1769
+ return isSamePath(dirname(canonicalCwd), canonicalRoot) && /^scratch-.+/.test(basename(canonicalCwd));
1770
+ }
1771
+ __name(isManagedScratchChild, "isManagedScratchChild");
1772
+ function isScratchRootCompatible(canonicalCwd, canonicalRoot) {
1773
+ if (isSamePath(canonicalCwd, canonicalRoot)) return false;
1774
+ if (isManagedScratchChild(canonicalCwd, canonicalRoot)) return true;
1775
+ return !isWithinRoot(canonicalCwd, canonicalRoot) && !isWithinRoot(canonicalRoot, canonicalCwd);
1776
+ }
1777
+ __name(isScratchRootCompatible, "isScratchRootCompatible");
1778
+ function validateRootStats(stats) {
1779
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
1780
+ throw new Error("Managed scratch root must be a non-symlink directory");
1781
+ }
1782
+ if (process.platform !== "win32" && typeof process.getuid === "function" && stats.uid !== process.getuid()) {
1783
+ throw new Error("Managed scratch root must be owned by the daemon user");
1784
+ }
1785
+ if (process.platform !== "win32" && (stats.mode & 63) !== 0) {
1786
+ throw new Error(
1787
+ "Managed scratch root must be accessible only to its owner"
1788
+ );
1789
+ }
1790
+ }
1791
+ __name(validateRootStats, "validateRootStats");
1792
+ function prepareManagedScratchRoot(root, startupWorkspaceCwds) {
1793
+ mkdirSync(root, { recursive: true, mode: 448 });
1794
+ const before = lstatSync(root);
1795
+ validateRootStats(before);
1796
+ const canonicalRoot = realpathSync.native(resolve(root));
1797
+ const after = lstatSync(canonicalRoot);
1798
+ validateRootStats(after);
1799
+ if (before.dev !== after.dev || before.ino !== after.ino) {
1800
+ throw new Error("Managed scratch root identity changed during validation");
1801
+ }
1802
+ if (startupWorkspaceCwds.some(
1803
+ (cwd) => !isScratchRootCompatible(cwd, canonicalRoot)
1804
+ )) {
1805
+ throw new Error("Managed scratch root conflicts with a startup workspace");
1806
+ }
1807
+ return {
1808
+ canonicalRoot,
1809
+ device: after.dev,
1810
+ inode: after.ino
1811
+ };
1812
+ }
1813
+ __name(prepareManagedScratchRoot, "prepareManagedScratchRoot");
1814
+ async function revalidateManagedScratchRoot(root) {
1815
+ const stats = await lstat(root.canonicalRoot);
1816
+ validateRootStats(stats);
1817
+ if (stats.dev !== root.device || stats.ino !== root.inode) {
1818
+ throw new Error("Managed scratch root identity changed");
1819
+ }
1820
+ const canonical = realpathSync.native(root.canonicalRoot);
1821
+ if (!isSamePath(canonical, root.canonicalRoot)) {
1822
+ throw new Error("Managed scratch root canonical path changed");
1823
+ }
1824
+ }
1825
+ __name(revalidateManagedScratchRoot, "revalidateManagedScratchRoot");
1826
+ async function createManagedScratchDirectory(root) {
1827
+ await revalidateManagedScratchRoot(root);
1828
+ const created = await mkdtemp(join(root.canonicalRoot, "scratch-"));
1829
+ const canonical = realpathSync.native(created);
1830
+ if (!isManagedScratchChild(canonical, root.canonicalRoot)) {
1831
+ throw new Error("Managed scratch directory escaped its accepted root");
1832
+ }
1833
+ await revalidateManagedScratchRoot(root);
1834
+ return canonical;
1835
+ }
1836
+ __name(createManagedScratchDirectory, "createManagedScratchDirectory");
1837
+
1838
+ // packages/cli/src/serve/server/request-helpers.ts
1839
+ init_esbuild_shims();
1840
+ function sendJsonBodyParserError(res, err) {
1841
+ if (err instanceof SyntaxError && "status" in err && err.status === 400) {
1842
+ res.status(400).json({ error: "Invalid JSON in request body" });
1843
+ return true;
1844
+ }
1845
+ if (err && typeof err === "object" && "status" in err && err.status === 413) {
1846
+ res.status(413).json({ error: "Request body too large (max 10 MB)" });
1847
+ return true;
1848
+ }
1849
+ return false;
1850
+ }
1851
+ __name(sendJsonBodyParserError, "sendJsonBodyParserError");
1852
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
1853
+ "__proto__",
1854
+ "constructor",
1855
+ "prototype"
1856
+ ]);
1857
+ var CLIENT_ID_HEADER = "x-qwen-client-id";
1858
+ var MAX_CLIENT_ID_LENGTH = 128;
1859
+ var MAX_TOOL_NAME_LENGTH = 256;
1860
+ var MAX_SERVER_NAME_LENGTH = 256;
1861
+ var CLIENT_ID_RE = /^[A-Za-z0-9._:-]+$/;
1862
+ var INVALID_PERMISSION_OUTCOME_ERROR = '`outcome` must be `{ outcome: "cancelled" }` or `{ outcome: "selected", optionId: string }`';
1863
+ var deferredRuntimeTimingKey = Symbol(
1864
+ "deferredRuntimeRequestTiming"
1865
+ );
1866
+ function setDeferredRuntimeRequestTiming(req, timing) {
1867
+ req[deferredRuntimeTimingKey] = timing;
1868
+ }
1869
+ __name(setDeferredRuntimeRequestTiming, "setDeferredRuntimeRequestTiming");
1870
+ function getDeferredRuntimeRequestTiming(req) {
1871
+ return req[deferredRuntimeTimingKey];
1872
+ }
1873
+ __name(getDeferredRuntimeRequestTiming, "getDeferredRuntimeRequestTiming");
1874
+ function safeBody(req) {
1875
+ const raw = req.body;
1876
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1877
+ return /* @__PURE__ */ Object.create(null);
1878
+ }
1879
+ const out = /* @__PURE__ */ Object.create(null);
1880
+ for (const [key, value] of Object.entries(raw)) {
1881
+ if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
1882
+ out[key] = value;
1883
+ }
1884
+ return out;
1885
+ }
1886
+ __name(safeBody, "safeBody");
1887
+ function parseOptionalWorkspaceCwd(body, boundWorkspace, res) {
1888
+ const hasCwd = "cwd" in body;
1889
+ if (hasCwd && typeof body["cwd"] !== "string") {
1890
+ res.status(400).json({ error: "`cwd` must be a string absolute path when provided" });
1891
+ return void 0;
1892
+ }
1893
+ if (hasCwd && body["cwd"].length > MAX_WORKSPACE_PATH_LENGTH) {
1894
+ res.status(400).json({
1895
+ error: `\`cwd\` exceeds the ${MAX_WORKSPACE_PATH_LENGTH}-character limit`
1896
+ });
1897
+ return void 0;
1898
+ }
1899
+ const cwd = translateAndCheckAbsoluteWorkspacePath(
1900
+ hasCwd ? body["cwd"] : boundWorkspace
1901
+ );
1902
+ if (cwd === null) {
1903
+ res.status(400).json({ error: "`cwd` must be an absolute path when provided" });
1904
+ return void 0;
1905
+ }
1906
+ return cwd;
1907
+ }
1908
+ __name(parseOptionalWorkspaceCwd, "parseOptionalWorkspaceCwd");
1909
+ function requireSessionId(req, res) {
1910
+ const sessionId = req.params["id"];
1911
+ if (!sessionId) {
1912
+ res.status(400).json({ error: "`sessionId` route parameter is required" });
1913
+ return null;
1914
+ }
1915
+ return sessionId;
1916
+ }
1917
+ __name(requireSessionId, "requireSessionId");
1918
+ function parseClientIdHeader(req, res) {
1919
+ const raw = req.get(CLIENT_ID_HEADER);
1920
+ if (raw === void 0 || raw === "") return void 0;
1921
+ if (raw.length > MAX_CLIENT_ID_LENGTH || !CLIENT_ID_RE.test(raw)) {
1922
+ res.status(400).json({
1923
+ error: "`X-Qwen-Client-Id` must be a non-empty token of 128 characters or fewer",
1924
+ code: "invalid_client_id"
1925
+ });
1926
+ return null;
1927
+ }
1928
+ return raw;
1929
+ }
1930
+ __name(parseClientIdHeader, "parseClientIdHeader");
1931
+ function detectFromLoopback(req) {
1932
+ const addr = req.socket?.remoteAddress;
1933
+ if (typeof addr !== "string") return false;
1934
+ if (addr === "::1") return true;
1935
+ if (addr.startsWith("127.")) return true;
1936
+ if (addr.startsWith("::ffff:127.")) return true;
1937
+ return false;
1938
+ }
1939
+ __name(detectFromLoopback, "detectFromLoopback");
1940
+ function validateMcpRuntimeServerName(name, res) {
1941
+ if (typeof name !== "string" || name.length === 0) {
1942
+ res.status(400).json({
1943
+ error: "Server name is required and must be a non-empty string",
1944
+ code: "invalid_server_name"
1945
+ });
1946
+ return false;
1947
+ }
1948
+ if (name.length > MAX_SERVER_NAME_LENGTH) {
1949
+ res.status(400).json({
1950
+ error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
1951
+ code: "invalid_server_name"
1952
+ });
1953
+ return false;
1954
+ }
1955
+ if (!/^[A-Za-z0-9_-]+$/.test(name)) {
1956
+ res.status(400).json({
1957
+ error: "Server name must contain only alphanumeric characters, underscores, and hyphens",
1958
+ code: "invalid_server_name"
1959
+ });
1960
+ return false;
1961
+ }
1962
+ if (name === "__proto__" || name === "constructor" || name === "prototype") {
1963
+ res.status(400).json({
1964
+ error: "Server name must not be a reserved JS property name",
1965
+ code: "invalid_server_name"
1966
+ });
1967
+ return false;
1968
+ }
1969
+ return true;
1970
+ }
1971
+ __name(validateMcpRuntimeServerName, "validateMcpRuntimeServerName");
1972
+ function parseAndValidateWorkspaceClientId(req, res, bridge) {
1973
+ const raw = parseClientIdHeader(req, res);
1974
+ if (raw === null || raw === void 0) return raw;
1975
+ const bridges = Array.isArray(bridge) ? bridge : [bridge];
1976
+ if (!bridges.some((candidate) => candidate.knownClientIds().has(raw))) {
1977
+ res.status(400).json({
1978
+ error: `Client id "${raw}" is not registered for this workspace`,
1979
+ code: "invalid_client_id",
1980
+ clientId: raw
1981
+ });
1982
+ return null;
1983
+ }
1984
+ return raw;
1985
+ }
1986
+ __name(parseAndValidateWorkspaceClientId, "parseAndValidateWorkspaceClientId");
1987
+ function createBuildWorkspaceCtx(boundWorkspace) {
1988
+ return (route, clientId) => ({
1989
+ originatorClientId: clientId,
1990
+ route,
1991
+ workspaceCwd: boundWorkspace
1992
+ });
1993
+ }
1994
+ __name(createBuildWorkspaceCtx, "createBuildWorkspaceCtx");
1995
+ function parsePermissionVoteBody(req, res) {
1996
+ const body = safeBody(req);
1997
+ const outcome = body["outcome"];
1998
+ if (!isValidOutcome(outcome)) {
1999
+ res.status(400).json({ error: INVALID_PERMISSION_OUTCOME_ERROR });
2000
+ return void 0;
2001
+ }
2002
+ return {
2003
+ ...body,
2004
+ outcome
2005
+ };
2006
+ }
2007
+ __name(parsePermissionVoteBody, "parsePermissionVoteBody");
2008
+ function isValidOutcome(raw) {
2009
+ if (typeof raw !== "object" || raw === null) return false;
2010
+ const obj = raw;
2011
+ if (obj["outcome"] === "cancelled") return true;
2012
+ return obj["outcome"] === "selected" && typeof obj["optionId"] === "string" && obj["optionId"].length > 0;
2013
+ }
2014
+ __name(isValidOutcome, "isValidOutcome");
2015
+ var MIN_QUERY_MAX_QUEUED = 16;
2016
+ var MAX_QUERY_MAX_QUEUED = 2048;
2017
+ function parseMaxQueuedQuery(raw, res) {
2018
+ if (raw === void 0) return void 0;
2019
+ if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
2020
+ writeStderrLine(
2021
+ `qwen serve: rejected ?maxQueued ${safeLogValue(raw)} (not a decimal integer)`
2022
+ );
2023
+ res.status(400).json({
2024
+ error: "`maxQueued` must be a decimal integer",
2025
+ code: "invalid_max_queued"
2026
+ });
2027
+ return null;
2028
+ }
2029
+ const n = Number.parseInt(raw, 10);
2030
+ if (!Number.isFinite(n) || n < MIN_QUERY_MAX_QUEUED || n > MAX_QUERY_MAX_QUEUED) {
2031
+ writeStderrLine(
2032
+ `qwen serve: rejected ?maxQueued ${safeLogValue(raw)} (outside [${MIN_QUERY_MAX_QUEUED}, ${MAX_QUERY_MAX_QUEUED}])`
2033
+ );
2034
+ res.status(400).json({
2035
+ error: `\`maxQueued\` must be in [${MIN_QUERY_MAX_QUEUED}, ${MAX_QUERY_MAX_QUEUED}]`,
2036
+ code: "invalid_max_queued"
2037
+ });
2038
+ return null;
2039
+ }
2040
+ return n;
2041
+ }
2042
+ __name(parseMaxQueuedQuery, "parseMaxQueuedQuery");
2043
+ function safeLogValue(raw) {
2044
+ return JSON.stringify(String(raw)).slice(0, 82);
2045
+ }
2046
+ __name(safeLogValue, "safeLogValue");
2047
+ function parseLastEventId(raw) {
2048
+ if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
2049
+ if (typeof raw === "string" && raw.length > 0) {
2050
+ writeStderrLine(
2051
+ `qwen serve: rejected Last-Event-ID ${safeLogValue(raw)} (not a decimal integer)`
2052
+ );
2053
+ }
2054
+ return void 0;
2055
+ }
2056
+ const n = Number.parseInt(raw, 10);
2057
+ if (!Number.isFinite(n) || n > Number.MAX_SAFE_INTEGER) {
2058
+ writeStderrLine(
2059
+ `qwen serve: rejected Last-Event-ID ${safeLogValue(raw)} (exceeds Number.MAX_SAFE_INTEGER)`
2060
+ );
2061
+ return void 0;
2062
+ }
2063
+ return n;
2064
+ }
2065
+ __name(parseLastEventId, "parseLastEventId");
2066
+
2067
+ // packages/cli/src/serve/rate-limit.ts
2068
+ init_esbuild_shims();
2069
+ var MAX_BUCKETS = 1e4;
2070
+ var GC_REQUEST_INTERVAL = 1e3;
2071
+ var GC_TIMER_INTERVAL_MS = 5 * 60 * 1e3;
2072
+ var WORKSPACE_QUALIFIED_ACP_PATH = /^\/workspaces\/[^/]+\/acp$/i;
2073
+ function resolveTier(method, path, workspaceQualifiedAcpEnabled) {
2074
+ const p = path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path;
2075
+ if (method === "OPTIONS") return null;
2076
+ if ((method === "GET" || method === "HEAD") && (p === "/health" || p === "/demo"))
2077
+ return null;
2078
+ if (method === "POST" && p.startsWith("/session/") && p.endsWith("/heartbeat"))
2079
+ return null;
2080
+ if (method === "GET" && p.startsWith("/session/") && p.endsWith("/events"))
2081
+ return null;
2082
+ if (p === "/acp" || p.startsWith("/acp/")) return null;
2083
+ if (workspaceQualifiedAcpEnabled && WORKSPACE_QUALIFIED_ACP_PATH.test(p))
2084
+ return null;
2085
+ if (method === "POST" && p.startsWith("/session/") && p.endsWith("/prompt"))
2086
+ return "prompt";
2087
+ if (method !== "GET" && method !== "HEAD") return "mutation";
2088
+ return "read";
2089
+ }
2090
+ __name(resolveTier, "resolveTier");
2091
+ function normalizeIp(raw) {
2092
+ if (raw.startsWith("::ffff:")) {
2093
+ return raw.slice(7);
2094
+ }
2095
+ return raw;
2096
+ }
2097
+ __name(normalizeIp, "normalizeIp");
2098
+ function createKeyExtractor(hostname) {
2099
+ const loopback = isLoopbackBind(hostname);
2100
+ return (req) => {
2101
+ const raw = req.get("x-qwen-client-id");
2102
+ const clientId = raw && raw.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(raw) ? raw : void 0;
2103
+ if (loopback) {
2104
+ return clientId ? `cid:${clientId}` : "anonymous";
2105
+ }
2106
+ const ip = normalizeIp(req.socket?.remoteAddress ?? "unknown");
2107
+ return clientId ? `${ip}:${clientId}` : ip;
2108
+ };
2109
+ }
2110
+ __name(createKeyExtractor, "createKeyExtractor");
2111
+ var LOG_SAMPLE_INTERVAL = 100;
2112
+ function createSampledLogger(onLog) {
2113
+ const state = /* @__PURE__ */ new Map();
2114
+ return {
2115
+ log(tier, key) {
2116
+ const logKey = `${tier}:${key}`;
2117
+ let entry = state.get(logKey);
2118
+ if (!entry) {
2119
+ entry = { count: 0, suppressed: 0 };
2120
+ state.set(logKey, entry);
2121
+ }
2122
+ entry.count++;
2123
+ if (entry.count === 1 || entry.count % LOG_SAMPLE_INTERVAL === 0) {
2124
+ onLog(tier, key, entry.suppressed);
2125
+ entry.suppressed = 0;
2126
+ } else {
2127
+ entry.suppressed++;
2128
+ }
2129
+ },
2130
+ clear() {
2131
+ state.clear();
2132
+ }
2133
+ };
2134
+ }
2135
+ __name(createSampledLogger, "createSampledLogger");
2136
+ function createRateLimiter(config) {
2137
+ for (const [tier, cfg] of Object.entries(config.tiers)) {
2138
+ if (!Number.isFinite(cfg.max) || cfg.max <= 0) {
2139
+ throw new Error(
2140
+ `rate limit: ${tier}.max must be a positive number, got ${cfg.max}`
2141
+ );
2142
+ }
2143
+ if (!Number.isFinite(cfg.windowMs) || cfg.windowMs <= 0) {
2144
+ throw new Error(
2145
+ `rate limit: ${tier}.windowMs must be a positive number, got ${cfg.windowMs}`
2146
+ );
2147
+ }
2148
+ }
2149
+ const buckets = /* @__PURE__ */ new Map();
2150
+ const keyExtractor = createKeyExtractor(config.hostname);
2151
+ const hitCounts = {
2152
+ prompt: 0,
2153
+ mutation: 0,
2154
+ read: 0
2155
+ };
2156
+ const rates = {
2157
+ prompt: config.tiers.prompt.max / config.tiers.prompt.windowMs,
2158
+ mutation: config.tiers.mutation.max / config.tiers.mutation.windowMs,
2159
+ read: config.tiers.read.max / config.tiers.read.windowMs
2160
+ };
2161
+ let draining = false;
2162
+ let requestCount = 0;
2163
+ const sampledLog = config.onLimitReached ? createSampledLogger(config.onLimitReached) : void 0;
2164
+ function sweep() {
2165
+ const now = Date.now();
2166
+ for (const [key, tierMap] of buckets) {
2167
+ let allStale = true;
2168
+ for (const [tier, bucket] of tierMap) {
2169
+ if (now - bucket.lastRefill < config.tiers[tier].windowMs * 2) {
2170
+ allStale = false;
2171
+ break;
2172
+ }
2173
+ }
2174
+ if (allStale) {
2175
+ buckets.delete(key);
2176
+ }
2177
+ }
2178
+ }
2179
+ __name(sweep, "sweep");
2180
+ const gcTimer = setInterval(sweep, GC_TIMER_INTERVAL_MS);
2181
+ gcTimer.unref();
2182
+ const middleware = /* @__PURE__ */ __name((req, res, next) => {
2183
+ try {
2184
+ if (draining) {
2185
+ next();
2186
+ return;
2187
+ }
2188
+ const tier = resolveTier(
2189
+ req.method,
2190
+ req.path,
2191
+ config.workspaceQualifiedAcpEnabled === true
2192
+ );
2193
+ if (tier === null) {
2194
+ next();
2195
+ return;
2196
+ }
2197
+ requestCount++;
2198
+ if (requestCount % GC_REQUEST_INTERVAL === 0) {
2199
+ sweep();
2200
+ }
2201
+ const key = keyExtractor(req);
2202
+ if (tryConsume(key, tier)) {
2203
+ next();
2204
+ } else {
2205
+ const tierConfig = config.tiers[tier];
2206
+ const rate = rates[tier];
2207
+ const bucket = buckets.get(key)?.get(tier);
2208
+ const retryAfterMs = Math.ceil((1 - (bucket?.tokens ?? 0)) / rate);
2209
+ const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
2210
+ res.setHeader("Retry-After", String(retryAfterSec));
2211
+ res.setHeader("X-RateLimit-Limit", String(tierConfig.max));
2212
+ res.setHeader("X-RateLimit-Remaining", "0");
2213
+ res.setHeader(
2214
+ "X-RateLimit-Reset",
2215
+ String(Math.ceil((Date.now() + retryAfterMs) / 1e3))
2216
+ );
2217
+ res.status(429).json({
2218
+ error: "Rate limit exceeded",
2219
+ code: "rate_limit_exceeded",
2220
+ tier,
2221
+ retryAfterMs
2222
+ });
2223
+ }
2224
+ } catch (err) {
2225
+ config.onError?.(err, req.path);
2226
+ next();
2227
+ }
2228
+ }, "middleware");
2229
+ function tryConsume(key, tier) {
2230
+ if (draining) return true;
2231
+ const tierConfig = config.tiers[tier];
2232
+ const rate = rates[tier];
2233
+ const now = Date.now();
2234
+ let tierMap = buckets.get(key);
2235
+ if (!tierMap) {
2236
+ if (buckets.size >= MAX_BUCKETS) {
2237
+ config.onError?.(
2238
+ new Error(`rate limit bucket overflow: ${buckets.size} keys`),
2239
+ `tryConsume:${tier}`
2240
+ );
2241
+ return true;
2242
+ }
2243
+ tierMap = /* @__PURE__ */ new Map();
2244
+ buckets.set(key, tierMap);
2245
+ }
2246
+ let bucket = tierMap.get(tier);
2247
+ if (!bucket) {
2248
+ bucket = { tokens: tierConfig.max, lastRefill: now };
2249
+ tierMap.set(tier, bucket);
2250
+ }
2251
+ const elapsed = Math.max(0, now - bucket.lastRefill);
2252
+ bucket.tokens = Math.min(tierConfig.max, bucket.tokens + elapsed * rate);
2253
+ bucket.lastRefill = now;
2254
+ if (bucket.tokens >= 1) {
2255
+ bucket.tokens -= 1;
2256
+ return true;
2257
+ }
2258
+ hitCounts[tier]++;
2259
+ if (sampledLog) sampledLog.log(tier, key);
2260
+ return false;
2261
+ }
2262
+ __name(tryConsume, "tryConsume");
2263
+ return {
2264
+ middleware,
2265
+ checkRate: tryConsume,
2266
+ reset() {
2267
+ buckets.clear();
2268
+ hitCounts.prompt = 0;
2269
+ hitCounts.mutation = 0;
2270
+ hitCounts.read = 0;
2271
+ requestCount = 0;
2272
+ sampledLog?.clear();
2273
+ },
2274
+ setDraining(v) {
2275
+ draining = v;
2276
+ },
2277
+ dispose() {
2278
+ clearInterval(gcTimer);
2279
+ buckets.clear();
2280
+ sampledLog?.clear();
2281
+ },
2282
+ getHitCounts() {
2283
+ return { ...hitCounts };
2284
+ }
2285
+ };
2286
+ }
2287
+ __name(createRateLimiter, "createRateLimiter");
2288
+ var RATE_LIMITER_KEY = "_rateLimiter";
2289
+ function setRateLimiter(app, limiter) {
2290
+ app.locals[RATE_LIMITER_KEY] = limiter;
2291
+ }
2292
+ __name(setRateLimiter, "setRateLimiter");
2293
+ function getRateLimiter(app) {
2294
+ return app.locals[RATE_LIMITER_KEY];
2295
+ }
2296
+ __name(getRateLimiter, "getRateLimiter");
2297
+
2298
+ // packages/cli/src/serve/acp-http-enabled.ts
2299
+ init_esbuild_shims();
2300
+ function resolveAcpHttpEnabled(env = process.env) {
2301
+ return env["QWEN_SERVE_ACP_HTTP"] !== "0";
2302
+ }
2303
+ __name(resolveAcpHttpEnabled, "resolveAcpHttpEnabled");
2304
+
2305
+ // packages/cli/src/serve/daemon-status.ts
2306
+ init_esbuild_shims();
2307
+ var DEFAULT_LISTENER_MAX_CONNECTIONS = 256;
2308
+ var SECTION_TIMEOUT_MS = 1e3;
2309
+ var CAPACITY_WARNING_RATIO = 0.8;
2310
+ function toDaemonStatusMemoryLimits(budget) {
2311
+ if (!budget) return null;
2312
+ return {
2313
+ enforced: false,
2314
+ configuredBudgetMb: budget.configuredBudgetMb,
2315
+ effectiveBudgetMb: budget.effectiveBudgetMb,
2316
+ budgetSource: budget.budgetSource,
2317
+ availableMemoryMb: budget.availableMemoryMb,
2318
+ availableMemorySource: budget.availableMemorySource,
2319
+ insufficientMemory: budget.insufficientMemory,
2320
+ modeled: {
2321
+ rootReserveMb: budget.rootReserveMb,
2322
+ childPoolMb: budget.childPoolMb,
2323
+ minChildHeapMb: MIN_CHILD_HEAP_MB,
2324
+ maxChildHeapMb: MAX_CHILD_HEAP_MB,
2325
+ legacyChildCeilingMb: budget.legacyChildCeilingMb
2326
+ }
2327
+ };
2328
+ }
2329
+ __name(toDaemonStatusMemoryLimits, "toDaemonStatusMemoryLimits");
2330
+ var SectionTimeoutError = class extends Error {
2331
+ constructor(section, timeoutMs) {
2332
+ super(`${section} status timed out after ${timeoutMs}ms`);
2333
+ this.section = section;
2334
+ this.timeoutMs = timeoutMs;
2335
+ this.name = "SectionTimeoutError";
2336
+ }
2337
+ static {
2338
+ __name(this, "SectionTimeoutError");
2339
+ }
2340
+ };
2341
+ function parseDaemonStatusDetail(raw) {
2342
+ if (raw === void 0) return { ok: true, detail: "summary" };
2343
+ if (raw === "summary" || raw === "full") {
2344
+ return { ok: true, detail: raw };
2345
+ }
2346
+ return { ok: false };
2347
+ }
2348
+ __name(parseDaemonStatusDetail, "parseDaemonStatusDetail");
2349
+ async function buildDaemonStatusResponse(detail, input) {
2350
+ const daemonLogStatus = input.daemonLog?.getStatus();
2351
+ const bridgeSnapshot = input.bridge.getDaemonStatusSnapshot();
2352
+ const lastActivity = input.bridge.lastActivityAt ?? null;
2353
+ const workspaceRuntimes = input.workspaceRegistry?.list();
2354
+ const workspaceSnapshots = workspaceRuntimes?.map((runtime) => ({
2355
+ workspaceCwd: runtime.workspaceCwd,
2356
+ snapshot: runtime.bridge === input.bridge ? bridgeSnapshot : runtime.bridge.getDaemonStatusSnapshot(),
2357
+ lastActivity: runtime.bridge === input.bridge ? lastActivity : runtime.bridge.lastActivityAt ?? null
2358
+ })) ?? [
2359
+ {
2360
+ workspaceCwd: input.boundWorkspace,
2361
+ snapshot: bridgeSnapshot,
2362
+ lastActivity
2363
+ }
2364
+ ];
2365
+ const aggregatedSessionCount = workspaceSnapshots.reduce(
2366
+ (sum, item) => sum + item.snapshot.sessionCount,
2367
+ 0
2368
+ );
2369
+ const aggregatedPendingPermissionCount = workspaceSnapshots.reduce(
2370
+ (sum, item) => sum + item.snapshot.pendingPermissionCount,
2371
+ 0
2372
+ );
2373
+ const aggregatedChannelLive = workspaceSnapshots.some(
2374
+ (item) => item.snapshot.channelLive
2375
+ );
2376
+ const memoryBudget = input.opts.daemonMemoryBudget;
2377
+ let runtimeMemory;
2378
+ if (memoryBudget) {
2379
+ const managedRuntimes = input.workspaceRegistry?.listManaged();
2380
+ const activeAcpChildCount = managedRuntimes ? managedRuntimes.filter((runtime) => runtime.bridge.isChannelLive()).length : workspaceSnapshots.filter((item) => item.snapshot.channelLive).length;
2381
+ const registeredWorkspaceCount = input.workspaceRegistry ? input.workspaceRegistry.listEntries().length : workspaceSnapshots.length;
2382
+ runtimeMemory = {
2383
+ registeredWorkspaces: registeredWorkspaceCount,
2384
+ activeAcpChildren: activeAcpChildCount,
2385
+ childRssCoverage: "primary_only",
2386
+ modeled: {
2387
+ recommendedShareAtRegisteredMb: registeredWorkspaceCount > 0 ? recommendedChildShareMb(memoryBudget, registeredWorkspaceCount) : null,
2388
+ recommendedShareAtActiveMb: activeAcpChildCount > 0 ? recommendedChildShareMb(memoryBudget, activeAcpChildCount) : null
2389
+ }
2390
+ };
2391
+ }
2392
+ const aggregatedLastActivity = workspaceSnapshots.reduce(
2393
+ (latest, item) => item.lastActivity !== null && (latest === null || item.lastActivity > latest) ? item.lastActivity : latest,
2394
+ null
2395
+ );
2396
+ const acpSnapshot = input.acpHandle?.registry.getSnapshot();
2397
+ const acpAggregate = input.acpHandle?.getSnapshot();
2398
+ const rateLimitHits = input.rateLimiter?.getHitCounts() ?? zeroRateHits();
2399
+ let pendingPrompts = 0;
2400
+ let derivedQueuedPrompts = 0;
2401
+ const derivedQueuedPromptsByWorkspace = [];
2402
+ for (const [index, { snapshot }] of workspaceSnapshots.entries()) {
2403
+ let derivedQueuedPromptsForWorkspace = 0;
2404
+ for (const session of snapshot.sessions) {
2405
+ pendingPrompts += session.pendingPromptCount;
2406
+ const sessionQueuedPrompts = Math.max(
2407
+ 0,
2408
+ session.pendingPromptCount - (session.hasActivePrompt ? 1 : 0)
2409
+ );
2410
+ derivedQueuedPrompts += sessionQueuedPrompts;
2411
+ derivedQueuedPromptsForWorkspace += sessionQueuedPrompts;
2412
+ }
2413
+ derivedQueuedPromptsByWorkspace[index] = derivedQueuedPromptsForWorkspace;
2414
+ }
2415
+ const queuedPrompts = workspaceRuntimes?.reduce(
2416
+ (sum, runtime, index) => sum + (runtime.bridge.pendingPromptTotal ?? derivedQueuedPromptsByWorkspace[index] ?? 0),
2417
+ 0
2418
+ ) ?? input.bridge.pendingPromptTotal ?? derivedQueuedPrompts;
2419
+ const channelWorker = input.getChannelWorkerSnapshot?.() ?? {
2420
+ enabled: false,
2421
+ state: "disabled",
2422
+ channels: []
2423
+ };
2424
+ const channelWorkers = (workspaceRuntimes?.length ?? 1) > 1 ? input.getChannelWorkerSnapshots?.() : void 0;
2425
+ const totalAdmissionSnapshot = input.getTotalSessionAdmissionSnapshot?.();
2426
+ const issues = [];
2427
+ let full;
2428
+ pushRuntimeIssues(
2429
+ issues,
2430
+ acpSnapshot,
2431
+ acpAggregate,
2432
+ rateLimitHits,
2433
+ input,
2434
+ channelWorker,
2435
+ channelWorkers,
2436
+ totalAdmissionSnapshot,
2437
+ workspaceSnapshots
2438
+ );
2439
+ if (daemonLogStatus?.health === "degraded") {
2440
+ issues.push({
2441
+ code: "daemon_log_degraded",
2442
+ severity: "warning",
2443
+ message: "Daemon file logging is degraded; inspect full status for details."
2444
+ });
2445
+ }
2446
+ if (detail === "full") {
2447
+ full = await buildFullStatus(
2448
+ input,
2449
+ acpAggregate,
2450
+ workspaceSnapshots.flatMap((item) => item.snapshot.sessions)
2451
+ );
2452
+ pushFullIssues(issues, full);
2453
+ }
2454
+ return {
2455
+ v: 1,
2456
+ detail,
2457
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2458
+ status: rollupStatus(issues),
2459
+ issues,
2460
+ daemon: {
2461
+ pid: process.pid,
2462
+ uptimeMs: Math.round(process.uptime() * 1e3),
2463
+ mode: input.opts.mode,
2464
+ workspaceCwd: input.boundWorkspace,
2465
+ ...input.startup ? { startup: cloneStartup(input.startup) } : {},
2466
+ ...input.qwenCodeVersion ? { qwenCodeVersion: input.qwenCodeVersion } : {},
2467
+ ...input.daemonLog?.getDaemonId() ? { daemonId: input.daemonLog.getDaemonId() } : {},
2468
+ ...daemonLogStatus ? {
2469
+ runId: daemonLogStatus.runId,
2470
+ logMode: daemonLogStatus.mode,
2471
+ logHealth: daemonLogStatus.health
2472
+ } : {},
2473
+ ...detail === "full" && input.daemonLog?.getLogPath() ? { logPath: input.daemonLog.getLogPath() } : {},
2474
+ ...detail === "full" && daemonLogStatus ? {
2475
+ logIssues: daemonLogStatus.issues,
2476
+ logDroppedRecords: daemonLogStatus.droppedRecords,
2477
+ logDroppedBytes: daemonLogStatus.droppedBytes
2478
+ } : {}
2479
+ },
2480
+ security: {
2481
+ tokenConfigured: Boolean(input.opts.token),
2482
+ requireAuth: input.opts.requireAuth === true,
2483
+ loopbackBind: isLoopbackBind(input.opts.hostname),
2484
+ allowOriginConfigured: input.opts.allowOrigins !== void 0 && input.opts.allowOrigins.length > 0,
2485
+ allowOriginMode: allowOriginMode(input.opts.allowOrigins),
2486
+ sessionShellCommandEnabled: input.sessionShellCommandEnabled
2487
+ },
2488
+ limits: {
2489
+ maxSessions: bridgeSnapshot.limits.maxSessions,
2490
+ maxTotalSessions: positiveFiniteOrNull(input.opts.maxTotalSessions),
2491
+ maxPendingPromptsPerSession: bridgeSnapshot.limits.maxPendingPromptsPerSession,
2492
+ listenerMaxConnections: listenerMaxConnections(input.opts.maxConnections),
2493
+ eventRingSize: bridgeSnapshot.limits.eventRingSize,
2494
+ compactedReplayMaxBytes: bridgeSnapshot.limits.compactedReplayMaxBytes,
2495
+ maxJournalEvents: bridgeSnapshot.limits.maxJournalEvents,
2496
+ maxJournalBytes: bridgeSnapshot.limits.maxJournalBytes,
2497
+ promptDeadlineMs: positiveFiniteOrNull(input.opts.promptDeadlineMs),
2498
+ writerIdleTimeoutMs: positiveFiniteOrNull(input.opts.writerIdleTimeoutMs),
2499
+ channelIdleTimeoutMs: bridgeSnapshot.limits.channelIdleTimeoutMs,
2500
+ sessionIdleTimeoutMs: bridgeSnapshot.limits.sessionIdleTimeoutMs,
2501
+ acpConnectionCap: acpSnapshot?.connectionCap ?? null,
2502
+ memory: toDaemonStatusMemoryLimits(memoryBudget)
2503
+ },
2504
+ ...workspaceRuntimes && workspaceRuntimes.length > 1 ? {
2505
+ workspaces: workspaceRuntimes.map((runtime) => ({
2506
+ id: runtime.workspaceId,
2507
+ cwd: runtime.workspaceCwd,
2508
+ ...runtime.displayName !== void 0 ? { displayName: runtime.displayName } : {},
2509
+ primary: runtime.primary,
2510
+ trusted: runtime.trusted
2511
+ }))
2512
+ } : {},
2513
+ capabilities: {
2514
+ protocolVersions: input.protocolVersions,
2515
+ features: [...input.features]
2516
+ },
2517
+ runtime: {
2518
+ sessions: {
2519
+ active: aggregatedSessionCount,
2520
+ ...totalAdmissionSnapshot ? { admissionInFlight: totalAdmissionSnapshot.inFlight } : {}
2521
+ },
2522
+ permissions: {
2523
+ pending: aggregatedPendingPermissionCount,
2524
+ policy: bridgeSnapshot.permissionPolicy
2525
+ },
2526
+ channel: { live: aggregatedChannelLive },
2527
+ channelWorker,
2528
+ ...channelWorkers && channelWorkers.length > 0 ? { channelWorkers } : {},
2529
+ transport: {
2530
+ restSseActive: input.getRestSseActive(),
2531
+ acp: {
2532
+ enabled: acpSnapshot !== void 0,
2533
+ connections: acpAggregate?.connectionCount ?? 0,
2534
+ connectionStreams: acpAggregate?.connectionStreams ?? 0,
2535
+ sessionStreams: acpAggregate?.sessionStreams ?? 0,
2536
+ sseStreams: acpAggregate?.sseStreams ?? 0,
2537
+ wsStreams: acpAggregate?.wsStreams ?? 0,
2538
+ pendingClientRequests: acpAggregate?.pendingClientRequests ?? 0
2539
+ }
2540
+ },
2541
+ rateLimit: {
2542
+ enabled: input.opts.rateLimit === true,
2543
+ rejectedSinceStart: rateLimitHits
2544
+ },
2545
+ ...input.getPerfSnapshot ? { perf: input.getPerfSnapshot() } : {},
2546
+ ...input.getMetricsSeries ? { metrics: { series: input.getMetricsSeries() } } : {},
2547
+ activity: {
2548
+ activePrompts: workspaceRuntimes?.reduce(
2549
+ (sum, runtime) => sum + (runtime.bridge.activePromptCount ?? 0),
2550
+ 0
2551
+ ) ?? input.bridge.activePromptCount ?? 0,
2552
+ pendingPrompts,
2553
+ queuedPrompts,
2554
+ lastActivityAt: aggregatedLastActivity !== null ? new Date(aggregatedLastActivity).toISOString() : null,
2555
+ idleSinceMs: aggregatedLastActivity !== null ? Date.now() - aggregatedLastActivity : null
2556
+ },
2557
+ ...runtimeMemory ? { memory: runtimeMemory } : {},
2558
+ process: process.memoryUsage()
2559
+ },
2560
+ ...full ? { full } : {}
2561
+ };
2562
+ }
2563
+ __name(buildDaemonStatusResponse, "buildDaemonStatusResponse");
2564
+ function cloneStartup(startup) {
2565
+ return {
2566
+ processStartedAt: startup.processStartedAt,
2567
+ ...startup.listenerReadyAt ? { listenerReadyAt: startup.listenerReadyAt } : {},
2568
+ ...startup.processToListenMs !== void 0 ? { processToListenMs: startup.processToListenMs } : {},
2569
+ ...startup.runQwenServeToListenMs !== void 0 ? { runQwenServeToListenMs: startup.runQwenServeToListenMs } : {},
2570
+ preheat: {
2571
+ status: startup.preheat.status,
2572
+ ...startup.preheat.durationMs !== void 0 ? { durationMs: startup.preheat.durationMs } : {},
2573
+ ...startup.preheat.error ? { error: startup.preheat.error } : {}
2574
+ }
2575
+ };
2576
+ }
2577
+ __name(cloneStartup, "cloneStartup");
2578
+ async function buildFullStatus(input, acpSnapshot, sessions) {
2579
+ const ctx = {
2580
+ route: "GET /daemon/status",
2581
+ workspaceCwd: input.boundWorkspace
2582
+ };
2583
+ const [mcp, skills, tools, providers, env, preflight, hooks, extensions] = await Promise.all([
2584
+ collectSection(
2585
+ "workspace.mcp",
2586
+ () => input.workspace.getWorkspaceMcpStatus(ctx)
2587
+ ),
2588
+ collectSection(
2589
+ "workspace.skills",
2590
+ () => input.workspace.getWorkspaceSkillsStatus(ctx)
2591
+ ),
2592
+ collectSection(
2593
+ "workspace.tools",
2594
+ () => input.bridge.getWorkspaceToolsStatus()
2595
+ ),
2596
+ collectSection(
2597
+ "workspace.providers",
2598
+ () => input.workspace.getWorkspaceProvidersStatus(ctx)
2599
+ ),
2600
+ collectSection(
2601
+ "workspace.env",
2602
+ () => input.workspace.getWorkspaceEnvStatus(ctx)
2603
+ ),
2604
+ collectSection(
2605
+ "workspace.preflight",
2606
+ () => input.workspace.getWorkspacePreflightStatus(ctx)
2607
+ ),
2608
+ collectSection(
2609
+ "workspace.hooks",
2610
+ () => input.workspace.getWorkspaceHooksStatus(ctx)
2611
+ ),
2612
+ collectSection(
2613
+ "workspace.extensions",
2614
+ () => input.workspace.getWorkspaceExtensionsStatus(ctx)
2615
+ )
2616
+ ]);
2617
+ return {
2618
+ sessions,
2619
+ acpConnections: acpSnapshot?.connections ?? [],
2620
+ workspace: {
2621
+ mcp,
2622
+ skills,
2623
+ tools,
2624
+ providers,
2625
+ env,
2626
+ preflight,
2627
+ hooks,
2628
+ extensions
2629
+ },
2630
+ auth: {
2631
+ supportedDeviceFlowProviders: [...input.supportedDeviceFlowProviders],
2632
+ pendingDeviceFlowCount: input.deviceFlowRegistry.listPending().length
2633
+ }
2634
+ };
2635
+ }
2636
+ __name(buildFullStatus, "buildFullStatus");
2637
+ async function collectSection(name, read) {
2638
+ const startMs = Date.now();
2639
+ try {
2640
+ const data = await withTimeout(read(), name, SECTION_TIMEOUT_MS);
2641
+ return {
2642
+ status: inferSectionStatus(data),
2643
+ durationMs: Date.now() - startMs,
2644
+ summary: summarizeStatusData(data),
2645
+ data
2646
+ };
2647
+ } catch (err) {
2648
+ return {
2649
+ status: "unavailable",
2650
+ durationMs: Date.now() - startMs,
2651
+ error: {
2652
+ kind: err instanceof SectionTimeoutError ? "timeout" : "error",
2653
+ message: err instanceof Error ? err.message : String(err)
2654
+ }
2655
+ };
2656
+ }
2657
+ }
2658
+ __name(collectSection, "collectSection");
2659
+ async function withTimeout(promise, section, timeoutMs) {
2660
+ let timer;
2661
+ try {
2662
+ return await Promise.race([
2663
+ promise,
2664
+ new Promise((_resolve, reject) => {
2665
+ timer = setTimeout(
2666
+ () => reject(new SectionTimeoutError(section, timeoutMs)),
2667
+ timeoutMs
2668
+ );
2669
+ })
2670
+ ]);
2671
+ } finally {
2672
+ if (timer) clearTimeout(timer);
2673
+ }
2674
+ }
2675
+ __name(withTimeout, "withTimeout");
2676
+ function pushRuntimeIssues(issues, acpSnapshot, acpAggregate, rateLimitHits, input, channelWorker, channelWorkers, totalAdmissionSnapshot, workspaceSnapshots) {
2677
+ for (const { workspaceCwd, snapshot } of workspaceSnapshots) {
2678
+ if (snapshot.limits.maxSessions !== null && snapshot.limits.maxSessions > 0 && snapshot.sessionCount / snapshot.limits.maxSessions >= CAPACITY_WARNING_RATIO) {
2679
+ issues.push({
2680
+ code: "session_capacity_high",
2681
+ severity: "warning",
2682
+ message: workspaceSnapshots.length > 1 ? `Workspace ${workspaceCwd} active sessions are at ${snapshot.sessionCount}/${snapshot.limits.maxSessions}.` : `Active sessions are at ${snapshot.sessionCount}/${snapshot.limits.maxSessions}.`
2683
+ });
2684
+ }
2685
+ }
2686
+ const maxTotalSessions = positiveFiniteOrNull(input.opts.maxTotalSessions);
2687
+ if (maxTotalSessions !== null) {
2688
+ const fallbackLiveCount = workspaceSnapshots.reduce(
2689
+ (sum, item) => sum + item.snapshot.sessionCount,
2690
+ 0
2691
+ );
2692
+ const totalActive = (totalAdmissionSnapshot?.liveCount ?? fallbackLiveCount) + (totalAdmissionSnapshot?.inFlight ?? 0);
2693
+ if (totalActive / maxTotalSessions >= CAPACITY_WARNING_RATIO) {
2694
+ issues.push({
2695
+ code: "total_session_capacity_high",
2696
+ severity: "warning",
2697
+ message: `Total active and in-flight sessions are at ${totalActive}/${maxTotalSessions}.`
2698
+ });
2699
+ }
2700
+ }
2701
+ if (acpSnapshot !== void 0 && acpSnapshot.connectionCap !== null && acpSnapshot.connectionCap > 0) {
2702
+ const cap = acpSnapshot.connectionCap;
2703
+ const busiest = (acpAggregate?.mounts ?? []).reduce(
2704
+ (max, m) => Math.max(max, m.connectionCount),
2705
+ acpSnapshot.connectionCount
2706
+ );
2707
+ if (busiest / cap >= CAPACITY_WARNING_RATIO) {
2708
+ issues.push({
2709
+ code: "connection_capacity_high",
2710
+ severity: "warning",
2711
+ message: `ACP connections are at ${busiest}/${cap} on the busiest workspace mount.`
2712
+ });
2713
+ }
2714
+ }
2715
+ const pendingPermissionCount = workspaceSnapshots.reduce(
2716
+ (sum, item) => sum + item.snapshot.pendingPermissionCount,
2717
+ 0
2718
+ );
2719
+ if (pendingPermissionCount > 0) {
2720
+ issues.push({
2721
+ code: "pending_permissions",
2722
+ severity: "warning",
2723
+ message: `${pendingPermissionCount} permission request(s) are pending.`
2724
+ });
2725
+ }
2726
+ const downWorkspaces = workspaceSnapshots.filter(
2727
+ (item) => item.snapshot.sessionCount > 0 && !item.snapshot.channelLive
2728
+ );
2729
+ if (downWorkspaces.length > 0) {
2730
+ issues.push({
2731
+ code: "acp_channel_down",
2732
+ severity: "error",
2733
+ 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).`
2734
+ });
2735
+ }
2736
+ if (input.opts.rateLimit === true && sumRateHits(rateLimitHits) > 0) {
2737
+ issues.push({
2738
+ code: "rate_limit_hits",
2739
+ severity: "warning",
2740
+ message: `${sumRateHits(rateLimitHits)} request(s) have been rejected by rate limiting since start.`
2741
+ });
2742
+ }
2743
+ const groupedWorkers = channelWorkers && channelWorkers.length > 0 ? channelWorkers : void 0;
2744
+ const workers = groupedWorkers ?? [channelWorker];
2745
+ for (const worker of workers) {
2746
+ pushChannelWorkerIssues(issues, worker, groupedWorkers !== void 0);
2747
+ }
2748
+ }
2749
+ __name(pushRuntimeIssues, "pushRuntimeIssues");
2750
+ function pushChannelWorkerIssues(issues, channelWorker, grouped) {
2751
+ const workspace = "workspaceCwd" in channelWorker ? ` for workspace ${channelWorker.workspaceCwd}` : "";
2752
+ const section = grouped ? "runtime.channelWorkers" : "runtime.channelWorker";
2753
+ if (channelWorker.enabled && (channelWorker.state === "exited" || channelWorker.state === "failed")) {
2754
+ const detailParts = [
2755
+ channelWorker.pid !== void 0 ? `pid=${channelWorker.pid}` : void 0,
2756
+ channelWorker.exitCode !== void 0 ? `code=${channelWorker.exitCode ?? "null"}` : void 0,
2757
+ channelWorker.signal ? `signal=${channelWorker.signal}` : void 0,
2758
+ channelWorker.restartCount !== void 0 ? `restarts=${channelWorker.restartCount}` : void 0,
2759
+ channelWorker.lastExitAt ? `lastExitAt=${channelWorker.lastExitAt}` : void 0,
2760
+ channelWorker.lastRestartAt ? `lastRestartAt=${channelWorker.lastRestartAt}` : void 0,
2761
+ channelWorker.nextRestartAt ? `nextRestartAt=${channelWorker.nextRestartAt}` : void 0,
2762
+ channelWorker.lastHeartbeatAt ? `lastHeartbeatAt=${channelWorker.lastHeartbeatAt}` : void 0,
2763
+ channelWorker.staleHeartbeatAt ? `staleHeartbeatAt=${channelWorker.staleHeartbeatAt}` : void 0
2764
+ ].filter(Boolean);
2765
+ const details = detailParts.length > 0 ? ` (${detailParts.join(", ")})` : "";
2766
+ const error = channelWorker.error ? `: ${channelWorker.error}` : "";
2767
+ const isPermanentFailure = channelWorker.state === "failed" && !channelWorker.nextRestartAt;
2768
+ issues.push({
2769
+ code: "channel_worker_exited",
2770
+ severity: isPermanentFailure ? "error" : "warning",
2771
+ message: `Channel worker${workspace} is ${channelWorker.state}${details}${error}.`,
2772
+ section
2773
+ });
2774
+ }
2775
+ if (channelWorker.enabled && channelWorker.state === "running" && channelWorker.requestedChannels !== void 0) {
2776
+ const connected = new Set(channelWorker.channels);
2777
+ const failed = channelWorker.requestedChannels.filter(
2778
+ (channel) => !connected.has(channel)
2779
+ );
2780
+ if (failed.length > 0) {
2781
+ issues.push({
2782
+ code: "channel_worker_partial_connect",
2783
+ severity: "warning",
2784
+ message: `Channel worker${workspace} connected ${channelWorker.channels.length}/${channelWorker.requestedChannels.length} channel(s). Failed: ${failed.join(", ")}.`,
2785
+ section
2786
+ });
2787
+ }
2788
+ }
2789
+ }
2790
+ __name(pushChannelWorkerIssues, "pushChannelWorkerIssues");
2791
+ function pushFullIssues(issues, full) {
2792
+ for (const [name, section] of Object.entries(full.workspace)) {
2793
+ if (section.status === "unavailable") {
2794
+ issues.push({
2795
+ code: "workspace_status_unavailable",
2796
+ severity: "warning",
2797
+ section: name,
2798
+ message: `${name} status is unavailable.`
2799
+ });
2800
+ }
2801
+ }
2802
+ const preflight = full.workspace["preflight"];
2803
+ if (preflight && sectionHasStatus(preflight, "error")) {
2804
+ issues.push({
2805
+ code: "preflight_error",
2806
+ severity: "error",
2807
+ section: "preflight",
2808
+ message: "Workspace preflight reports an error."
2809
+ });
2810
+ }
2811
+ const mcp = full.workspace["mcp"];
2812
+ const mcpBudget = mcp ? inspectMcpBudget(mcp) : void 0;
2813
+ if (mcpBudget === "exhausted") {
2814
+ issues.push({
2815
+ code: "mcp_budget_exhausted",
2816
+ severity: "error",
2817
+ section: "mcp",
2818
+ message: "MCP client budget is exhausted."
2819
+ });
2820
+ } else if (mcpBudget === "warning") {
2821
+ issues.push({
2822
+ code: "mcp_budget_warning",
2823
+ severity: "warning",
2824
+ section: "mcp",
2825
+ message: "MCP client budget is near capacity."
2826
+ });
2827
+ }
2828
+ }
2829
+ __name(pushFullIssues, "pushFullIssues");
2830
+ function inferSectionStatus(data) {
2831
+ const statuses = collectStatuses(data);
2832
+ if (statuses.includes("error")) return "error";
2833
+ if (statuses.includes("warning")) return "warning";
2834
+ return "ok";
2835
+ }
2836
+ __name(inferSectionStatus, "inferSectionStatus");
2837
+ function summarizeStatusData(data) {
2838
+ const summary = {};
2839
+ if (!isRecord(data)) return summary;
2840
+ copyBoolean(data, summary, "initialized");
2841
+ copyBoolean(data, summary, "acpChannelLive");
2842
+ copyString(data, summary, "discoveryState");
2843
+ copyString(data, summary, "budgetMode");
2844
+ copyNumber(data, summary, "clientCount");
2845
+ copyNumber(data, summary, "clientBudget");
2846
+ for (const key of [
2847
+ "cells",
2848
+ "errors",
2849
+ "servers",
2850
+ "budgets",
2851
+ "skills",
2852
+ "tools",
2853
+ "providers",
2854
+ "hooks",
2855
+ "extensions"
2856
+ ]) {
2857
+ const value = data[key];
2858
+ if (Array.isArray(value)) {
2859
+ summary[`${key}Count`] = value.length;
2860
+ }
2861
+ }
2862
+ summarizeMcpServers(data, summary);
2863
+ return summary;
2864
+ }
2865
+ __name(summarizeStatusData, "summarizeStatusData");
2866
+ function summarizeMcpServers(data, summary) {
2867
+ const servers = data["servers"];
2868
+ if (!Array.isArray(servers)) return;
2869
+ let connected = 0;
2870
+ let errored = 0;
2871
+ let disabled = 0;
2872
+ for (const server of servers) {
2873
+ if (!isRecord(server)) continue;
2874
+ if (server["disabled"] === true) {
2875
+ disabled++;
2876
+ } else if (server["status"] === "error") {
2877
+ errored++;
2878
+ } else if (server["mcpStatus"] === "connected") {
2879
+ connected++;
2880
+ }
2881
+ }
2882
+ summary["serversConnected"] = connected;
2883
+ summary["serversErrored"] = errored;
2884
+ summary["serversDisabled"] = disabled;
2885
+ }
2886
+ __name(summarizeMcpServers, "summarizeMcpServers");
2887
+ function collectStatuses(data) {
2888
+ const statuses = [];
2889
+ visitStatusContainers(data, (record) => {
2890
+ const status = record["status"];
2891
+ if (typeof status === "string") statuses.push(status);
2892
+ });
2893
+ return statuses;
2894
+ }
2895
+ __name(collectStatuses, "collectStatuses");
2896
+ function sectionHasStatus(section, status) {
2897
+ return collectStatuses(section.data).includes(status);
2898
+ }
2899
+ __name(sectionHasStatus, "sectionHasStatus");
2900
+ function inspectMcpBudget(section) {
2901
+ const data = section.data;
2902
+ if (!isRecord(data)) return void 0;
2903
+ const budgetIssue = inspectBudgetContainers(data);
2904
+ if (budgetIssue) return budgetIssue;
2905
+ const clientCount = numberValue(data["clientCount"]);
2906
+ const clientBudget = numberValue(data["clientBudget"]);
2907
+ if (clientCount !== void 0 && clientBudget !== void 0 && clientBudget > 0) {
2908
+ const ratio = clientCount / clientBudget;
2909
+ if (ratio >= 1) return "exhausted";
2910
+ if (ratio >= 0.75) return "warning";
2911
+ }
2912
+ return void 0;
2913
+ }
2914
+ __name(inspectMcpBudget, "inspectMcpBudget");
2915
+ function inspectBudgetContainers(data) {
2916
+ let result;
2917
+ visitStatusContainers(data, (record) => {
2918
+ if (result === "exhausted") return;
2919
+ const errorKind = record["errorKind"];
2920
+ const disabledReason = record["disabledReason"];
2921
+ const status = record["status"];
2922
+ const kind = record["kind"];
2923
+ const refusedCount = numberValue(record["refusedCount"]);
2924
+ if (errorKind === "budget_exhausted" || disabledReason === "budget" || kind === "mcp_budget" && status === "error" || refusedCount !== void 0 && refusedCount > 0) {
2925
+ result = "exhausted";
2926
+ return;
2927
+ }
2928
+ if (kind === "mcp_budget" && status === "warning") {
2929
+ result = "warning";
2930
+ }
2931
+ });
2932
+ return result;
2933
+ }
2934
+ __name(inspectBudgetContainers, "inspectBudgetContainers");
2935
+ function visitStatusContainers(data, visit) {
2936
+ if (!isRecord(data)) return;
2937
+ visit(data);
2938
+ for (const key of [
2939
+ "cells",
2940
+ "errors",
2941
+ "servers",
2942
+ "budgets",
2943
+ "skills",
2944
+ "tools",
2945
+ "providers",
2946
+ "hooks",
2947
+ "extensions"
2948
+ ]) {
2949
+ const value = data[key];
2950
+ if (!Array.isArray(value)) continue;
2951
+ for (const item of value) visitStatusContainers(item, visit);
2952
+ }
2953
+ }
2954
+ __name(visitStatusContainers, "visitStatusContainers");
2955
+ function rollupStatus(issues) {
2956
+ if (issues.some((issue) => issue.severity === "error")) return "error";
2957
+ if (issues.length > 0) return "warning";
2958
+ return "ok";
2959
+ }
2960
+ __name(rollupStatus, "rollupStatus");
2961
+ function allowOriginMode(allowOrigins) {
2962
+ if (!allowOrigins || allowOrigins.length === 0) return "none";
2963
+ return allowOrigins.includes("*") ? "any" : "specific";
2964
+ }
2965
+ __name(allowOriginMode, "allowOriginMode");
2966
+ function listenerMaxConnections(value) {
2967
+ if (value === void 0) return DEFAULT_LISTENER_MAX_CONNECTIONS;
2968
+ if (value === 0 || value === Infinity) return null;
2969
+ return Number.isFinite(value) && value > 0 ? value : null;
2970
+ }
2971
+ __name(listenerMaxConnections, "listenerMaxConnections");
2972
+ function positiveFiniteOrNull(value) {
2973
+ return value !== void 0 && Number.isFinite(value) && value > 0 ? value : null;
2974
+ }
2975
+ __name(positiveFiniteOrNull, "positiveFiniteOrNull");
2976
+ function zeroRateHits() {
2977
+ return { prompt: 0, mutation: 0, read: 0 };
2978
+ }
2979
+ __name(zeroRateHits, "zeroRateHits");
2980
+ function sumRateHits(hits) {
2981
+ return hits.prompt + hits.mutation + hits.read;
2982
+ }
2983
+ __name(sumRateHits, "sumRateHits");
2984
+ function isRecord(value) {
2985
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2986
+ }
2987
+ __name(isRecord, "isRecord");
2988
+ function numberValue(value) {
2989
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2990
+ }
2991
+ __name(numberValue, "numberValue");
2992
+ function copyBoolean(from, to, key) {
2993
+ const value = from[key];
2994
+ if (typeof value === "boolean") to[key] = value;
2995
+ }
2996
+ __name(copyBoolean, "copyBoolean");
2997
+ function copyString(from, to, key) {
2998
+ const value = from[key];
2999
+ if (typeof value === "string") to[key] = value;
3000
+ }
3001
+ __name(copyString, "copyString");
3002
+ function copyNumber(from, to, key) {
3003
+ const value = numberValue(from[key]);
3004
+ if (value !== void 0) to[key] = value;
3005
+ }
3006
+ __name(copyNumber, "copyNumber");
3007
+
3008
+ // packages/cli/src/serve/cdp-mcp-command.ts
3009
+ init_esbuild_shims();
3010
+ var QWEN_CDP_MCP_COMMAND_ENV = "QWEN_CDP_MCP_COMMAND";
3011
+ function resolveCdpMcpCommand(env) {
3012
+ const command = env[QWEN_CDP_MCP_COMMAND_ENV]?.trim();
3013
+ return command ? command : void 0;
3014
+ }
3015
+ __name(resolveCdpMcpCommand, "resolveCdpMcpCommand");
3016
+ function isBrowserAutomationMcpAvailable(opts, env) {
3017
+ return opts.cdpTunnelOverWs === true && !opts.token && resolveAcpHttpEnabled(env) && resolveCdpMcpCommand(env) !== void 0;
3018
+ }
3019
+ __name(isBrowserAutomationMcpAvailable, "isBrowserAutomationMcpAvailable");
3020
+
3021
+ // packages/cli/src/serve/voice/workspace-voice-coordinator.ts
3022
+ init_esbuild_shims();
3023
+ var MAX_CONCURRENT_VOICE_SESSIONS = 8;
3024
+ var DISPOSE_WAIT_MS = 5e3;
3025
+ var VoiceLeaseAbortError = class extends Error {
3026
+ constructor(kind, message) {
3027
+ super(message);
3028
+ this.kind = kind;
3029
+ this.name = "VoiceLeaseAbortError";
3030
+ }
3031
+ static {
3032
+ __name(this, "VoiceLeaseAbortError");
3033
+ }
3034
+ };
3035
+ var Lease = class {
3036
+ constructor(onRelease) {
3037
+ this.onRelease = onRelease;
3038
+ }
3039
+ static {
3040
+ __name(this, "Lease");
3041
+ }
3042
+ controller = new AbortController();
3043
+ signal = this.controller.signal;
3044
+ released = false;
3045
+ release() {
3046
+ if (this.released) return;
3047
+ this.released = true;
3048
+ this.onRelease(this);
3049
+ }
3050
+ abort(reason) {
3051
+ if (!this.signal.aborted) this.controller.abort(reason);
3052
+ }
3053
+ };
3054
+ var WorkspaceVoiceCoordinator = class {
3055
+ static {
3056
+ __name(this, "WorkspaceVoiceCoordinator");
3057
+ }
3058
+ states = /* @__PURE__ */ new Map();
3059
+ disposed = /* @__PURE__ */ new WeakSet();
3060
+ active = 0;
3061
+ acquire(runtime) {
3062
+ if (this.disposed.has(runtime)) {
3063
+ return { kind: "rejected", reason: "draining" };
3064
+ }
3065
+ const state = this.stateFor(runtime);
3066
+ if (state.draining) return { kind: "rejected", reason: "draining" };
3067
+ if (this.active >= MAX_CONCURRENT_VOICE_SESSIONS) {
3068
+ return { kind: "rejected", reason: "capacity" };
3069
+ }
3070
+ const lease = new Lease((current) => this.release(runtime, current));
3071
+ state.leases.add(lease);
3072
+ this.active++;
3073
+ return { kind: "admitted", lease };
3074
+ }
3075
+ beginWorkspaceDrain(runtime) {
3076
+ this.stateFor(runtime).draining = true;
3077
+ }
3078
+ cancelWorkspaceDrain(runtime) {
3079
+ const state = this.states.get(runtime);
3080
+ if (!state || state.completed) return;
3081
+ state.draining = false;
3082
+ if (state.leases.size === 0) this.states.delete(runtime);
3083
+ }
3084
+ completeWorkspaceDrain(runtime) {
3085
+ this.disposed.add(runtime);
3086
+ const state = this.states.get(runtime);
3087
+ if (!state) return;
3088
+ state.draining = true;
3089
+ state.completed = true;
3090
+ const abortReason = new VoiceLeaseAbortError(
3091
+ "workspace_removed",
3092
+ "Workspace drain completed"
3093
+ );
3094
+ for (const lease of state.leases) lease.abort(abortReason);
3095
+ this.deleteIfIdle(runtime, state);
3096
+ }
3097
+ getWorkspaceActivity(runtime) {
3098
+ return this.states.get(runtime)?.leases.size ?? 0;
3099
+ }
3100
+ async disposeRuntime(runtime, reason) {
3101
+ this.disposed.add(runtime);
3102
+ const state = this.states.get(runtime);
3103
+ if (!state) return;
3104
+ state.draining = true;
3105
+ state.completed = true;
3106
+ const abortReason = new VoiceLeaseAbortError(
3107
+ reason,
3108
+ reason === "daemon_shutdown" ? "Daemon is shutting down" : reason === "workspace_removed" ? "Workspace runtime was removed" : "Workspace trust was reconfigured"
3109
+ );
3110
+ for (const lease of state.leases) lease.abort(abortReason);
3111
+ if (state.leases.size === 0) {
3112
+ this.deleteIfIdle(runtime, state);
3113
+ return;
3114
+ }
3115
+ let resolveIdle;
3116
+ const idle = new Promise((resolve2) => {
3117
+ resolveIdle = resolve2;
3118
+ state.idleWaiters.add(resolve2);
3119
+ });
3120
+ let timeout;
3121
+ let timedOut = false;
3122
+ try {
3123
+ await Promise.race([
3124
+ idle,
3125
+ new Promise((resolve2) => {
3126
+ timeout = setTimeout(() => {
3127
+ timedOut = true;
3128
+ resolve2();
3129
+ }, DISPOSE_WAIT_MS);
3130
+ timeout.unref?.();
3131
+ })
3132
+ ]);
3133
+ } finally {
3134
+ if (timeout) clearTimeout(timeout);
3135
+ if (resolveIdle) state.idleWaiters.delete(resolveIdle);
3136
+ }
3137
+ if (timedOut && state.leases.size > 0) {
3138
+ process.stderr.write(
3139
+ `qwen serve: Voice runtime disposal timed out with ${state.leases.size} active lease(s).
3140
+ `
3141
+ );
3142
+ }
3143
+ }
3144
+ stateFor(runtime) {
3145
+ let state = this.states.get(runtime);
3146
+ if (!state) {
3147
+ state = {
3148
+ draining: false,
3149
+ completed: false,
3150
+ leases: /* @__PURE__ */ new Set(),
3151
+ idleWaiters: /* @__PURE__ */ new Set()
3152
+ };
3153
+ this.states.set(runtime, state);
3154
+ }
3155
+ return state;
3156
+ }
3157
+ release(runtime, lease) {
3158
+ const state = this.states.get(runtime);
3159
+ if (!state || !state.leases.delete(lease)) return;
3160
+ this.active--;
3161
+ if (state.leases.size === 0) {
3162
+ for (const resolve2 of state.idleWaiters) resolve2();
3163
+ state.idleWaiters.clear();
3164
+ }
3165
+ this.deleteIfIdle(runtime, state);
3166
+ }
3167
+ deleteIfIdle(runtime, state) {
3168
+ if (state.completed && state.leases.size === 0) this.states.delete(runtime);
3169
+ }
3170
+ };
3171
+
3172
+ // packages/cli/src/serve/server/access-log.ts
3173
+ init_esbuild_shims();
3174
+ import { performance } from "node:perf_hooks";
3175
+ var SESSION_ID_RE = /\/session\/([^/]+)/;
3176
+ var ACCESS_LOG_BURST = 60;
3177
+ var ACCESS_LOG_REFILL_PER_SECOND = 2;
3178
+ var ROUTE_MAX_BYTES = 2 * 1024;
3179
+ var SESSION_ID_MAX_BYTES = 256;
3180
+ var CLIENT_ID_MAX_BYTES = 256;
3181
+ var ACCESS_LOG_CONTROLLER_LOCAL = "accessLogController";
3182
+ function emptySuppressedCounts() {
3183
+ return {
3184
+ suppressed: 0,
3185
+ status2xx: 0,
3186
+ status3xx: 0,
3187
+ status4xx: 0,
3188
+ status5xx: 0,
3189
+ statusOther: 0
3190
+ };
3191
+ }
3192
+ __name(emptySuppressedCounts, "emptySuppressedCounts");
3193
+ function countSuppressed(counts, status) {
3194
+ counts.suppressed += 1;
3195
+ if (status >= 200 && status < 300) counts.status2xx += 1;
3196
+ else if (status >= 300 && status < 400) counts.status3xx += 1;
3197
+ else if (status >= 400 && status < 500) counts.status4xx += 1;
3198
+ else if (status >= 500 && status < 600) counts.status5xx += 1;
3199
+ else counts.statusOther += 1;
3200
+ }
3201
+ __name(countSuppressed, "countSuppressed");
3202
+ function firstRawHeader(rawHeaders, targetName) {
3203
+ for (let i = 0; i + 1 < rawHeaders.length; i += 2) {
3204
+ if (rawHeaders[i]?.toLowerCase() === targetName) return rawHeaders[i + 1];
3205
+ }
3206
+ return void 0;
3207
+ }
3208
+ __name(firstRawHeader, "firstRawHeader");
3209
+ function truncateUtf8(value, maxBytes) {
3210
+ const bytes = Buffer.from(value, "utf8");
3211
+ if (bytes.length <= maxBytes) return { value };
3212
+ let end = maxBytes;
3213
+ while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
3214
+ return {
3215
+ value: bytes.subarray(0, end).toString("utf8"),
3216
+ originalBytes: bytes.length
3217
+ };
3218
+ }
3219
+ __name(truncateUtf8, "truncateUtf8");
3220
+ function installAccessLogMiddleware(app, daemonLog, monotonicNow = () => performance.now()) {
3221
+ let sealed = false;
3222
+ let tokens = ACCESS_LOG_BURST;
3223
+ let refillBaseline = monotonicNow();
3224
+ let suppressed = emptySuppressedCounts();
3225
+ const refill = /* @__PURE__ */ __name(() => {
3226
+ const now = Math.max(monotonicNow(), refillBaseline);
3227
+ tokens = Math.min(
3228
+ ACCESS_LOG_BURST,
3229
+ tokens + (now - refillBaseline) / 1e3 * ACCESS_LOG_REFILL_PER_SECOND
3230
+ );
3231
+ refillBaseline = now;
3232
+ }, "refill");
3233
+ const flushSuppressed = /* @__PURE__ */ __name(() => {
3234
+ if (!daemonLog || suppressed.suppressed === 0) return false;
3235
+ daemonLog.warn("access logs suppressed", suppressed);
3236
+ suppressed = emptySuppressedCounts();
3237
+ return true;
3238
+ }, "flushSuppressed");
3239
+ const controller = {
3240
+ sealAndFlushSuppressed: /* @__PURE__ */ __name(() => {
3241
+ if (sealed) return;
3242
+ sealed = true;
3243
+ try {
3244
+ flushSuppressed();
3245
+ } catch {
3246
+ }
3247
+ }, "sealAndFlushSuppressed")
3248
+ };
3249
+ app.locals[ACCESS_LOG_CONTROLLER_LOCAL] = controller;
3250
+ if (!daemonLog) return controller;
3251
+ app.use((req, res, next) => {
3252
+ const { method, path: reqPath } = req;
3253
+ if (method === "GET" && reqPath === "/health" || method === "POST" && reqPath.endsWith("/heartbeat")) {
3254
+ return next();
3255
+ }
3256
+ const startMs = monotonicNow();
3257
+ res.on("finish", () => {
3258
+ try {
3259
+ if (sealed) return;
3260
+ const status = res.statusCode;
3261
+ if (method === "GET" && reqPath.endsWith("/events") && status === 200) {
3262
+ return;
3263
+ }
3264
+ refill();
3265
+ if (suppressed.suppressed > 0 && tokens >= 1) {
3266
+ tokens -= 1;
3267
+ flushSuppressed();
3268
+ }
3269
+ if (tokens < 1) {
3270
+ countSuppressed(suppressed, status);
3271
+ return;
3272
+ }
3273
+ tokens -= 1;
3274
+ const route = truncateUtf8(`${method} ${reqPath}`, ROUTE_MAX_BYTES);
3275
+ const sessionMatch = reqPath.match(SESSION_ID_RE);
3276
+ const sessionId = sessionMatch?.[1] ? truncateUtf8(sessionMatch[1], SESSION_ID_MAX_BYTES) : void 0;
3277
+ const rawClientId = firstRawHeader(req.rawHeaders, "x-qwen-client-id");
3278
+ const clientId = rawClientId ? truncateUtf8(rawClientId, CLIENT_ID_MAX_BYTES) : void 0;
3279
+ const ctx = {
3280
+ route: route.value,
3281
+ ...route.originalBytes ? { routeOriginalBytes: route.originalBytes } : {},
3282
+ ...sessionId ? {
3283
+ sessionId: sessionId.value,
3284
+ ...sessionId.originalBytes ? { sessionIdOriginalBytes: sessionId.originalBytes } : {}
3285
+ } : {},
3286
+ ...clientId ? {
3287
+ clientId: clientId.value,
3288
+ ...clientId.originalBytes ? { clientIdOriginalBytes: clientId.originalBytes } : {}
3289
+ } : {},
3290
+ status,
3291
+ durationMs: Math.max(0, Math.round(monotonicNow() - startMs))
3292
+ };
3293
+ if (status >= 400) daemonLog.warn("request completed", ctx);
3294
+ else daemonLog.info("request completed", ctx);
3295
+ } catch {
3296
+ }
3297
+ });
3298
+ next();
3299
+ });
3300
+ return controller;
3301
+ }
3302
+ __name(installAccessLogMiddleware, "installAccessLogMiddleware");
3303
+
3304
+ export {
3305
+ sanitizeForStderr,
3306
+ brandSecret,
3307
+ unsafeRevealSecret,
3308
+ UnsupportedDeviceFlowProviderError,
3309
+ TooManyActiveDeviceFlowsError,
3310
+ UpstreamDeviceFlowError,
3311
+ setDeviceFlowRegistry,
3312
+ getDeviceFlowRegistry,
3313
+ DeviceFlowRegistry,
3314
+ createBridgeFileSystemAdapter,
3315
+ PathMutexRegistry,
3316
+ isDeepHealthQuery,
3317
+ denyBrowserOriginCors,
3318
+ parseAllowOriginPatterns,
3319
+ allowOriginCors,
3320
+ hostAllowlist,
3321
+ bearerAuth,
3322
+ createMutationGate,
3323
+ ClientMcpSenderRegistry,
3324
+ createClientMcpServerProvider,
3325
+ SERVE_CAPABILITY_REGISTRY,
3326
+ getAdvertisedServeFeatures,
3327
+ getServeProtocolVersions,
3328
+ CAPABILITIES_SCHEMA_VERSION,
3329
+ isManagedScratchChild,
3330
+ isScratchRootCompatible,
3331
+ prepareManagedScratchRoot,
3332
+ createManagedScratchDirectory,
3333
+ sendJsonBodyParserError,
3334
+ CLIENT_ID_HEADER,
3335
+ MAX_CLIENT_ID_LENGTH,
3336
+ MAX_TOOL_NAME_LENGTH,
3337
+ MAX_SERVER_NAME_LENGTH,
3338
+ CLIENT_ID_RE,
3339
+ setDeferredRuntimeRequestTiming,
3340
+ getDeferredRuntimeRequestTiming,
3341
+ safeBody,
3342
+ parseOptionalWorkspaceCwd,
3343
+ requireSessionId,
3344
+ parseClientIdHeader,
3345
+ detectFromLoopback,
3346
+ validateMcpRuntimeServerName,
3347
+ parseAndValidateWorkspaceClientId,
3348
+ createBuildWorkspaceCtx,
3349
+ parsePermissionVoteBody,
3350
+ parseMaxQueuedQuery,
3351
+ safeLogValue,
3352
+ parseLastEventId,
3353
+ createRateLimiter,
3354
+ setRateLimiter,
3355
+ getRateLimiter,
3356
+ resolveAcpHttpEnabled,
3357
+ toDaemonStatusMemoryLimits,
3358
+ parseDaemonStatusDetail,
3359
+ buildDaemonStatusResponse,
3360
+ allowOriginMode,
3361
+ listenerMaxConnections,
3362
+ positiveFiniteOrNull,
3363
+ QWEN_CDP_MCP_COMMAND_ENV,
3364
+ resolveCdpMcpCommand,
3365
+ isBrowserAutomationMcpAvailable,
3366
+ MAX_CONCURRENT_VOICE_SESSIONS,
3367
+ VoiceLeaseAbortError,
3368
+ WorkspaceVoiceCoordinator,
3369
+ ACCESS_LOG_CONTROLLER_LOCAL,
3370
+ installAccessLogMiddleware
3371
+ };
3372
+ /**
3373
+ * @license
3374
+ * Copyright 2025 Qwen Team
3375
+ * SPDX-License-Identifier: Apache-2.0
3376
+ */
3377
+ /**
3378
+ * @license
3379
+ * Copyright 2026 Qwen Team
3380
+ * SPDX-License-Identifier: Apache-2.0
3381
+ */