@zubriq/code 0.19.8-2 → 0.19.9-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundled/qc-helper/docs/features/channels/_meta.ts +1 -0
- package/dist/bundled/qc-helper/docs/features/channels/overview.md +23 -12
- package/dist/bundled/qc-helper/docs/features/channels/plugins.md +1 -0
- package/dist/bundled/qc-helper/docs/features/channels/wecom.md +111 -0
- package/dist/bundled/qc-helper/docs/features/hooks.md +36 -34
- package/dist/bundled/qc-helper/docs/features/memory.md +1 -1
- package/dist/bundled/qc-helper/docs/qwen-serve.md +17 -10
- package/dist/bundled/review/SKILL.md +6 -2
- package/dist/chunks/{MaxSizedBox-DTQF2MAY.js → MaxSizedBox-W6SAJMJC.js} +13 -11
- package/dist/chunks/{StandaloneSessionPicker-XYO5V6IL.js → StandaloneSessionPicker-YBKZZKIA.js} +28 -23
- package/dist/chunks/{acpAgent-STMSDYGP.js → acpAgent-SQJQBOTS.js} +110 -80
- package/dist/chunks/{agent-C6YBZNSA.js → agent-QU7Q2RZN.js} +8 -8
- package/dist/chunks/{agent-headless-65TQRHOY.js → agent-headless-PBHLWHND.js} +8 -8
- package/dist/chunks/{anthropicContentGenerator-FZQAEMYK.js → anthropicContentGenerator-52G6ZA3D.js} +31 -4
- package/dist/chunks/{artifact-tool-H2TNPNP5.js → artifact-tool-D233YJFH.js} +25 -6
- package/dist/chunks/{bridge-GHGQNQRB.js → bridge-LJLLDUMW.js} +17 -14
- package/dist/chunks/{ca-HCMB2I54.js → ca-MKD32FQL.js} +26 -1
- package/dist/chunks/{channel-worker-supervisor-B7MSMB5K.js → channel-worker-supervisor-NZQUOEFJ.js} +3 -3
- package/dist/chunks/{chunk-O3DVJ2JO.js → chunk-27AYMAL6.js} +506 -63
- package/dist/chunks/{chunk-AKSRZ5BA.js → chunk-2MDC5WDB.js} +17 -111
- package/dist/chunks/{chunk-GKUVMLJH.js → chunk-2PRSBVPI.js} +3 -3
- package/dist/chunks/chunk-332PWN27.js +19 -0
- package/dist/chunks/{chunk-FVMWEHWU.js → chunk-3B26UVCX.js} +114 -12
- package/dist/chunks/{chunk-46IFEQDM.js → chunk-3F22JT4E.js} +1 -1
- package/dist/chunks/{chunk-W6RH6KHR.js → chunk-3GPVYALM.js} +4 -2
- package/dist/chunks/{chunk-G7ZG7CA6.js → chunk-3UP777ZS.js} +8 -7
- package/dist/chunks/{chunk-TYOF23VK.js → chunk-43W4YSO6.js} +1 -12
- package/dist/chunks/{chunk-5XZ6FOTY.js → chunk-4IK56VLC.js} +1 -1
- package/dist/chunks/{chunk-GSNACUDM.js → chunk-5FAUIGXH.js} +5 -13
- package/dist/chunks/{chunk-VEB3NYSO.js → chunk-5MBO76SL.js} +5 -0
- package/dist/chunks/{chunk-FACTYQFS.js → chunk-5MRUKUOG.js} +5347 -4132
- package/dist/chunks/{chunk-DH7XPKJV.js → chunk-6EUMFVPJ.js} +5 -5
- package/dist/chunks/{chunk-ZCMLUE4O.js → chunk-6JL5OVLX.js} +1 -1
- package/dist/chunks/{chunk-A6XKBVOM.js → chunk-74E6VVF6.js} +11 -11
- package/dist/chunks/chunk-A2X3KLC3.js +3807 -0
- package/dist/chunks/{chunk-TJS7CXNX.js → chunk-B6B2MUUF.js} +5 -5
- package/dist/chunks/{chunk-OMO3UY57.js → chunk-BN5VYAOZ.js} +681 -1232
- package/dist/chunks/{chunk-AXYGF5SX.js → chunk-BYUA6YYS.js} +15 -9
- package/dist/chunks/{chunk-NCFY7PWS.js → chunk-CCGOFQV3.js} +51 -2
- package/dist/chunks/{chunk-66KNL43D.js → chunk-CD6USWHZ.js} +1 -1
- package/dist/chunks/{chunk-LLVYPOQV.js → chunk-CEITMQ62.js} +2 -2
- package/dist/chunks/{chunk-PMMJS5AM.js → chunk-EJP6Z7NU.js} +4 -3
- package/dist/chunks/{chunk-PY6PG4BL.js → chunk-EZTNYRHH.js} +6 -5
- package/dist/chunks/{chunk-PADAEB6D.js → chunk-FAATDXYS.js} +308 -81
- package/dist/chunks/{chunk-DNV4FSN5.js → chunk-FBET5XZ2.js} +6 -4
- package/dist/chunks/{chunk-3CRBBLJZ.js → chunk-FES4YXJ3.js} +9 -9
- package/dist/chunks/{chunk-6N76CE6T.js → chunk-FHTUTVIQ.js} +10 -5
- package/dist/chunks/chunk-FII3C4E7.js +168 -0
- package/dist/chunks/{chunk-2NL7ED5Q.js → chunk-FV7425LN.js} +22 -1
- package/dist/chunks/{chunk-6UBX2BIL.js → chunk-GAI76CFX.js} +4 -4
- package/dist/chunks/{chunk-B34OV7K2.js → chunk-GGQQA4JW.js} +13 -0
- package/dist/chunks/chunk-GLE2YUPU.js +34 -0
- package/dist/chunks/{chunk-X265R5C6.js → chunk-GN3F6PIU.js} +1 -1
- package/dist/chunks/{chunk-PYWGOON5.js → chunk-GWUN54UD.js} +1 -1
- package/dist/chunks/chunk-H3TELH3H.js +942 -0
- package/dist/chunks/{chunk-3SBUWSLI.js → chunk-HD4DZI2L.js} +2 -2
- package/dist/chunks/{chunk-HXVVCTID.js → chunk-HPQIVXSP.js} +1 -1
- package/dist/chunks/{chunk-WSVZFWN4.js → chunk-IBY3Q3HG.js} +693 -104
- package/dist/chunks/{chunk-P42OA4GP.js → chunk-JGR6W4IX.js} +14 -8
- package/dist/chunks/chunk-K652V4NH.js +283 -0
- package/dist/chunks/{chunk-QUR23Z2Q.js → chunk-KH2CZZDD.js} +2 -2
- package/dist/chunks/chunk-LGFAYIKX.js +115 -0
- package/dist/chunks/{chunk-FT5ASRN5.js → chunk-LZDW5TDV.js} +5 -5
- package/dist/chunks/{chunk-COPJASIO.js → chunk-MA4AZNP7.js} +10 -10
- package/dist/chunks/{chunk-4RQIB5UZ.js → chunk-O2YT7VB4.js} +0 -34
- package/dist/chunks/{chunk-4KJ2BPIT.js → chunk-OACJLMLD.js} +2 -38
- package/dist/chunks/{chunk-UMU5USBC.js → chunk-PCWYMX57.js} +14 -153
- package/dist/chunks/{chunk-Z33V6JSK.js → chunk-Q3PQ5VGJ.js} +1 -1
- package/dist/chunks/{chunk-GXQXFHYV.js → chunk-QK3I36QG.js} +1 -1
- package/dist/chunks/chunk-RA7TR75G.js +8223 -0
- package/dist/chunks/{chunk-KSPVDFE4.js → chunk-SAIILPQ3.js} +0 -3
- package/dist/chunks/{chunk-65A6UQDZ.js → chunk-SNZPA74J.js} +146 -54
- package/dist/chunks/{chunk-AGTU5PQ6.js → chunk-SSP3UT5C.js} +4 -4
- package/dist/chunks/{chunk-SWHGVEH5.js → chunk-SUCRVT5L.js} +1 -1
- package/dist/chunks/{chunk-SVK2TREP.js → chunk-TRR5KGDN.js} +1 -1
- package/dist/chunks/chunk-UIDXQNMV.js +13 -0
- package/dist/chunks/{chunk-PIORL4OD.js → chunk-UKV3D7WB.js} +0 -44
- package/dist/chunks/chunk-UYNUIGUR.js +167 -0
- package/dist/chunks/chunk-V2TYBEWU.js +28 -0
- package/dist/chunks/chunk-V35MD7KN.js +13 -0
- package/dist/chunks/{chunk-Z3VQSA4A.js → chunk-VR3KCBHH.js} +1 -1
- package/dist/chunks/{chunk-VQZSRYKB.js → chunk-W4CTWW7M.js} +423 -61
- package/dist/chunks/{chunk-7SZJYZPR.js → chunk-WJAW2O5H.js} +92 -465
- package/dist/chunks/{chunk-23TH7TAA.js → chunk-WN4YJJGH.js} +2 -2
- package/dist/chunks/chunk-XNXVCNAT.js +88 -0
- package/dist/chunks/{chunk-TDGF35EN.js → chunk-XOHNA2S6.js} +1 -1
- package/dist/chunks/chunk-XQPMKWSV.js +487 -0
- package/dist/chunks/{chunk-VEDR5V7H.js → chunk-XWDHZP7P.js} +8 -8
- package/dist/chunks/{chunk-VKWGD42I.js → chunk-YCKKP4QA.js} +10 -10
- package/dist/chunks/{chunk-3Y2SRVRF.js → chunk-YJWMOGGZ.js} +3 -3
- package/dist/chunks/{chunk-RFIL6REB.js → chunk-YKZIAK7C.js} +119 -29
- package/dist/chunks/{chunk-4KMU5RWV.js → chunk-YZDBUH6S.js} +45 -9
- package/dist/chunks/chunk-Z3I3QJK3.js +47 -0
- package/dist/chunks/{chunk-RTCBBRJK.js → chunk-ZDGPPCTW.js} +20 -8
- package/dist/chunks/chunk-ZL5HN75Q.js +61 -0
- package/dist/chunks/{chunk-J6GMF2DO.js → chunk-ZL7JCYWM.js} +66 -7
- package/dist/chunks/{computer-use-BH6F7LI7.js → computer-use-4B45EG2F.js} +8 -8
- package/dist/chunks/{contextCommand-URWDQXBJ.js → contextCommand-GIVRWNTS.js} +12 -11
- package/dist/chunks/{cron-create-DDYU4DIR.js → cron-create-JSXEIPVS.js} +1 -1
- package/dist/chunks/{cron-delete-DFKOUZWN.js → cron-delete-OIFVEPGU.js} +1 -1
- package/dist/chunks/{cron-list-ME5QGAR5.js → cron-list-D4ZX4IOE.js} +1 -1
- package/dist/chunks/{daemon-XWG2EGFX.js → daemon-WPAENEBW.js} +71 -3
- package/dist/chunks/{daemon-status-provider-CLNN2UY2.js → daemon-status-provider-WBOAMCHE.js} +20 -17
- package/dist/chunks/{de-PLLFDERI.js → de-ZFPPQVSF.js} +26 -1
- package/dist/chunks/{dist-LPVU3L2M.js → dist-BEXOZXLQ.js} +6 -3792
- package/dist/chunks/{dist-7GU63KI5.js → dist-E6UANPIM.js} +1 -1
- package/dist/chunks/{dist-VHBP2PIG.js → dist-H2AEJCZ5.js} +2 -2
- package/dist/chunks/{dist-3UDX5T4T.js → dist-O2IRTRPP.js} +1 -1
- package/dist/chunks/dist-SCFLLYMK.js +3217 -0
- package/dist/chunks/{dist-H4HHZYCI.js → dist-Z34QPAQW.js} +2 -2
- package/dist/chunks/{earlyInputCapture-SFINSVH2.js → earlyInputCapture-JWMCAJZR.js} +8 -8
- package/dist/chunks/{edit-5OJRPFGZ.js → edit-O5FUVHBE.js} +9 -9
- package/dist/chunks/{en-AXARKOWO.js → en-N4EPZAQI.js} +31 -2
- package/dist/chunks/{enter-worktree-HK54JBSR.js → enter-worktree-LOJFRC3D.js} +8 -8
- package/dist/chunks/{enterPlanMode-5GXOH5PQ.js → enterPlanMode-VS3FAUPX.js} +8 -8
- package/dist/chunks/environment-WKW5TLSH.js +90 -0
- package/dist/chunks/{errors-73SZF7O7.js → errors-LEZXHJMR.js} +10 -10
- package/dist/chunks/{exit-worktree-CBX23VFX.js → exit-worktree-RN2NXRMP.js} +8 -8
- package/dist/chunks/{exitPlanMode-HADS7AAV.js → exitPlanMode-EPC424I4.js} +8 -8
- package/dist/chunks/{fast-path-3QQW3W6W.js → fast-path-UUPAFOSQ.js} +12 -3
- package/dist/chunks/{fast-path-settings-RCRXH2JG.js → fast-path-settings-7RIFI3PK.js} +3 -2
- package/dist/chunks/{fr-MBMNQSQ6.js → fr-5WWMY53B.js} +30 -1
- package/dist/chunks/{gemini-Q4KKJMFH.js → gemini-QCKK4XNU.js} +438 -265
- package/dist/chunks/{geminiContentGenerator-SBKWTYFU.js → geminiContentGenerator-DX4SRXJA.js} +1 -1
- package/dist/chunks/{glob-ORVXVDJJ.js → glob-4URMJWRX.js} +8 -8
- package/dist/chunks/{grep-TMSN6VOF.js → grep-AEQB5KED.js} +8 -8
- package/dist/chunks/handleAutoUpdate-Y4YNG34Y.js +65 -0
- package/dist/chunks/{initializer-NFO725XQ.js → initializer-NDEBMSOG.js} +17 -13
- package/dist/chunks/{ja-HFV5QCHR.js → ja-CUHZOLEJ.js} +30 -1
- package/dist/chunks/{list-NP6QTWKD.js → list-VSCBNOWT.js} +17 -15
- package/dist/chunks/{loadedSettingsAdapter-5PXZC4ZO.js → loadedSettingsAdapter-FDO752BA.js} +15 -13
- package/dist/chunks/{loop-wakeup-ZW5P6VPY.js → loop-wakeup-OHWEMFP5.js} +2 -2
- package/dist/chunks/{ls-L654WOAT.js → ls-7MFU2QYW.js} +1 -1
- package/dist/chunks/{lsp-M35KE54Y.js → lsp-XHDTPTGV.js} +2 -4
- package/dist/chunks/{mcp-D3C75I47.js → mcp-MLPWTHUV.js} +15 -13
- package/dist/chunks/{monitor-YZFSN33Z.js → monitor-WCVHIJUJ.js} +16 -11
- package/dist/chunks/{nonInteractiveCli-U7ZDVLQD.js → nonInteractiveCli-32HZK274.js} +52 -44
- package/dist/chunks/{notebook-edit-LVCCTW65.js → notebook-edit-NPOMPKS4.js} +9 -9
- package/dist/chunks/{openaiContentGenerator-G4JHCIGX.js → openaiContentGenerator-6AIBSRQK.js} +6 -6
- package/dist/chunks/{pidfile-O37JZBNN.js → pidfile-7Y3QEDSZ.js} +8 -8
- package/dist/chunks/{pt-2THT3YHA.js → pt-OLYN42DS.js} +26 -1
- package/dist/chunks/{qwenContentGenerator-SPHKYVJF.js → qwenContentGenerator-OUYOAYT4.js} +10 -10
- package/dist/chunks/{read-file-MBGSR77H.js → read-file-EESFA24L.js} +3 -3
- package/dist/chunks/{record-artifact-PP3LKHTS.js → record-artifact-SQ2OKRJW.js} +3 -3
- package/dist/chunks/{ripGrep-WMM4G6QB.js → ripGrep-DLYV7ZZG.js} +8 -8
- package/dist/chunks/{ru-PER5I4OI.js → ru-MKM2HFDS.js} +26 -1
- package/dist/chunks/{run-qwen-serve-3BOGJBTS.js → run-qwen-serve-WUEHQROF.js} +602 -143
- package/dist/chunks/{scheduler-KWPXUSJ4.js → scheduler-SK4Y66V4.js} +8 -8
- package/dist/chunks/{send-message-OA6MV4EU.js → send-message-76GT3JMC.js} +1 -1
- package/dist/chunks/{serve-6JXYVZRD.js → serve-UCDGVZFI.js} +17 -14
- package/dist/chunks/{server-KOSHCREY.js → server-FVGFFVZ4.js} +1394 -328
- package/dist/chunks/{session-5RI6JYZ6.js → session-RIGKDMGR.js} +53 -45
- package/dist/chunks/{settings-URS74H6Z.js → settings-LVFFGXGE.js} +24 -20
- package/dist/chunks/{shell-6VOD5NUH.js → shell-PAWNAFYN.js} +8 -8
- package/dist/chunks/{skill-MKIISOW7.js → skill-5YIN3Z6Y.js} +15 -5
- package/dist/chunks/{spawnChannel-GM7HIIDH.js → spawnChannel-KIMZHWNR.js} +10 -10
- package/dist/chunks/{src-X2S7YA5D.js → src-BIWCD3VX.js} +21 -9
- package/dist/chunks/{startInteractiveUI-REEHDN4Z.js → startInteractiveUI-AFXLGUFI.js} +33759 -41620
- package/dist/chunks/{task-create-RPFZNB4B.js → task-create-WCHTB435.js} +2 -2
- package/dist/chunks/{task-update-5WUG5MVQ.js → task-update-XYWMHJGJ.js} +2 -2
- package/dist/chunks/{team-create-MQVOXIHZ.js → team-create-26WOMISV.js} +8 -8
- package/dist/chunks/{team-plan-approval-H74NA3K2.js → team-plan-approval-B5XB6UQC.js} +8 -8
- package/dist/chunks/{theme-manager-YMC4EJQO.js → theme-manager-4TJ5TEFD.js} +8 -8
- package/dist/chunks/{tool-search-VUJGWH7W.js → tool-search-FFU63E6M.js} +6 -23
- package/dist/chunks/total-session-admission-JOD7SMPJ.js +72 -0
- package/dist/chunks/{trustedFolders-UMMVN4P2.js → trustedFolders-IJ2QHXSC.js} +9 -9
- package/dist/chunks/updateCheck-5Z25DCBP.js +65 -0
- package/dist/chunks/{validateNonInterActiveAuth-ORA3KCZO.js → validateNonInterActiveAuth-G5EY47DE.js} +49 -41
- package/dist/chunks/{version-II46VXNW.js → version-LGO3GOKQ.js} +2 -1
- package/dist/chunks/{workflow-KYFPOWKK.js → workflow-ZTK3WT3D.js} +9 -9
- package/dist/chunks/{workspace-providers-status-QDQEYCHT.js → workspace-providers-status-YMPTRKW2.js} +18 -15
- package/dist/chunks/workspace-registry-NBUL6LBH.js +74 -0
- package/dist/chunks/{workspace-service-YBVUOSP7.js → workspace-service-V6G2RCD4.js} +21 -19
- package/dist/chunks/{workspace-skills-status-3ASS4VEE.js → workspace-skills-status-ZCW44BCH.js} +16 -14
- package/dist/chunks/{write-file-7ZHZO4K5.js → write-file-4FUTAEW6.js} +9 -9
- package/dist/chunks/{zh-RS3HZHFH.js → zh-J4F6QWQV.js} +31 -2
- package/dist/chunks/{zh-TW-L5GRQ4S4.js → zh-TW-XVDWOLSW.js} +31 -2
- package/dist/cli.js +11 -11
- package/dist/locales/ca.js +30 -0
- package/dist/locales/de.js +30 -0
- package/dist/locales/en.js +38 -2
- package/dist/locales/fr.js +36 -0
- package/dist/locales/ja.js +36 -0
- package/dist/locales/pt.js +30 -0
- package/dist/locales/ru.js +30 -0
- package/dist/locales/zh-TW.js +38 -2
- package/dist/locales/zh.js +38 -2
- package/dist/package.json +3 -3
- package/dist/web-shell/assets/{arc-C6tzvtOa.js → arc-BWyoJpY2.js} +1 -1
- package/dist/web-shell/assets/{architectureDiagram-3BPJPVTR-DSYQFGn9.js → architectureDiagram-3BPJPVTR-BT0mGrz2.js} +1 -1
- package/dist/web-shell/assets/{blockDiagram-GPEHLZMM-CHjBdXAD.js → blockDiagram-GPEHLZMM-BLgUWvyf.js} +1 -1
- package/dist/web-shell/assets/{c4Diagram-AAUBKEIU-BQzK6Yyb.js → c4Diagram-AAUBKEIU-DYx9M7oU.js} +1 -1
- package/dist/web-shell/assets/channel-wmPUGHtg.js +1 -0
- package/dist/web-shell/assets/{chunk-2J33WTMH-hpP2p3Ij.js → chunk-2J33WTMH-CfMHWbuZ.js} +1 -1
- package/dist/web-shell/assets/{chunk-4BX2VUAB-B5KtK1JJ.js → chunk-4BX2VUAB-B1URoqEV.js} +1 -1
- package/dist/web-shell/assets/{chunk-55IACEB6-BDv6Mfr2.js → chunk-55IACEB6-DAIaM4QF.js} +1 -1
- package/dist/web-shell/assets/{chunk-727SXJPM-BevPWfrX.js → chunk-727SXJPM-B2X34knw.js} +1 -1
- package/dist/web-shell/assets/{chunk-AQP2D5EJ-ChDrz2ad.js → chunk-AQP2D5EJ-Cif-zLTQ.js} +1 -1
- package/dist/web-shell/assets/{chunk-FMBD7UC4-ClMdVSDs.js → chunk-FMBD7UC4-H18WCXdY.js} +1 -1
- package/dist/web-shell/assets/{chunk-ND2GUHAM-BceV8kZS.js → chunk-ND2GUHAM-YFtetHi0.js} +1 -1
- package/dist/web-shell/assets/{chunk-QZHKN3VN-DtyK2wwc.js → chunk-QZHKN3VN-By2hiH3r.js} +1 -1
- package/dist/web-shell/assets/classDiagram-4FO5ZUOK-BA2ljEjW.js +1 -0
- package/dist/web-shell/assets/classDiagram-v2-Q7XG4LA2-BA2ljEjW.js +1 -0
- package/dist/web-shell/assets/{cose-bilkent-S5V4N54A-BGMsqwmE.js → cose-bilkent-S5V4N54A-VrcnnLZu.js} +1 -1
- package/dist/web-shell/assets/{dagre-BM42HDAG-C-71clpF.js → dagre-BM42HDAG-CGo-IaVV.js} +1 -1
- package/dist/web-shell/assets/{diagram-2AECGRRQ-WIzzlkec.js → diagram-2AECGRRQ-Dl4QjzIB.js} +1 -1
- package/dist/web-shell/assets/{diagram-5GNKFQAL-CtnhZUP1.js → diagram-5GNKFQAL-qMLmsO0u.js} +1 -1
- package/dist/web-shell/assets/{diagram-KO2AKTUF-CS_IUezp.js → diagram-KO2AKTUF-CHlmuxPI.js} +1 -1
- package/dist/web-shell/assets/{diagram-LMA3HP47-BQnGRwN9.js → diagram-LMA3HP47-BUwU3GwT.js} +1 -1
- package/dist/web-shell/assets/{diagram-OG6HWLK6-By2VIocq.js → diagram-OG6HWLK6-HEhwPDUM.js} +1 -1
- package/dist/web-shell/assets/{erDiagram-TEJ5UH35-Dy8WeNkr.js → erDiagram-TEJ5UH35-1n0llBmM.js} +1 -1
- package/dist/web-shell/assets/{flowDiagram-I6XJVG4X-Diu_4y0w.js → flowDiagram-I6XJVG4X-BN74uMnt.js} +1 -1
- package/dist/web-shell/assets/{ganttDiagram-6RSMTGT7-CH8euujP.js → ganttDiagram-6RSMTGT7-BoOq78i3.js} +1 -1
- package/dist/web-shell/assets/{gitGraphDiagram-PVQCEYII-DHiObZq1.js → gitGraphDiagram-PVQCEYII-CN8b00c8.js} +1 -1
- package/dist/web-shell/assets/index-CNwoO1Cn.js +783 -0
- package/dist/web-shell/assets/index-CWQ0z6kZ.css +5 -0
- package/dist/web-shell/assets/{infoDiagram-5YYISTIA-CvC9A6fh.js → infoDiagram-5YYISTIA-CCurnIna.js} +1 -1
- package/dist/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK46kC4E.js → ishikawaDiagram-YF4QCWOH-AwuRqdUP.js} +1 -1
- package/dist/web-shell/assets/{journeyDiagram-JHISSGLW-B2TIiKq4.js → journeyDiagram-JHISSGLW-B7IICk-E.js} +1 -1
- package/dist/web-shell/assets/{kanban-definition-UN3LZRKU-Bh0WAulO.js → kanban-definition-UN3LZRKU-BYF0OKhx.js} +1 -1
- package/dist/web-shell/assets/{linear-Bxe9SHMk.js → linear-GWWguGfD.js} +1 -1
- package/dist/web-shell/assets/{mermaid.core-B1Pv6D5B.js → mermaid.core-JqtkxvCl.js} +5 -5
- package/dist/web-shell/assets/{mindmap-definition-RKZ34NQL-Bz0joLH8.js → mindmap-definition-RKZ34NQL-B0MmHsZ6.js} +1 -1
- package/dist/web-shell/assets/{pieDiagram-4H26LBE5-CnN4x-VS.js → pieDiagram-4H26LBE5-DhImUZLQ.js} +1 -1
- package/dist/web-shell/assets/{quadrantDiagram-W4KKPZXB-BYWD4xO4.js → quadrantDiagram-W4KKPZXB-DGeSC3DT.js} +1 -1
- package/dist/web-shell/assets/{requirementDiagram-4Y6WPE33-B4MEhI9h.js → requirementDiagram-4Y6WPE33-DkspY6Re.js} +1 -1
- package/dist/web-shell/assets/{sankeyDiagram-5OEKKPKP-Dk_FgKLp.js → sankeyDiagram-5OEKKPKP-CaOofS2x.js} +1 -1
- package/dist/web-shell/assets/{sequenceDiagram-3UESZ5HK-B4ksiBZQ.js → sequenceDiagram-3UESZ5HK-DFQ_kFnN.js} +1 -1
- package/dist/web-shell/assets/{stateDiagram-AJRCARHV-CYf0xSPl.js → stateDiagram-AJRCARHV-D3snw0Tz.js} +1 -1
- package/dist/web-shell/assets/stateDiagram-v2-BHNVJYJU-BbSm_9d0.js +1 -0
- package/dist/web-shell/assets/{timeline-definition-PNZ67QCA-0CXeWHNY.js → timeline-definition-PNZ67QCA-l4zH8nLM.js} +1 -1
- package/dist/web-shell/assets/{vennDiagram-CIIHVFJN-DHoYY9v2.js → vennDiagram-CIIHVFJN-BnRlA98M.js} +1 -1
- package/dist/web-shell/assets/{wardley-L42UT6IY-D_65qshr.js → wardley-L42UT6IY-BS0xUdmd.js} +1 -1
- package/dist/web-shell/assets/{wardleyDiagram-YWT4CUSO-Cv90x1sd.js → wardleyDiagram-YWT4CUSO-DKGBUWyQ.js} +1 -1
- package/dist/web-shell/assets/{xychartDiagram-2RQKCTM6-5FQCYJyh.js → xychartDiagram-2RQKCTM6-DVb2HuOB.js} +1 -1
- package/dist/web-shell/index.html +2 -2
- package/package.json +3 -2
- package/dist/web-shell/assets/channel-V-ZaJrA6.js +0 -1
- package/dist/web-shell/assets/classDiagram-4FO5ZUOK-s-H0snGT.js +0 -1
- package/dist/web-shell/assets/classDiagram-v2-Q7XG4LA2-s-H0snGT.js +0 -1
- package/dist/web-shell/assets/index-DW75jq0K.css +0 -5
- package/dist/web-shell/assets/index-tiCo0xbZ.js +0 -779
- package/dist/web-shell/assets/stateDiagram-v2-BHNVJYJU-CMtrFRUb.js +0 -1
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
openQwenAsrRealtimeStream,
|
|
12
12
|
openVoiceStream,
|
|
13
13
|
openVoiceStreamWithRetry
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HPQIVXSP.js";
|
|
15
15
|
import {
|
|
16
16
|
SetupGithubError,
|
|
17
17
|
collectSessionData,
|
|
@@ -22,61 +22,10 @@ import {
|
|
|
22
22
|
toJson,
|
|
23
23
|
toJsonl,
|
|
24
24
|
toMarkdown
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-FHTUTVIQ.js";
|
|
26
|
+
import "./chunk-2MDC5WDB.js";
|
|
27
|
+
import "./chunk-LGFAYIKX.js";
|
|
26
28
|
import "./chunk-54YZOP7P.js";
|
|
27
|
-
import "./chunk-AKSRZ5BA.js";
|
|
28
|
-
import {
|
|
29
|
-
createDaemonStatusProvider
|
|
30
|
-
} from "./chunk-UMU5USBC.js";
|
|
31
|
-
import "./chunk-GKUVMLJH.js";
|
|
32
|
-
import "./chunk-KHDZHZMH.js";
|
|
33
|
-
import {
|
|
34
|
-
createWorkspaceProvidersStatusProvider
|
|
35
|
-
} from "./chunk-AXYGF5SX.js";
|
|
36
|
-
import {
|
|
37
|
-
getAuthTypeFromEnv,
|
|
38
|
-
resolveCliGenerationConfig
|
|
39
|
-
} from "./chunk-VEDR5V7H.js";
|
|
40
|
-
import {
|
|
41
|
-
createWorkspaceSkillsStatusProvider
|
|
42
|
-
} from "./chunk-3Y2SRVRF.js";
|
|
43
|
-
import "./chunk-6IAWPIQ6.js";
|
|
44
|
-
import {
|
|
45
|
-
EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
|
|
46
|
-
WorkspaceVoiceError,
|
|
47
|
-
buildWorkspaceVoiceSettingsWrites,
|
|
48
|
-
buildWorkspaceVoiceStatus,
|
|
49
|
-
createDaemonWorkspaceService,
|
|
50
|
-
hasConfiguredBatchVoiceTranscriptionModel,
|
|
51
|
-
transcribeWorkspaceVoiceAudio,
|
|
52
|
-
validateWorkspaceVoiceState,
|
|
53
|
-
voiceSettingsScopeToWire
|
|
54
|
-
} from "./chunk-A6XKBVOM.js";
|
|
55
|
-
import {
|
|
56
|
-
PermissionRulesValidationError,
|
|
57
|
-
buildPermissionSettings,
|
|
58
|
-
isPermissionRuleType,
|
|
59
|
-
normalizePermissionRules,
|
|
60
|
-
readPermissionRuleSet
|
|
61
|
-
} from "./chunk-46IFEQDM.js";
|
|
62
|
-
import {
|
|
63
|
-
assertVoiceBaseUrlNetworkAllowed,
|
|
64
|
-
isStreamingVoiceModel,
|
|
65
|
-
resolveVoiceStreamConfig,
|
|
66
|
-
resolveVoiceTranscriptionConfig,
|
|
67
|
-
sanitizeVoiceErrorMessage,
|
|
68
|
-
transcribeVoiceAudio
|
|
69
|
-
} from "./chunk-PY6PG4BL.js";
|
|
70
|
-
import {
|
|
71
|
-
isVoiceEnabled,
|
|
72
|
-
isVoiceMode,
|
|
73
|
-
readVoiceModel
|
|
74
|
-
} from "./chunk-SWHGVEH5.js";
|
|
75
|
-
import "./chunk-E7KNELIZ.js";
|
|
76
|
-
import {
|
|
77
|
-
WorkspacePermissionRulesSessionRequiredError,
|
|
78
|
-
WorkspaceSettingsPartialPersistError
|
|
79
|
-
} from "./chunk-BMFMJINR.js";
|
|
80
29
|
import {
|
|
81
30
|
CAPABILITIES_SCHEMA_VERSION,
|
|
82
31
|
CLIENT_ID_HEADER,
|
|
@@ -122,28 +71,84 @@ import {
|
|
|
122
71
|
setRateLimiter,
|
|
123
72
|
unsafeRevealSecret,
|
|
124
73
|
validateMcpRuntimeServerName
|
|
125
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-YKZIAK7C.js";
|
|
126
75
|
import {
|
|
127
76
|
isLoopbackBind
|
|
128
77
|
} from "./chunk-RNBYOUGV.js";
|
|
129
78
|
import "./chunk-3HX5LZ6R.js";
|
|
130
79
|
import "./chunk-7RYW5LQV.js";
|
|
131
80
|
import {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
validateSettingValue
|
|
137
|
-
} from "./chunk-7SZJYZPR.js";
|
|
81
|
+
createDaemonStatusProvider
|
|
82
|
+
} from "./chunk-PCWYMX57.js";
|
|
83
|
+
import "./chunk-2PRSBVPI.js";
|
|
84
|
+
import "./chunk-KHDZHZMH.js";
|
|
138
85
|
import {
|
|
139
|
-
|
|
140
|
-
} from "./chunk-
|
|
141
|
-
import
|
|
142
|
-
|
|
86
|
+
createWorkspaceProvidersStatusProvider
|
|
87
|
+
} from "./chunk-BYUA6YYS.js";
|
|
88
|
+
import {
|
|
89
|
+
getAuthTypeFromEnv,
|
|
90
|
+
resolveCliGenerationConfig
|
|
91
|
+
} from "./chunk-XWDHZP7P.js";
|
|
92
|
+
import {
|
|
93
|
+
snapshotProcessEnv
|
|
94
|
+
} from "./chunk-FII3C4E7.js";
|
|
95
|
+
import {
|
|
96
|
+
createWorkspaceSkillsStatusProvider
|
|
97
|
+
} from "./chunk-YJWMOGGZ.js";
|
|
98
|
+
import "./chunk-6IAWPIQ6.js";
|
|
99
|
+
import {
|
|
100
|
+
EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
|
|
101
|
+
WorkspaceVoiceError,
|
|
102
|
+
buildWorkspaceVoiceSettingsWrites,
|
|
103
|
+
buildWorkspaceVoiceStatus,
|
|
104
|
+
createDaemonWorkspaceService,
|
|
105
|
+
hasConfiguredBatchVoiceTranscriptionModel,
|
|
106
|
+
transcribeWorkspaceVoiceAudio,
|
|
107
|
+
validateWorkspaceVoiceState,
|
|
108
|
+
voiceSettingsScopeToWire
|
|
109
|
+
} from "./chunk-74E6VVF6.js";
|
|
110
|
+
import {
|
|
111
|
+
PermissionRulesValidationError,
|
|
112
|
+
buildPermissionSettings,
|
|
113
|
+
isPermissionRuleType,
|
|
114
|
+
normalizePermissionRules,
|
|
115
|
+
readPermissionRuleSet
|
|
116
|
+
} from "./chunk-3F22JT4E.js";
|
|
117
|
+
import {
|
|
118
|
+
assertVoiceBaseUrlNetworkAllowed,
|
|
119
|
+
isStreamingVoiceModel,
|
|
120
|
+
resolveVoiceStreamConfig,
|
|
121
|
+
resolveVoiceTranscriptionConfig,
|
|
122
|
+
sanitizeVoiceErrorMessage,
|
|
123
|
+
transcribeVoiceAudio
|
|
124
|
+
} from "./chunk-EZTNYRHH.js";
|
|
125
|
+
import {
|
|
126
|
+
isVoiceEnabled,
|
|
127
|
+
isVoiceMode,
|
|
128
|
+
readVoiceModel
|
|
129
|
+
} from "./chunk-SUCRVT5L.js";
|
|
130
|
+
import "./chunk-E7KNELIZ.js";
|
|
131
|
+
import {
|
|
132
|
+
WorkspacePermissionRulesSessionRequiredError,
|
|
133
|
+
WorkspaceSettingsPartialPersistError
|
|
134
|
+
} from "./chunk-BMFMJINR.js";
|
|
135
|
+
import {
|
|
136
|
+
createTotalSessionAdmissionController
|
|
137
|
+
} from "./chunk-ZL5HN75Q.js";
|
|
138
|
+
import {
|
|
139
|
+
createSingleWorkspaceRegistry
|
|
140
|
+
} from "./chunk-XNXVCNAT.js";
|
|
141
|
+
import "./chunk-V35MD7KN.js";
|
|
143
142
|
import {
|
|
144
143
|
SessionArtifactValidationError,
|
|
145
144
|
createAcpSessionBridge
|
|
146
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-FAATDXYS.js";
|
|
146
|
+
import "./chunk-AGHU5VRK.js";
|
|
147
|
+
import {
|
|
148
|
+
EVENT_SCHEMA_VERSION,
|
|
149
|
+
SubscriberLimitExceededError
|
|
150
|
+
} from "./chunk-3UP777ZS.js";
|
|
151
|
+
import "./chunk-GLE2YUPU.js";
|
|
147
152
|
import "./chunk-P7M4GA57.js";
|
|
148
153
|
import {
|
|
149
154
|
BranchWhilePromptActiveError,
|
|
@@ -168,35 +173,49 @@ import {
|
|
|
168
173
|
SessionNotFoundError,
|
|
169
174
|
SessionShellClientRequiredError,
|
|
170
175
|
SessionShellDisabledError,
|
|
176
|
+
TotalSessionLimitExceededError,
|
|
171
177
|
WorkspaceInitConflictError,
|
|
172
178
|
WorkspaceInitPathEscapeError,
|
|
173
179
|
WorkspaceInitRaceError,
|
|
174
180
|
WorkspaceInitSymlinkError,
|
|
175
181
|
WorkspaceMismatchError
|
|
176
|
-
} from "./chunk-
|
|
177
|
-
import "./chunk-AGHU5VRK.js";
|
|
182
|
+
} from "./chunk-GGQQA4JW.js";
|
|
178
183
|
import {
|
|
179
184
|
MAX_WORKSPACE_PATH_LENGTH,
|
|
180
185
|
canonicalizeWorkspace,
|
|
181
186
|
canonicalizeWorkspaces
|
|
182
187
|
} from "./chunk-ER3BKOLB.js";
|
|
183
188
|
import {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
} from "./chunk-G7ZG7CA6.js";
|
|
189
|
+
QWEN_SERVER_TOKEN_ENV
|
|
190
|
+
} from "./chunk-S6RLAIUR.js";
|
|
187
191
|
import "./chunk-VRDMOSQS.js";
|
|
188
|
-
import
|
|
192
|
+
import {
|
|
193
|
+
getDialogSettingKeys,
|
|
194
|
+
getNestedProperty,
|
|
195
|
+
getSettingDefinition,
|
|
196
|
+
loadSettings,
|
|
197
|
+
validateSettingValue
|
|
198
|
+
} from "./chunk-WJAW2O5H.js";
|
|
199
|
+
import {
|
|
200
|
+
SUPPORTED_LANGUAGES
|
|
201
|
+
} from "./chunk-MA4AZNP7.js";
|
|
202
|
+
import "./chunk-TLRYABYP.js";
|
|
203
|
+
import "./chunk-XQPMKWSV.js";
|
|
204
|
+
import "./chunk-O2YT7VB4.js";
|
|
205
|
+
import "./chunk-Z3I3QJK3.js";
|
|
206
|
+
import "./chunk-EJP6Z7NU.js";
|
|
189
207
|
import "./chunk-EXPMGZZV.js";
|
|
190
208
|
import {
|
|
191
209
|
STATUS_SCHEMA_VERSION,
|
|
192
210
|
createIdleWorkspaceMemoryStatus,
|
|
193
211
|
mapDomainErrorToErrorKind
|
|
194
|
-
} from "./chunk-
|
|
212
|
+
} from "./chunk-Q3PQ5VGJ.js";
|
|
195
213
|
import "./chunk-HLKG6CZN.js";
|
|
196
|
-
import "./chunk-
|
|
214
|
+
import "./chunk-V2TYBEWU.js";
|
|
215
|
+
import "./chunk-43W4YSO6.js";
|
|
197
216
|
import {
|
|
198
217
|
getWorkspaceTrustStatus
|
|
199
|
-
} from "./chunk-
|
|
218
|
+
} from "./chunk-VR3KCBHH.js";
|
|
200
219
|
import {
|
|
201
220
|
writeStderrLine
|
|
202
221
|
} from "./chunk-Y6Z2O3WR.js";
|
|
@@ -230,18 +249,20 @@ import {
|
|
|
230
249
|
encodeTextFileContent,
|
|
231
250
|
glob,
|
|
232
251
|
loadIgnoreRules,
|
|
233
|
-
|
|
252
|
+
loadUsageHistoryWithLive,
|
|
234
253
|
parseInstallSource,
|
|
235
254
|
redactUrlCredentials,
|
|
236
255
|
shouldShowStep,
|
|
256
|
+
stripTerminalControlSequences,
|
|
237
257
|
writeWorkspaceContextFile
|
|
238
|
-
} from "./chunk-
|
|
258
|
+
} from "./chunk-5MRUKUOG.js";
|
|
239
259
|
import "./chunk-5P5XGNYH.js";
|
|
240
260
|
import "./chunk-SKAZWEV5.js";
|
|
241
261
|
import "./chunk-K5PGHDBN.js";
|
|
242
262
|
import {
|
|
243
|
-
MAX_JOBS
|
|
244
|
-
|
|
263
|
+
MAX_JOBS,
|
|
264
|
+
nextDurableFireMs
|
|
265
|
+
} from "./chunk-3B26UVCX.js";
|
|
245
266
|
import "./chunk-W7EEOR7D.js";
|
|
246
267
|
import "./chunk-42XGFRJS.js";
|
|
247
268
|
import "./chunk-X2XNQQJI.js";
|
|
@@ -252,11 +273,12 @@ import {
|
|
|
252
273
|
} from "./chunk-2MPVVENX.js";
|
|
253
274
|
import "./chunk-ZTQ26VBE.js";
|
|
254
275
|
import {
|
|
276
|
+
appendCronRun,
|
|
255
277
|
generateCronTaskId,
|
|
278
|
+
getCronFilePath,
|
|
256
279
|
readCronTasks,
|
|
257
|
-
removeCronTasks,
|
|
258
280
|
updateCronTasks
|
|
259
|
-
} from "./chunk-
|
|
281
|
+
} from "./chunk-FV7425LN.js";
|
|
260
282
|
import "./chunk-MLZQVCF3.js";
|
|
261
283
|
import "./chunk-LQ7TMOCE.js";
|
|
262
284
|
import "./chunk-E5A7LHNN.js";
|
|
@@ -264,7 +286,7 @@ import {
|
|
|
264
286
|
APPROVAL_MODES,
|
|
265
287
|
SubagentError,
|
|
266
288
|
SubagentErrorCode
|
|
267
|
-
} from "./chunk-
|
|
289
|
+
} from "./chunk-5MBO76SL.js";
|
|
268
290
|
import "./chunk-GO6LNQXT.js";
|
|
269
291
|
import "./chunk-NQKLOAVJ.js";
|
|
270
292
|
import "./chunk-WMPVYQ4P.js";
|
|
@@ -283,12 +305,12 @@ import {
|
|
|
283
305
|
recordDaemonHttpRequest,
|
|
284
306
|
recordDaemonHttpResponse,
|
|
285
307
|
withDaemonRequestSpan
|
|
286
|
-
} from "./chunk-
|
|
308
|
+
} from "./chunk-27AYMAL6.js";
|
|
287
309
|
import "./chunk-E6US47KI.js";
|
|
288
310
|
import "./chunk-UWCTAVOD.js";
|
|
289
311
|
import "./chunk-OFEVLU4C.js";
|
|
290
312
|
import "./chunk-OMX7CUOE.js";
|
|
291
|
-
import "./chunk-
|
|
313
|
+
import "./chunk-OACJLMLD.js";
|
|
292
314
|
import "./chunk-SUAKHHK7.js";
|
|
293
315
|
import "./chunk-FEJ2FZ3U.js";
|
|
294
316
|
import "./chunk-X3YGAX7V.js";
|
|
@@ -305,8 +327,8 @@ import "./chunk-AHNJGLFV.js";
|
|
|
305
327
|
import "./chunk-22IFUCVR.js";
|
|
306
328
|
import "./chunk-7LTB54MK.js";
|
|
307
329
|
import "./chunk-SIUQ3YYX.js";
|
|
308
|
-
import "./chunk-
|
|
309
|
-
import "./chunk-
|
|
330
|
+
import "./chunk-UKV3D7WB.js";
|
|
331
|
+
import "./chunk-CCGOFQV3.js";
|
|
310
332
|
import "./chunk-IRJWHZWU.js";
|
|
311
333
|
import "./chunk-SYCJMSIJ.js";
|
|
312
334
|
import "./chunk-55ZMG67I.js";
|
|
@@ -345,9 +367,6 @@ import { createHash as createHash3, timingSafeEqual } from "node:crypto";
|
|
|
345
367
|
init_esbuild_shims();
|
|
346
368
|
import path8 from "node:path";
|
|
347
369
|
|
|
348
|
-
// packages/cli/src/serve/acp-session-bridge.ts
|
|
349
|
-
init_esbuild_shims();
|
|
350
|
-
|
|
351
370
|
// packages/cli/src/serve/fs/errors.ts
|
|
352
371
|
init_esbuild_shims();
|
|
353
372
|
var DEFAULT_STATUS_BY_KIND = {
|
|
@@ -3247,7 +3266,8 @@ function cloneTask(task) {
|
|
|
3247
3266
|
removedEntries: task.result.removedEntries.map((entry) => ({
|
|
3248
3267
|
...entry
|
|
3249
3268
|
})),
|
|
3250
|
-
touchedTopics: [...task.result.touchedTopics]
|
|
3269
|
+
touchedTopics: [...task.result.touchedTopics],
|
|
3270
|
+
touchedScopes: [...task.result.touchedScopes]
|
|
3251
3271
|
} : void 0
|
|
3252
3272
|
};
|
|
3253
3273
|
}
|
|
@@ -3452,7 +3472,8 @@ var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
|
|
|
3452
3472
|
task.result = {
|
|
3453
3473
|
summary: result.summary ?? formatWorkspaceMemoryForgetSummary(result.removedEntries.length),
|
|
3454
3474
|
removedEntries: result.removedEntries,
|
|
3455
|
-
touchedTopics: result.touchedTopics
|
|
3475
|
+
touchedTopics: result.touchedTopics,
|
|
3476
|
+
touchedScopes: result.touchedScopes
|
|
3456
3477
|
};
|
|
3457
3478
|
task.updatedAt = nowIso();
|
|
3458
3479
|
} catch (err) {
|
|
@@ -3474,7 +3495,7 @@ var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
|
|
|
3474
3495
|
this.publishManagedMemoryChanged({
|
|
3475
3496
|
source: "workspace_memory_forget",
|
|
3476
3497
|
taskId: task.taskId,
|
|
3477
|
-
touchedScopes:
|
|
3498
|
+
touchedScopes: task.result.touchedScopes,
|
|
3478
3499
|
...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
|
|
3479
3500
|
});
|
|
3480
3501
|
}
|
|
@@ -4893,6 +4914,67 @@ __name(createSessionOrganizationService, "createSessionOrganizationService");
|
|
|
4893
4914
|
|
|
4894
4915
|
// packages/cli/src/serve/server/session-archive.ts
|
|
4895
4916
|
init_esbuild_shims();
|
|
4917
|
+
|
|
4918
|
+
// packages/cli/src/serve/scheduled-task-session-lifecycle.ts
|
|
4919
|
+
init_esbuild_shims();
|
|
4920
|
+
function isBoundTask(task) {
|
|
4921
|
+
return typeof task.sessionId === "string" && task.sessionId.length > 0;
|
|
4922
|
+
}
|
|
4923
|
+
__name(isBoundTask, "isBoundTask");
|
|
4924
|
+
async function disableTasksForSessions(projectRoot, sessionIds) {
|
|
4925
|
+
if (sessionIds.length === 0) return;
|
|
4926
|
+
const targets = new Set(sessionIds);
|
|
4927
|
+
await updateCronTasks(projectRoot, (tasks) => {
|
|
4928
|
+
let changed = false;
|
|
4929
|
+
const next = tasks.map((task) => {
|
|
4930
|
+
if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled !== false) {
|
|
4931
|
+
changed = true;
|
|
4932
|
+
return { ...task, enabled: false, disabledByArchive: true };
|
|
4933
|
+
}
|
|
4934
|
+
return task;
|
|
4935
|
+
});
|
|
4936
|
+
return changed ? next : tasks;
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
__name(disableTasksForSessions, "disableTasksForSessions");
|
|
4940
|
+
async function enableTasksForSessions(projectRoot, sessionIds, now = Date.now()) {
|
|
4941
|
+
if (sessionIds.length === 0) return;
|
|
4942
|
+
const targets = new Set(sessionIds);
|
|
4943
|
+
await updateCronTasks(projectRoot, (tasks) => {
|
|
4944
|
+
let changed = false;
|
|
4945
|
+
const next = tasks.map((task) => {
|
|
4946
|
+
if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled === false && task.disabledByArchive === true) {
|
|
4947
|
+
changed = true;
|
|
4948
|
+
const resumed = { ...task, enabled: true };
|
|
4949
|
+
delete resumed.disabledByArchive;
|
|
4950
|
+
const minute = now - now % 6e4;
|
|
4951
|
+
if (resumed.recurring) {
|
|
4952
|
+
resumed.lastFiredAt = minute;
|
|
4953
|
+
} else {
|
|
4954
|
+
resumed.createdAt = now;
|
|
4955
|
+
resumed.lastFiredAt = minute;
|
|
4956
|
+
}
|
|
4957
|
+
return resumed;
|
|
4958
|
+
}
|
|
4959
|
+
return task;
|
|
4960
|
+
});
|
|
4961
|
+
return changed ? next : tasks;
|
|
4962
|
+
});
|
|
4963
|
+
}
|
|
4964
|
+
__name(enableTasksForSessions, "enableTasksForSessions");
|
|
4965
|
+
async function removeTasksForSessions(projectRoot, sessionIds) {
|
|
4966
|
+
if (sessionIds.length === 0) return;
|
|
4967
|
+
const targets = new Set(sessionIds);
|
|
4968
|
+
await updateCronTasks(projectRoot, (tasks) => {
|
|
4969
|
+
const next = tasks.filter(
|
|
4970
|
+
(task) => !isBoundTask(task) || !targets.has(task.sessionId)
|
|
4971
|
+
);
|
|
4972
|
+
return next.length === tasks.length ? tasks : next;
|
|
4973
|
+
});
|
|
4974
|
+
}
|
|
4975
|
+
__name(removeTasksForSessions, "removeTasksForSessions");
|
|
4976
|
+
|
|
4977
|
+
// packages/cli/src/serve/server/session-archive.ts
|
|
4896
4978
|
var SessionArchiveCoordinator = class {
|
|
4897
4979
|
static {
|
|
4898
4980
|
__name(this, "SessionArchiveCoordinator");
|
|
@@ -4995,6 +5077,13 @@ async function deleteDaemonSessions(params) {
|
|
|
4995
5077
|
}
|
|
4996
5078
|
})
|
|
4997
5079
|
);
|
|
5080
|
+
await removeTasksForSessions(service.getProjectRoot(), removed).catch(
|
|
5081
|
+
(err) => {
|
|
5082
|
+
logSessionArchiveWarning(
|
|
5083
|
+
`removeTasksForSessions failed for [${removed.join(", ")}]: ${err instanceof Error ? err.message : String(err)}`
|
|
5084
|
+
);
|
|
5085
|
+
}
|
|
5086
|
+
);
|
|
4998
5087
|
return { removed, notFound, errors: [...closeErrors, ...removeErrors] };
|
|
4999
5088
|
}
|
|
5000
5089
|
__name(deleteDaemonSessions, "deleteDaemonSessions");
|
|
@@ -5151,6 +5240,13 @@ async function archiveDaemonSessions(params) {
|
|
|
5151
5240
|
notFound,
|
|
5152
5241
|
errors
|
|
5153
5242
|
});
|
|
5243
|
+
await disableTasksForSessions(service.getProjectRoot(), archived).catch(
|
|
5244
|
+
(err) => {
|
|
5245
|
+
logSessionArchiveWarning(
|
|
5246
|
+
`disableTasksForSessions failed for [${archived.join(", ")}]: ${err instanceof Error ? err.message : String(err)} \u2014 bound tasks may keep firing until reconciled`
|
|
5247
|
+
);
|
|
5248
|
+
}
|
|
5249
|
+
);
|
|
5154
5250
|
return { archived, alreadyArchived, notFound, errors };
|
|
5155
5251
|
}
|
|
5156
5252
|
__name(archiveDaemonSessions, "archiveDaemonSessions");
|
|
@@ -5197,6 +5293,17 @@ async function unarchiveDaemonSessions(params) {
|
|
|
5197
5293
|
notFound,
|
|
5198
5294
|
errors
|
|
5199
5295
|
});
|
|
5296
|
+
const resumeSessionIds = [.../* @__PURE__ */ new Set([...unarchived, ...alreadyActive])];
|
|
5297
|
+
try {
|
|
5298
|
+
await enableTasksForSessions(service.getProjectRoot(), resumeSessionIds);
|
|
5299
|
+
} catch (err) {
|
|
5300
|
+
logSessionArchiveWarning(
|
|
5301
|
+
`enableTasksForSessions failed for [${resumeSessionIds.join(", ")}]: ${err instanceof Error ? err.message : String(err)}`
|
|
5302
|
+
);
|
|
5303
|
+
for (const sessionId of resumeSessionIds) {
|
|
5304
|
+
errors.push({ sessionId, error: err });
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5200
5307
|
return { unarchived, alreadyActive, notFound, errors };
|
|
5201
5308
|
}
|
|
5202
5309
|
__name(unarchiveDaemonSessions, "unarchiveDaemonSessions");
|
|
@@ -5629,7 +5736,27 @@ function toRpcError(err) {
|
|
|
5629
5736
|
case "InvalidClientIdError":
|
|
5630
5737
|
return { code: RPC.INVALID_PARAMS, message: errMsg(err) };
|
|
5631
5738
|
case "SessionLimitExceededError":
|
|
5632
|
-
return {
|
|
5739
|
+
return {
|
|
5740
|
+
code: RPC.INTERNAL_ERROR,
|
|
5741
|
+
message: errMsg(err),
|
|
5742
|
+
data: {
|
|
5743
|
+
errorKind: "session_limit_exceeded",
|
|
5744
|
+
limit: err.limit,
|
|
5745
|
+
scope: "workspace",
|
|
5746
|
+
retryable: true
|
|
5747
|
+
}
|
|
5748
|
+
};
|
|
5749
|
+
case "TotalSessionLimitExceededError":
|
|
5750
|
+
return {
|
|
5751
|
+
code: RPC.INTERNAL_ERROR,
|
|
5752
|
+
message: errMsg(err),
|
|
5753
|
+
data: {
|
|
5754
|
+
errorKind: "session_limit_exceeded",
|
|
5755
|
+
limit: err.limit,
|
|
5756
|
+
scope: err.scope,
|
|
5757
|
+
retryable: true
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5633
5760
|
case "PromptQueueFullError": {
|
|
5634
5761
|
const promptErr = err;
|
|
5635
5762
|
return {
|
|
@@ -9798,10 +9925,10 @@ function isCdpInboundFrame(frame) {
|
|
|
9798
9925
|
}
|
|
9799
9926
|
__name(isCdpInboundFrame, "isCdpInboundFrame");
|
|
9800
9927
|
var CdpReverseLink = class {
|
|
9801
|
-
constructor(sendToExtension, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS,
|
|
9928
|
+
constructor(sendToExtension, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, log2) {
|
|
9802
9929
|
this.sendToExtension = sendToExtension;
|
|
9803
9930
|
this.commandTimeoutMs = commandTimeoutMs;
|
|
9804
|
-
this.log =
|
|
9931
|
+
this.log = log2;
|
|
9805
9932
|
}
|
|
9806
9933
|
static {
|
|
9807
9934
|
__name(this, "CdpReverseLink");
|
|
@@ -10243,10 +10370,10 @@ var CdpBrowserEmulator = class {
|
|
|
10243
10370
|
var CLOSE_NO_BRIDGE = 1011;
|
|
10244
10371
|
var CLOSE_NORMAL = 1e3;
|
|
10245
10372
|
var CDP_WS_HEARTBEAT_MS = 15e3;
|
|
10246
|
-
function attachCdpClient(ws, registry,
|
|
10373
|
+
function attachCdpClient(ws, registry, log2) {
|
|
10247
10374
|
const bridge = registry.getActive();
|
|
10248
10375
|
if (!bridge) {
|
|
10249
|
-
|
|
10376
|
+
log2("qwen serve: /cdp rejected \u2014 no extension bridge connected");
|
|
10250
10377
|
try {
|
|
10251
10378
|
ws.close(
|
|
10252
10379
|
CLOSE_NO_BRIDGE,
|
|
@@ -10257,7 +10384,7 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10257
10384
|
return;
|
|
10258
10385
|
}
|
|
10259
10386
|
if (bridge.cdpBound) {
|
|
10260
|
-
|
|
10387
|
+
log2("qwen serve: /cdp rejected \u2014 a puppeteer client is already bound");
|
|
10261
10388
|
try {
|
|
10262
10389
|
ws.close(CLOSE_NO_BRIDGE, "A CDP client is already connected");
|
|
10263
10390
|
} catch {
|
|
@@ -10268,19 +10395,19 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10268
10395
|
const link = new CdpReverseLink(
|
|
10269
10396
|
(frame) => bridge.send(frame),
|
|
10270
10397
|
void 0,
|
|
10271
|
-
|
|
10398
|
+
log2
|
|
10272
10399
|
);
|
|
10273
10400
|
const emulator = new CdpBrowserEmulator({
|
|
10274
10401
|
reply: /* @__PURE__ */ __name((frame) => {
|
|
10275
10402
|
safeWsSend(ws, JSON.stringify(frame), "CDP");
|
|
10276
10403
|
}, "reply"),
|
|
10277
10404
|
forwardToTab: link.forwardToTab,
|
|
10278
|
-
log
|
|
10405
|
+
log: log2
|
|
10279
10406
|
});
|
|
10280
10407
|
link.bindEmulator(emulator);
|
|
10281
10408
|
bridge.routeInbound = (frame) => link.handleInbound(frame);
|
|
10282
10409
|
link.onDetach = (reason) => {
|
|
10283
|
-
|
|
10410
|
+
log2(`qwen serve: /cdp tab detached (${reason}); closing puppeteer socket`);
|
|
10284
10411
|
try {
|
|
10285
10412
|
ws.close(CLOSE_NORMAL, `tab detached: ${reason}`);
|
|
10286
10413
|
} catch {
|
|
@@ -10291,7 +10418,7 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10291
10418
|
const heartbeat = setInterval(() => {
|
|
10292
10419
|
if (disposed) return;
|
|
10293
10420
|
if (!heartbeatAlive) {
|
|
10294
|
-
|
|
10421
|
+
log2("qwen serve: /cdp heartbeat missed; closing puppeteer socket");
|
|
10295
10422
|
dispose("puppeteer /cdp heartbeat missed");
|
|
10296
10423
|
try {
|
|
10297
10424
|
ws.close(CLOSE_NORMAL, "cdp heartbeat missed");
|
|
@@ -10303,7 +10430,7 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10303
10430
|
try {
|
|
10304
10431
|
ws.ping();
|
|
10305
10432
|
} catch (err) {
|
|
10306
|
-
|
|
10433
|
+
log2(
|
|
10307
10434
|
`qwen serve: /cdp heartbeat ping failed: ${err instanceof Error ? err.message : String(err)}`
|
|
10308
10435
|
);
|
|
10309
10436
|
dispose("puppeteer /cdp heartbeat ping failed");
|
|
@@ -10321,11 +10448,11 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10321
10448
|
if (notifyExtension && registry.getActive() === bridge) {
|
|
10322
10449
|
try {
|
|
10323
10450
|
bridge.send({ type: CDP_FRAME_TYPES.release });
|
|
10324
|
-
|
|
10451
|
+
log2(
|
|
10325
10452
|
`qwen serve: /cdp sent release to extension (puppeteer disconnected: ${reason})`
|
|
10326
10453
|
);
|
|
10327
10454
|
} catch (err) {
|
|
10328
|
-
|
|
10455
|
+
log2(
|
|
10329
10456
|
`qwen serve: /cdp release send failed: ${err instanceof Error ? err.message : String(err)}`
|
|
10330
10457
|
);
|
|
10331
10458
|
}
|
|
@@ -10338,7 +10465,7 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10338
10465
|
}
|
|
10339
10466
|
}, "dispose");
|
|
10340
10467
|
link.onAttachFailure = (reason) => {
|
|
10341
|
-
|
|
10468
|
+
log2(`qwen serve: /cdp attach failed (${reason}); closing puppeteer socket`);
|
|
10342
10469
|
dispose(`cdp_attach failed: ${reason}`, false);
|
|
10343
10470
|
try {
|
|
10344
10471
|
ws.close(CLOSE_NO_BRIDGE, "cdp attach failed");
|
|
@@ -10349,7 +10476,7 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10349
10476
|
heartbeatAlive = true;
|
|
10350
10477
|
});
|
|
10351
10478
|
bridge.onExtensionGone = () => {
|
|
10352
|
-
|
|
10479
|
+
log2("qwen serve: extension /acp dropped; closing puppeteer /cdp socket");
|
|
10353
10480
|
dispose("extension /acp disconnected", false);
|
|
10354
10481
|
try {
|
|
10355
10482
|
ws.close(CLOSE_NORMAL, "extension disconnected");
|
|
@@ -10365,19 +10492,19 @@ function attachCdpClient(ws, registry, log) {
|
|
|
10365
10492
|
return;
|
|
10366
10493
|
}
|
|
10367
10494
|
void emulator.handleFromClient(frame).catch((err) => {
|
|
10368
|
-
|
|
10495
|
+
log2(
|
|
10369
10496
|
`qwen serve: /cdp emulator error: ${err instanceof Error ? err.message : String(err)}`
|
|
10370
10497
|
);
|
|
10371
10498
|
});
|
|
10372
10499
|
});
|
|
10373
10500
|
ws.on("close", () => dispose("puppeteer /cdp socket closed"));
|
|
10374
10501
|
ws.on("error", (err) => {
|
|
10375
|
-
|
|
10502
|
+
log2(
|
|
10376
10503
|
`qwen serve: /cdp WS error: ${err instanceof Error ? err.message : String(err)}`
|
|
10377
10504
|
);
|
|
10378
10505
|
dispose("puppeteer /cdp socket error");
|
|
10379
10506
|
});
|
|
10380
|
-
|
|
10507
|
+
log2("qwen serve: /cdp puppeteer client bound to extension bridge");
|
|
10381
10508
|
}
|
|
10382
10509
|
__name(attachCdpClient, "attachCdpClient");
|
|
10383
10510
|
|
|
@@ -10516,7 +10643,7 @@ var MAX_INFLIGHT_MCP_DISPATCH = 8;
|
|
|
10516
10643
|
function mountAcpHttp(app, bridge, opts) {
|
|
10517
10644
|
const enabled = opts.enabled ?? process.env["QWEN_SERVE_ACP_HTTP"] !== "0";
|
|
10518
10645
|
if (!enabled) return void 0;
|
|
10519
|
-
const
|
|
10646
|
+
const path14 = opts.path ?? "/acp";
|
|
10520
10647
|
const dispatcherRef = {};
|
|
10521
10648
|
const registry = new ConnectionRegistry(
|
|
10522
10649
|
(req, clientId) => {
|
|
@@ -10653,7 +10780,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
10653
10780
|
opts.archiveCoordinator ?? new SessionArchiveCoordinator()
|
|
10654
10781
|
);
|
|
10655
10782
|
dispatcherRef.current = dispatcher;
|
|
10656
|
-
app.post(
|
|
10783
|
+
app.post(path14, async (req, res) => {
|
|
10657
10784
|
const ct = req.headers["content-type"];
|
|
10658
10785
|
if (!ct || !ct.startsWith("application/json")) {
|
|
10659
10786
|
res.status(415).json({ error: "Content-Type must be application/json" });
|
|
@@ -10757,7 +10884,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
10757
10884
|
);
|
|
10758
10885
|
});
|
|
10759
10886
|
});
|
|
10760
|
-
app.get(
|
|
10887
|
+
app.get(path14, (req, res) => {
|
|
10761
10888
|
const accept = req.headers["accept"] ?? "";
|
|
10762
10889
|
if (!accept.includes("text/event-stream")) {
|
|
10763
10890
|
res.status(406).json({ error: "Accept header must include text/event-stream" });
|
|
@@ -10843,7 +10970,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
10843
10970
|
onPumpSettled();
|
|
10844
10971
|
});
|
|
10845
10972
|
});
|
|
10846
|
-
app.delete(
|
|
10973
|
+
app.delete(path14, (req, res) => {
|
|
10847
10974
|
const connectionId = headerOf(req, ACP_CONNECTION_HEADER);
|
|
10848
10975
|
if (!connectionId) {
|
|
10849
10976
|
res.status(400).json({ error: "Missing Acp-Connection-Id" });
|
|
@@ -10906,7 +11033,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
10906
11033
|
const extraRoute = opts.extraWsRoutes?.find(
|
|
10907
11034
|
(route) => route.path === url.pathname
|
|
10908
11035
|
);
|
|
10909
|
-
if (url.pathname !==
|
|
11036
|
+
if (url.pathname !== path14 && !isCdpPath && !extraRoute) {
|
|
10910
11037
|
logReject(`unknown-path ${url.pathname}`);
|
|
10911
11038
|
socket.destroy();
|
|
10912
11039
|
return;
|
|
@@ -11303,7 +11430,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
11303
11430
|
});
|
|
11304
11431
|
}, "upgradeListener");
|
|
11305
11432
|
httpServer.on("upgrade", upgradeListener);
|
|
11306
|
-
writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${
|
|
11433
|
+
writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${path14}`);
|
|
11307
11434
|
}
|
|
11308
11435
|
__name(setupWebSocket, "setupWebSocket");
|
|
11309
11436
|
return {
|
|
@@ -11356,14 +11483,14 @@ function readVoiceModel2(settings) {
|
|
|
11356
11483
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
11357
11484
|
}
|
|
11358
11485
|
__name(readVoiceModel2, "readVoiceModel");
|
|
11359
|
-
function buildModelsConfig(settings) {
|
|
11486
|
+
function buildModelsConfig(settings, env) {
|
|
11360
11487
|
const merged = settings.merged;
|
|
11361
|
-
const selectedAuthType = merged.security?.auth?.selectedType ?? getAuthTypeFromEnv();
|
|
11488
|
+
const selectedAuthType = merged.security?.auth?.selectedType ?? getAuthTypeFromEnv(env);
|
|
11362
11489
|
const resolvedCliConfig = resolveCliGenerationConfig({
|
|
11363
11490
|
argv: {},
|
|
11364
11491
|
settings: merged,
|
|
11365
11492
|
selectedAuthType,
|
|
11366
|
-
env
|
|
11493
|
+
env
|
|
11367
11494
|
});
|
|
11368
11495
|
return new ModelsConfig({
|
|
11369
11496
|
initialAuthType: selectedAuthType,
|
|
@@ -11373,17 +11500,29 @@ function buildModelsConfig(settings) {
|
|
|
11373
11500
|
});
|
|
11374
11501
|
}
|
|
11375
11502
|
__name(buildModelsConfig, "buildModelsConfig");
|
|
11376
|
-
function loadDaemonVoiceContext(workspaceCwd) {
|
|
11377
|
-
const settings = loadSettings(
|
|
11503
|
+
function loadDaemonVoiceContext(workspaceCwd, options = {}) {
|
|
11504
|
+
const settings = loadSettings(
|
|
11505
|
+
workspaceCwd,
|
|
11506
|
+
options.env ? { skipLoadEnvironment: true } : true
|
|
11507
|
+
);
|
|
11378
11508
|
const voiceModel = readVoiceModel2(settings);
|
|
11379
11509
|
if (!voiceModel) {
|
|
11380
11510
|
throw new Error("No voice model is configured for this workspace.");
|
|
11381
11511
|
}
|
|
11382
|
-
const models = buildModelsConfig(
|
|
11383
|
-
|
|
11512
|
+
const models = buildModelsConfig(
|
|
11513
|
+
settings,
|
|
11514
|
+
options.env ?? snapshotProcessEnv()
|
|
11515
|
+
);
|
|
11516
|
+
resolveVoiceTranscriptionConfig({
|
|
11517
|
+
config: models,
|
|
11518
|
+
settings,
|
|
11519
|
+
voiceModel,
|
|
11520
|
+
env: options.env
|
|
11521
|
+
});
|
|
11384
11522
|
return {
|
|
11385
11523
|
settings,
|
|
11386
11524
|
models,
|
|
11525
|
+
...options.env ? { env: options.env } : {},
|
|
11387
11526
|
voiceModel,
|
|
11388
11527
|
streaming: isStreamingVoiceModel(voiceModel)
|
|
11389
11528
|
};
|
|
@@ -11423,7 +11562,8 @@ async function defaultOpenStream(ctx, callbacks) {
|
|
|
11423
11562
|
const cfg = resolveVoiceStreamConfig({
|
|
11424
11563
|
config: ctx.models,
|
|
11425
11564
|
settings: ctx.settings,
|
|
11426
|
-
voiceModel: ctx.voiceModel
|
|
11565
|
+
voiceModel: ctx.voiceModel,
|
|
11566
|
+
env: ctx.env
|
|
11427
11567
|
});
|
|
11428
11568
|
await assertVoiceBaseUrlNetworkAllowed(cfg);
|
|
11429
11569
|
return await openVoiceStreamWithRetry(
|
|
@@ -11438,7 +11578,12 @@ __name(defaultOpenStream, "defaultOpenStream");
|
|
|
11438
11578
|
function defaultTranscribe(ctx, pcm) {
|
|
11439
11579
|
return transcribeVoiceAudio(
|
|
11440
11580
|
{ data: encodeWav(pcm), mimeType: "audio/wav" },
|
|
11441
|
-
{
|
|
11581
|
+
{
|
|
11582
|
+
config: ctx.models,
|
|
11583
|
+
settings: ctx.settings,
|
|
11584
|
+
voiceModel: ctx.voiceModel,
|
|
11585
|
+
env: ctx.env
|
|
11586
|
+
}
|
|
11442
11587
|
).catch((error2) => {
|
|
11443
11588
|
debugLogger3.debug(
|
|
11444
11589
|
`[voice-ws] batch transcription error: ${errMessage(error2)}`
|
|
@@ -11477,7 +11622,7 @@ function parseControl(text) {
|
|
|
11477
11622
|
}
|
|
11478
11623
|
__name(parseControl, "parseControl");
|
|
11479
11624
|
function createVoiceWsConnectionHandler(boundWorkspace, deps = {}) {
|
|
11480
|
-
const loadContext = deps.loadContext ?? loadDaemonVoiceContext;
|
|
11625
|
+
const loadContext = deps.loadContext ?? ((workspaceCwd) => loadDaemonVoiceContext(workspaceCwd, { env: deps.env }));
|
|
11481
11626
|
const openStream = deps.openStream ?? defaultOpenStream;
|
|
11482
11627
|
const transcribe = deps.transcribe ?? defaultTranscribe;
|
|
11483
11628
|
let activeSessions = 0;
|
|
@@ -11885,6 +12030,7 @@ function registerDaemonStatusRoutes(app, deps) {
|
|
|
11885
12030
|
opts: deps.opts,
|
|
11886
12031
|
boundWorkspace: deps.boundWorkspace,
|
|
11887
12032
|
bridge: deps.bridge,
|
|
12033
|
+
workspaceRegistry: deps.workspaceRegistry,
|
|
11888
12034
|
workspace: deps.workspace,
|
|
11889
12035
|
daemonLog: deps.daemonLog,
|
|
11890
12036
|
startup: deps.startup,
|
|
@@ -11899,7 +12045,8 @@ function registerDaemonStatusRoutes(app, deps) {
|
|
|
11899
12045
|
sessionShellCommandEnabled: deps.sessionShellCommandEnabled,
|
|
11900
12046
|
getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot,
|
|
11901
12047
|
getPerfSnapshot: deps.getPerfSnapshot,
|
|
11902
|
-
getMetricsSeries: deps.getMetricsSeries
|
|
12048
|
+
getMetricsSeries: deps.getMetricsSeries,
|
|
12049
|
+
getTotalSessionAdmissionSnapshot: deps.getTotalSessionAdmissionSnapshot
|
|
11903
12050
|
})
|
|
11904
12051
|
);
|
|
11905
12052
|
} catch (err) {
|
|
@@ -14185,8 +14332,88 @@ __name(registerWorkspaceTrustRoutes, "registerWorkspaceTrustRoutes");
|
|
|
14185
14332
|
|
|
14186
14333
|
// packages/cli/src/serve/routes/permission.ts
|
|
14187
14334
|
init_esbuild_shims();
|
|
14335
|
+
|
|
14336
|
+
// packages/cli/src/serve/routes/session-runtime.ts
|
|
14337
|
+
init_esbuild_shims();
|
|
14338
|
+
function requireSessionRuntime(opts) {
|
|
14339
|
+
const {
|
|
14340
|
+
sessionId,
|
|
14341
|
+
route,
|
|
14342
|
+
res,
|
|
14343
|
+
workspaceRegistry,
|
|
14344
|
+
daemonLog,
|
|
14345
|
+
details = {}
|
|
14346
|
+
} = opts;
|
|
14347
|
+
if (workspaceRegistry.list().length === 1) {
|
|
14348
|
+
return workspaceRegistry.primary;
|
|
14349
|
+
}
|
|
14350
|
+
const resolution = workspaceRegistry.resolveLiveSessionOwner(sessionId);
|
|
14351
|
+
if (resolution.kind === "found") {
|
|
14352
|
+
const runtime = resolution.runtime;
|
|
14353
|
+
if (!runtime.primary && !runtime.trusted) {
|
|
14354
|
+
daemonLog?.warn("session routing failed", {
|
|
14355
|
+
route,
|
|
14356
|
+
resolutionKind: "untrusted_workspace",
|
|
14357
|
+
sessionId,
|
|
14358
|
+
workspaceId: runtime.workspaceId,
|
|
14359
|
+
workspaceCwd: runtime.workspaceCwd,
|
|
14360
|
+
...details
|
|
14361
|
+
});
|
|
14362
|
+
res.status(403).json({
|
|
14363
|
+
error: `Workspace "${runtime.workspaceCwd}" is not trusted.`,
|
|
14364
|
+
code: "untrusted_workspace",
|
|
14365
|
+
sessionId,
|
|
14366
|
+
workspaceId: runtime.workspaceId,
|
|
14367
|
+
workspaceCwd: runtime.workspaceCwd
|
|
14368
|
+
});
|
|
14369
|
+
return void 0;
|
|
14370
|
+
}
|
|
14371
|
+
return runtime;
|
|
14372
|
+
}
|
|
14373
|
+
if (resolution.kind === "not_found") {
|
|
14374
|
+
daemonLog?.warn("session routing failed", {
|
|
14375
|
+
route,
|
|
14376
|
+
resolutionKind: "not_found",
|
|
14377
|
+
sessionId,
|
|
14378
|
+
...details
|
|
14379
|
+
});
|
|
14380
|
+
res.status(404).json({
|
|
14381
|
+
error: `No session with id "${sessionId}"`,
|
|
14382
|
+
code: "session_not_found",
|
|
14383
|
+
sessionId
|
|
14384
|
+
});
|
|
14385
|
+
return void 0;
|
|
14386
|
+
}
|
|
14387
|
+
const workspaceIds = resolution.runtimes.map(
|
|
14388
|
+
(runtime) => runtime.workspaceId
|
|
14389
|
+
);
|
|
14390
|
+
daemonLog?.warn("session routing failed", {
|
|
14391
|
+
route,
|
|
14392
|
+
resolutionKind: "ambiguous",
|
|
14393
|
+
sessionId,
|
|
14394
|
+
workspaceIds,
|
|
14395
|
+
...details
|
|
14396
|
+
});
|
|
14397
|
+
res.status(500).json({
|
|
14398
|
+
error: `Session owner is ambiguous for "${sessionId}"`,
|
|
14399
|
+
code: "ambiguous_session_owner",
|
|
14400
|
+
sessionId,
|
|
14401
|
+
route,
|
|
14402
|
+
workspaceIds
|
|
14403
|
+
});
|
|
14404
|
+
return void 0;
|
|
14405
|
+
}
|
|
14406
|
+
__name(requireSessionRuntime, "requireSessionRuntime");
|
|
14407
|
+
|
|
14408
|
+
// packages/cli/src/serve/routes/permission.ts
|
|
14188
14409
|
function registerPermissionRoutes(app, deps) {
|
|
14189
|
-
const {
|
|
14410
|
+
const {
|
|
14411
|
+
bridge,
|
|
14412
|
+
workspaceRegistry,
|
|
14413
|
+
daemonLog,
|
|
14414
|
+
mutate,
|
|
14415
|
+
sendPermissionVoteError: sendPermissionVoteError2
|
|
14416
|
+
} = deps;
|
|
14190
14417
|
app.post("/session/:id/permission/:requestId", mutate(), (req, res) => {
|
|
14191
14418
|
const sessionId = req.params["id"];
|
|
14192
14419
|
const requestId = req.params["requestId"];
|
|
@@ -14201,7 +14428,16 @@ function registerPermissionRoutes(app, deps) {
|
|
|
14201
14428
|
};
|
|
14202
14429
|
let accepted;
|
|
14203
14430
|
try {
|
|
14204
|
-
|
|
14431
|
+
const runtime = requireSessionRuntime({
|
|
14432
|
+
sessionId,
|
|
14433
|
+
route: "POST /session/:id/permission/:requestId",
|
|
14434
|
+
res,
|
|
14435
|
+
workspaceRegistry,
|
|
14436
|
+
daemonLog,
|
|
14437
|
+
details: { requestId }
|
|
14438
|
+
});
|
|
14439
|
+
if (!runtime) return;
|
|
14440
|
+
accepted = runtime.bridge.respondToSessionPermission(
|
|
14205
14441
|
sessionId,
|
|
14206
14442
|
requestId,
|
|
14207
14443
|
response,
|
|
@@ -14328,6 +14564,25 @@ function encodeOrganizedCursor(last, group, archiveState) {
|
|
|
14328
14564
|
).toString("base64url");
|
|
14329
14565
|
}
|
|
14330
14566
|
__name(encodeOrganizedCursor, "encodeOrganizedCursor");
|
|
14567
|
+
function parseLiveSessionCursor(cursor) {
|
|
14568
|
+
if (cursor === "") return void 0;
|
|
14569
|
+
try {
|
|
14570
|
+
const parsed = JSON.parse(
|
|
14571
|
+
Buffer.from(cursor, "base64url").toString("utf8")
|
|
14572
|
+
);
|
|
14573
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof parsed.activityTime !== "number" || !Number.isFinite(parsed.activityTime) || typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
|
|
14574
|
+
throw new Error("invalid live cursor");
|
|
14575
|
+
}
|
|
14576
|
+
return parsed;
|
|
14577
|
+
} catch {
|
|
14578
|
+
throw new InvalidCursorError(cursor, "live");
|
|
14579
|
+
}
|
|
14580
|
+
}
|
|
14581
|
+
__name(parseLiveSessionCursor, "parseLiveSessionCursor");
|
|
14582
|
+
function encodeLiveSessionCursor(last) {
|
|
14583
|
+
return Buffer.from(JSON.stringify(last), "utf8").toString("base64url");
|
|
14584
|
+
}
|
|
14585
|
+
__name(encodeLiveSessionCursor, "encodeLiveSessionCursor");
|
|
14331
14586
|
function toSummary2(item) {
|
|
14332
14587
|
return {
|
|
14333
14588
|
sessionId: item.sessionId,
|
|
@@ -14373,6 +14628,19 @@ function getSummaryActivityTime(session) {
|
|
|
14373
14628
|
return Number.isFinite(time) ? time : 0;
|
|
14374
14629
|
}
|
|
14375
14630
|
__name(getSummaryActivityTime, "getSummaryActivityTime");
|
|
14631
|
+
function getLiveSessionCursorKey(session) {
|
|
14632
|
+
return {
|
|
14633
|
+
activityTime: getSummaryActivityTime(session),
|
|
14634
|
+
sessionId: session.sessionId
|
|
14635
|
+
};
|
|
14636
|
+
}
|
|
14637
|
+
__name(getLiveSessionCursorKey, "getLiveSessionCursorKey");
|
|
14638
|
+
function compareLiveSessionCursorKeys(a, b) {
|
|
14639
|
+
const byTime = b.activityTime - a.activityTime;
|
|
14640
|
+
if (byTime !== 0) return byTime;
|
|
14641
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
14642
|
+
}
|
|
14643
|
+
__name(compareLiveSessionCursorKeys, "compareLiveSessionCursorKeys");
|
|
14376
14644
|
function compareOrganizedSessions(activityTimeById, a, b) {
|
|
14377
14645
|
return compareOrganizedCursorKeys(
|
|
14378
14646
|
getOrganizedCursorKey(activityTimeById, a),
|
|
@@ -14580,6 +14848,31 @@ async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
|
|
|
14580
14848
|
return { sessions, nextCursor };
|
|
14581
14849
|
}
|
|
14582
14850
|
__name(listWorkspaceSessionsForResponse, "listWorkspaceSessionsForResponse");
|
|
14851
|
+
function listLiveWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
|
|
14852
|
+
const rawSize = options?.size;
|
|
14853
|
+
const requestedSize = typeof rawSize === "number" && Number.isSafeInteger(rawSize) ? rawSize : DEFAULT_SESSION_PAGE_SIZE;
|
|
14854
|
+
const pageSize = Math.min(Math.max(requestedSize, 1), MAX_SESSION_PAGE_SIZE);
|
|
14855
|
+
const cursorKey = options?.cursor !== void 0 ? parseLiveSessionCursor(options.cursor) : void 0;
|
|
14856
|
+
const sessions = bridge.listWorkspaceSessions(workspaceCwd).sort(
|
|
14857
|
+
(a, b) => compareLiveSessionCursorKeys(
|
|
14858
|
+
getLiveSessionCursorKey(a),
|
|
14859
|
+
getLiveSessionCursorKey(b)
|
|
14860
|
+
)
|
|
14861
|
+
);
|
|
14862
|
+
const afterCursor = cursorKey === void 0 ? sessions : sessions.filter(
|
|
14863
|
+
(session) => compareLiveSessionCursorKeys(
|
|
14864
|
+
cursorKey,
|
|
14865
|
+
getLiveSessionCursorKey(session)
|
|
14866
|
+
) < 0
|
|
14867
|
+
);
|
|
14868
|
+
const page = afterCursor.slice(0, pageSize);
|
|
14869
|
+
const nextCursor = page.length < afterCursor.length ? encodeLiveSessionCursor(getLiveSessionCursorKey(page[page.length - 1])) : void 0;
|
|
14870
|
+
return {
|
|
14871
|
+
sessions: page,
|
|
14872
|
+
...nextCursor !== void 0 ? { nextCursor } : {}
|
|
14873
|
+
};
|
|
14874
|
+
}
|
|
14875
|
+
__name(listLiveWorkspaceSessionsForResponse, "listLiveWorkspaceSessionsForResponse");
|
|
14583
14876
|
function parseSessionPageSizeQuery(raw) {
|
|
14584
14877
|
if (typeof raw !== "string") return void 0;
|
|
14585
14878
|
const trimmed = raw.trim();
|
|
@@ -14683,6 +14976,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
14683
14976
|
const {
|
|
14684
14977
|
boundWorkspace,
|
|
14685
14978
|
bridge,
|
|
14979
|
+
workspaceRegistry,
|
|
14686
14980
|
archiveCoordinator,
|
|
14687
14981
|
mutate,
|
|
14688
14982
|
sendBridgeError: sendBridgeError2,
|
|
@@ -14691,23 +14985,175 @@ function registerSessionRoutes(app, deps) {
|
|
|
14691
14985
|
sessionShellCommandEnabled
|
|
14692
14986
|
} = deps;
|
|
14693
14987
|
const LANGUAGE_CODES = deps.languageCodes;
|
|
14694
|
-
const
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14988
|
+
const logSessionRoutingFailure = /* @__PURE__ */ __name((route, resolutionKind, details = {}) => {
|
|
14989
|
+
daemonLog?.warn("session routing failed", {
|
|
14990
|
+
route,
|
|
14991
|
+
resolutionKind,
|
|
14992
|
+
...details
|
|
14993
|
+
});
|
|
14994
|
+
}, "logSessionRoutingFailure");
|
|
14995
|
+
const sendWorkspaceMismatch = /* @__PURE__ */ __name((res, requestedWorkspace) => {
|
|
14996
|
+
const runtimes = workspaceRegistry.list();
|
|
14997
|
+
if (runtimes.length > 1) {
|
|
14698
14998
|
res.status(400).json({
|
|
14699
|
-
error:
|
|
14700
|
-
code: "
|
|
14999
|
+
error: `Workspace mismatch: daemon is bound to ${runtimes.length} workspaces; none matched the requested workspace.`,
|
|
15000
|
+
code: "workspace_mismatch",
|
|
15001
|
+
boundWorkspace,
|
|
15002
|
+
workspaceCount: runtimes.length,
|
|
15003
|
+
requestedWorkspace
|
|
14701
15004
|
});
|
|
14702
|
-
return
|
|
15005
|
+
return;
|
|
14703
15006
|
}
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
|
|
15007
|
+
res.status(400).json({
|
|
15008
|
+
error: `Workspace mismatch: daemon is bound to "${boundWorkspace}"`,
|
|
15009
|
+
code: "workspace_mismatch",
|
|
15010
|
+
boundWorkspace,
|
|
15011
|
+
requestedWorkspace
|
|
15012
|
+
});
|
|
15013
|
+
}, "sendWorkspaceMismatch");
|
|
15014
|
+
const resolveRuntimeForSessionCreation = /* @__PURE__ */ __name((body, res) => {
|
|
15015
|
+
const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
|
|
15016
|
+
if (cwd === void 0) return void 0;
|
|
15017
|
+
let key;
|
|
15018
|
+
try {
|
|
15019
|
+
key = canonicalizeWorkspace(cwd);
|
|
15020
|
+
} catch (err) {
|
|
15021
|
+
if (workspaceRegistry.list().length > 1 && "cwd" in body) {
|
|
15022
|
+
logSessionRoutingFailure("POST /session", "workspace_mismatch", {
|
|
15023
|
+
requestedWorkspace: cwd
|
|
15024
|
+
});
|
|
15025
|
+
sendWorkspaceMismatch(res, cwd);
|
|
15026
|
+
return void 0;
|
|
15027
|
+
}
|
|
15028
|
+
sendBridgeError2(res, err, { route: "POST /session" });
|
|
15029
|
+
return void 0;
|
|
15030
|
+
}
|
|
15031
|
+
if (workspaceRegistry.list().length === 1) {
|
|
15032
|
+
return {
|
|
15033
|
+
runtime: workspaceRegistry.primary,
|
|
15034
|
+
workspaceCwd: "cwd" in body ? key : workspaceRegistry.primary.workspaceCwd
|
|
15035
|
+
};
|
|
15036
|
+
}
|
|
15037
|
+
const runtime = workspaceRegistry.resolveWorkspaceCwd(
|
|
15038
|
+
"cwd" in body ? key : void 0
|
|
15039
|
+
);
|
|
15040
|
+
if (!runtime) {
|
|
15041
|
+
logSessionRoutingFailure("POST /session", "workspace_mismatch", {
|
|
15042
|
+
requestedWorkspace: key
|
|
15043
|
+
});
|
|
15044
|
+
sendWorkspaceMismatch(res, key);
|
|
15045
|
+
return void 0;
|
|
15046
|
+
}
|
|
15047
|
+
if (!runtime.primary && !runtime.trusted) {
|
|
15048
|
+
logSessionRoutingFailure("POST /session", "untrusted_workspace", {
|
|
15049
|
+
workspaceId: runtime.workspaceId,
|
|
15050
|
+
workspaceCwd: runtime.workspaceCwd
|
|
15051
|
+
});
|
|
15052
|
+
res.status(403).json({
|
|
15053
|
+
error: `Workspace "${runtime.workspaceCwd}" is not trusted.`,
|
|
15054
|
+
code: "untrusted_workspace",
|
|
15055
|
+
workspaceCwd: runtime.workspaceCwd
|
|
15056
|
+
});
|
|
15057
|
+
return void 0;
|
|
15058
|
+
}
|
|
15059
|
+
return { runtime, workspaceCwd: runtime.workspaceCwd };
|
|
15060
|
+
}, "resolveRuntimeForSessionCreation");
|
|
15061
|
+
const resolveRuntimeFromWorkspaceParam = /* @__PURE__ */ __name((req, res) => {
|
|
15062
|
+
const workspaceParam = req.params["id"] ?? "";
|
|
15063
|
+
const byId = workspaceRegistry.getByWorkspaceId(workspaceParam);
|
|
15064
|
+
if (byId) return byId;
|
|
15065
|
+
if (!path10.isAbsolute(workspaceParam)) {
|
|
15066
|
+
res.status(400).json({
|
|
15067
|
+
error: "`:id` must decode to a workspace id or absolute path"
|
|
15068
|
+
});
|
|
15069
|
+
return null;
|
|
15070
|
+
}
|
|
15071
|
+
let key;
|
|
15072
|
+
try {
|
|
15073
|
+
key = canonicalizeWorkspace(workspaceParam);
|
|
15074
|
+
} catch {
|
|
15075
|
+
sendWorkspaceMismatch(res, workspaceParam);
|
|
15076
|
+
return null;
|
|
15077
|
+
}
|
|
15078
|
+
const runtime = workspaceRegistry.getByWorkspaceCwd(key);
|
|
15079
|
+
if (!runtime) {
|
|
15080
|
+
sendWorkspaceMismatch(res, key);
|
|
15081
|
+
return null;
|
|
15082
|
+
}
|
|
15083
|
+
return runtime;
|
|
15084
|
+
}, "resolveRuntimeFromWorkspaceParam");
|
|
15085
|
+
const resolvePrimaryOnlyWorkspaceCwd = /* @__PURE__ */ __name((cwd, res, route) => {
|
|
15086
|
+
let key;
|
|
15087
|
+
try {
|
|
15088
|
+
key = canonicalizeWorkspace(cwd);
|
|
15089
|
+
} catch (err) {
|
|
15090
|
+
sendBridgeError2(res, err, { route });
|
|
15091
|
+
return void 0;
|
|
15092
|
+
}
|
|
15093
|
+
if (key !== boundWorkspace) {
|
|
15094
|
+
const runtime = workspaceRegistry.getByWorkspaceCwd(key);
|
|
15095
|
+
if (runtime && !runtime.primary) {
|
|
15096
|
+
res.status(400).json({
|
|
15097
|
+
error: "Non-primary persisted session load/resume is not supported in Phase 2a.",
|
|
15098
|
+
code: "secondary_workspace_load_not_supported",
|
|
15099
|
+
workspaceCwd: runtime.workspaceCwd,
|
|
15100
|
+
workspaceId: runtime.workspaceId,
|
|
15101
|
+
route
|
|
15102
|
+
});
|
|
15103
|
+
return void 0;
|
|
15104
|
+
}
|
|
15105
|
+
sendWorkspaceMismatch(res, key);
|
|
15106
|
+
return void 0;
|
|
15107
|
+
}
|
|
15108
|
+
return key;
|
|
15109
|
+
}, "resolvePrimaryOnlyWorkspaceCwd");
|
|
15110
|
+
const resolveLiveSessionRuntime = /* @__PURE__ */ __name((sessionId, res, route) => requireSessionRuntime({
|
|
15111
|
+
sessionId,
|
|
15112
|
+
route,
|
|
15113
|
+
res,
|
|
15114
|
+
workspaceRegistry,
|
|
15115
|
+
daemonLog
|
|
15116
|
+
}), "resolveLiveSessionRuntime");
|
|
15117
|
+
const sendNonPrimarySessionRouteUnsupported = /* @__PURE__ */ __name((res, route, sessionId, runtime) => {
|
|
15118
|
+
res.status(400).json({
|
|
15119
|
+
error: `Route "${route}" is primary-only for non-primary workspace sessions in Phase 2a.`,
|
|
15120
|
+
code: "non_primary_session_route_not_supported",
|
|
15121
|
+
sessionId,
|
|
15122
|
+
workspaceId: runtime.workspaceId,
|
|
15123
|
+
workspaceCwd: runtime.workspaceCwd,
|
|
15124
|
+
route
|
|
15125
|
+
});
|
|
15126
|
+
}, "sendNonPrimarySessionRouteUnsupported");
|
|
15127
|
+
const withOwnerMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
|
|
15128
|
+
const sessionId = requireSessionId(req, res);
|
|
15129
|
+
if (sessionId === null) return;
|
|
15130
|
+
const runtime = resolveLiveSessionRuntime(sessionId, res, route);
|
|
15131
|
+
if (!runtime) return;
|
|
15132
|
+
try {
|
|
15133
|
+
await archiveCoordinator.runSharedMany([sessionId], async () => {
|
|
15134
|
+
await handler(req, res, sessionId, runtime);
|
|
15135
|
+
});
|
|
15136
|
+
} catch (err) {
|
|
15137
|
+
sendBridgeError2(res, err, { route, sessionId });
|
|
15138
|
+
}
|
|
15139
|
+
}, "withOwnerMutableSession");
|
|
15140
|
+
const parseSessionIdsBody = /* @__PURE__ */ __name((req, res) => {
|
|
15141
|
+
const body = safeBody(req);
|
|
15142
|
+
const sessionIds = body["sessionIds"];
|
|
15143
|
+
if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((id) => typeof id === "string")) {
|
|
15144
|
+
res.status(400).json({
|
|
15145
|
+
error: "`sessionIds` must be a non-empty string array (max 100)",
|
|
15146
|
+
code: "invalid_request"
|
|
15147
|
+
});
|
|
15148
|
+
return void 0;
|
|
15149
|
+
}
|
|
15150
|
+
return [...new Set(sessionIds)];
|
|
15151
|
+
}, "parseSessionIdsBody");
|
|
15152
|
+
const serializeSessionErrors = /* @__PURE__ */ __name((errors) => errors.map((e) => ({
|
|
15153
|
+
sessionId: e.sessionId,
|
|
15154
|
+
error: e.error instanceof Error ? e.error.message : String(e.error)
|
|
15155
|
+
})), "serializeSessionErrors");
|
|
15156
|
+
const resolveWorkspaceParam = /* @__PURE__ */ __name((req, res) => {
|
|
14711
15157
|
const workspaceCwd = req.params["id"] ?? "";
|
|
14712
15158
|
if (!path10.isAbsolute(workspaceCwd)) {
|
|
14713
15159
|
res.status(400).json({ error: "`:id` must decode to an absolute workspace path" });
|
|
@@ -14728,6 +15174,12 @@ function registerSessionRoutes(app, deps) {
|
|
|
14728
15174
|
const withMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
|
|
14729
15175
|
const sessionId = requireSessionId(req, res);
|
|
14730
15176
|
if (sessionId === null) return;
|
|
15177
|
+
const runtime = resolveLiveSessionRuntime(sessionId, res, route);
|
|
15178
|
+
if (!runtime) return;
|
|
15179
|
+
if (!runtime.primary) {
|
|
15180
|
+
sendNonPrimarySessionRouteUnsupported(res, route, sessionId, runtime);
|
|
15181
|
+
return;
|
|
15182
|
+
}
|
|
14731
15183
|
try {
|
|
14732
15184
|
await archiveCoordinator.runSharedMany([sessionId], async () => {
|
|
14733
15185
|
await handler(req, res, sessionId);
|
|
@@ -14738,8 +15190,9 @@ function registerSessionRoutes(app, deps) {
|
|
|
14738
15190
|
}, "withMutableSession");
|
|
14739
15191
|
app.post("/session", mutate(), async (req, res) => {
|
|
14740
15192
|
const body = safeBody(req);
|
|
14741
|
-
const
|
|
14742
|
-
if (
|
|
15193
|
+
const resolvedRuntime = resolveRuntimeForSessionCreation(body, res);
|
|
15194
|
+
if (resolvedRuntime === void 0) return;
|
|
15195
|
+
const { runtime, workspaceCwd } = resolvedRuntime;
|
|
14743
15196
|
const modelServiceId = typeof body["modelServiceId"] === "string" ? body["modelServiceId"] : void 0;
|
|
14744
15197
|
const rawSessionScope = body["sessionScope"];
|
|
14745
15198
|
let sessionScope;
|
|
@@ -14756,8 +15209,8 @@ function registerSessionRoutes(app, deps) {
|
|
|
14756
15209
|
const clientId = parseClientIdHeader(req, res);
|
|
14757
15210
|
if (clientId === null) return;
|
|
14758
15211
|
try {
|
|
14759
|
-
const session = await bridge.spawnOrAttach({
|
|
14760
|
-
workspaceCwd
|
|
15212
|
+
const session = await runtime.bridge.spawnOrAttach({
|
|
15213
|
+
workspaceCwd,
|
|
14761
15214
|
modelServiceId,
|
|
14762
15215
|
...clientId !== void 0 ? { clientId } : {},
|
|
14763
15216
|
...sessionScope !== void 0 ? { sessionScope } : {}
|
|
@@ -14779,10 +15232,10 @@ function registerSessionRoutes(app, deps) {
|
|
|
14779
15232
|
);
|
|
14780
15233
|
}
|
|
14781
15234
|
if (!session.attached) {
|
|
14782
|
-
bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
|
|
15235
|
+
runtime.bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
|
|
14783
15236
|
});
|
|
14784
15237
|
} else {
|
|
14785
|
-
bridge.detachClient(session.sessionId, session.clientId).catch(() => {
|
|
15238
|
+
runtime.bridge.detachClient(session.sessionId, session.clientId).catch(() => {
|
|
14786
15239
|
});
|
|
14787
15240
|
}
|
|
14788
15241
|
return;
|
|
@@ -14798,21 +15251,27 @@ function registerSessionRoutes(app, deps) {
|
|
|
14798
15251
|
const body = safeBody(req);
|
|
14799
15252
|
const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
|
|
14800
15253
|
if (cwd === void 0) return;
|
|
15254
|
+
const primaryCwd = resolvePrimaryOnlyWorkspaceCwd(
|
|
15255
|
+
cwd,
|
|
15256
|
+
res,
|
|
15257
|
+
`POST /session/:id/${action}`
|
|
15258
|
+
);
|
|
15259
|
+
if (primaryCwd === void 0) return;
|
|
14801
15260
|
const clientId = parseClientIdHeader(req, res);
|
|
14802
15261
|
if (clientId === null) return;
|
|
14803
15262
|
try {
|
|
14804
15263
|
const session = await archiveCoordinator.runSharedMany(
|
|
14805
15264
|
[sessionId],
|
|
14806
15265
|
async () => {
|
|
14807
|
-
await assertSessionLoadable(
|
|
15266
|
+
await assertSessionLoadable(primaryCwd, sessionId);
|
|
14808
15267
|
return action === "load" ? await bridge.loadSession({
|
|
14809
15268
|
sessionId,
|
|
14810
|
-
workspaceCwd:
|
|
15269
|
+
workspaceCwd: primaryCwd,
|
|
14811
15270
|
historyReplay: "response",
|
|
14812
15271
|
...clientId !== void 0 ? { clientId } : {}
|
|
14813
15272
|
}) : await bridge.resumeSession({
|
|
14814
15273
|
sessionId,
|
|
14815
|
-
workspaceCwd:
|
|
15274
|
+
workspaceCwd: primaryCwd,
|
|
14816
15275
|
...clientId !== void 0 ? { clientId } : {}
|
|
14817
15276
|
});
|
|
14818
15277
|
}
|
|
@@ -14930,8 +15389,14 @@ function registerSessionRoutes(app, deps) {
|
|
|
14930
15389
|
app.get("/session/:id/status", (req, res) => {
|
|
14931
15390
|
const sessionId = requireSessionId(req, res);
|
|
14932
15391
|
if (sessionId === null) return;
|
|
15392
|
+
const runtime = resolveLiveSessionRuntime(
|
|
15393
|
+
sessionId,
|
|
15394
|
+
res,
|
|
15395
|
+
"GET /session/:id/status"
|
|
15396
|
+
);
|
|
15397
|
+
if (!runtime) return;
|
|
14933
15398
|
try {
|
|
14934
|
-
res.status(200).json(bridge.getSessionSummary(sessionId));
|
|
15399
|
+
res.status(200).json(runtime.bridge.getSessionSummary(sessionId));
|
|
14935
15400
|
} catch (err) {
|
|
14936
15401
|
sendBridgeError2(res, err, {
|
|
14937
15402
|
route: "GET /session/:id/status",
|
|
@@ -15200,9 +15665,10 @@ function registerSessionRoutes(app, deps) {
|
|
|
15200
15665
|
app.post(
|
|
15201
15666
|
"/session/:id/prompt",
|
|
15202
15667
|
mutate(),
|
|
15203
|
-
|
|
15668
|
+
withOwnerMutableSession(
|
|
15204
15669
|
"POST /session/:id/prompt",
|
|
15205
|
-
async (req, res, sessionId) => {
|
|
15670
|
+
async (req, res, sessionId, runtime) => {
|
|
15671
|
+
const ownerBridge = runtime.bridge;
|
|
15206
15672
|
const body = safeBody(req);
|
|
15207
15673
|
const prompt = body["prompt"];
|
|
15208
15674
|
if (!Array.isArray(prompt) || prompt.length === 0) {
|
|
@@ -15236,7 +15702,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
15236
15702
|
const promptId = crypto2.randomUUID();
|
|
15237
15703
|
const forwardedBody = { ...body };
|
|
15238
15704
|
delete forwardedBody["deadlineMs"];
|
|
15239
|
-
const lastEventId =
|
|
15705
|
+
const lastEventId = ownerBridge.getSessionLastEventId(sessionId);
|
|
15240
15706
|
addDaemonRequestAttribute("qwen-code.prompt_id", promptId);
|
|
15241
15707
|
const abort = new AbortController();
|
|
15242
15708
|
let responseFinished = false;
|
|
@@ -15264,7 +15730,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
15264
15730
|
}
|
|
15265
15731
|
let promptPromise;
|
|
15266
15732
|
try {
|
|
15267
|
-
promptPromise =
|
|
15733
|
+
promptPromise = ownerBridge.sendPrompt(
|
|
15268
15734
|
sessionId,
|
|
15269
15735
|
{
|
|
15270
15736
|
...forwardedBody,
|
|
@@ -15337,25 +15803,28 @@ function registerSessionRoutes(app, deps) {
|
|
|
15337
15803
|
app.post(
|
|
15338
15804
|
"/session/:id/heartbeat",
|
|
15339
15805
|
mutate(),
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15806
|
+
withOwnerMutableSession(
|
|
15807
|
+
"POST /session/:id/heartbeat",
|
|
15808
|
+
(req, res, sessionId, runtime) => {
|
|
15809
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15810
|
+
if (clientId === null) return;
|
|
15811
|
+
const result = runtime.bridge.recordHeartbeat(
|
|
15812
|
+
sessionId,
|
|
15813
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
15814
|
+
);
|
|
15815
|
+
res.status(200).json(result);
|
|
15816
|
+
}
|
|
15817
|
+
)
|
|
15349
15818
|
);
|
|
15350
15819
|
app.post(
|
|
15351
15820
|
"/session/:id/detach",
|
|
15352
15821
|
mutate(),
|
|
15353
|
-
|
|
15822
|
+
withOwnerMutableSession(
|
|
15354
15823
|
"POST /session/:id/detach",
|
|
15355
|
-
async (req, res, sessionId) => {
|
|
15824
|
+
async (req, res, sessionId, runtime) => {
|
|
15356
15825
|
const clientId = parseClientIdHeader(req, res);
|
|
15357
15826
|
if (clientId === null) return;
|
|
15358
|
-
await bridge.detachClient(sessionId, clientId);
|
|
15827
|
+
await runtime.bridge.detachClient(sessionId, clientId);
|
|
15359
15828
|
res.status(204).end();
|
|
15360
15829
|
}
|
|
15361
15830
|
)
|
|
@@ -15363,13 +15832,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
15363
15832
|
app.post(
|
|
15364
15833
|
"/session/:id/cancel",
|
|
15365
15834
|
mutate(),
|
|
15366
|
-
|
|
15835
|
+
withOwnerMutableSession(
|
|
15367
15836
|
"POST /session/:id/cancel",
|
|
15368
|
-
async (req, res, sessionId) => {
|
|
15837
|
+
async (req, res, sessionId, runtime) => {
|
|
15369
15838
|
const body = safeBody(req);
|
|
15370
15839
|
const clientId = parseClientIdHeader(req, res);
|
|
15371
15840
|
if (clientId === null) return;
|
|
15372
|
-
await bridge.cancelSession(
|
|
15841
|
+
await runtime.bridge.cancelSession(
|
|
15373
15842
|
sessionId,
|
|
15374
15843
|
{
|
|
15375
15844
|
...body,
|
|
@@ -15388,10 +15857,16 @@ function registerSessionRoutes(app, deps) {
|
|
|
15388
15857
|
const sessionId = req.params["id"];
|
|
15389
15858
|
const clientId = parseClientIdHeader(req, res);
|
|
15390
15859
|
if (clientId === null) return;
|
|
15860
|
+
const runtime = resolveLiveSessionRuntime(
|
|
15861
|
+
sessionId,
|
|
15862
|
+
res,
|
|
15863
|
+
"DELETE /session/:id"
|
|
15864
|
+
);
|
|
15865
|
+
if (!runtime) return;
|
|
15391
15866
|
try {
|
|
15392
15867
|
await archiveCoordinator.runExclusiveMany(
|
|
15393
15868
|
[sessionId],
|
|
15394
|
-
async () => bridge.closeSession(
|
|
15869
|
+
async () => runtime.bridge.closeSession(
|
|
15395
15870
|
sessionId,
|
|
15396
15871
|
clientId !== void 0 ? { clientId } : void 0
|
|
15397
15872
|
)
|
|
@@ -15637,21 +16112,9 @@ function registerSessionRoutes(app, deps) {
|
|
|
15637
16112
|
}
|
|
15638
16113
|
);
|
|
15639
16114
|
app.get("/workspace/:id/sessions", async (req, res) => {
|
|
15640
|
-
const
|
|
15641
|
-
if (
|
|
15642
|
-
|
|
15643
|
-
return;
|
|
15644
|
-
}
|
|
15645
|
-
const key = canonicalizeWorkspace(workspaceCwd);
|
|
15646
|
-
if (key !== boundWorkspace) {
|
|
15647
|
-
res.status(400).json({
|
|
15648
|
-
error: `Workspace mismatch: daemon is bound to "${boundWorkspace}"`,
|
|
15649
|
-
code: "workspace_mismatch",
|
|
15650
|
-
boundWorkspace,
|
|
15651
|
-
requestedWorkspace: key
|
|
15652
|
-
});
|
|
15653
|
-
return;
|
|
15654
|
-
}
|
|
16115
|
+
const runtime = resolveRuntimeFromWorkspaceParam(req, res);
|
|
16116
|
+
if (runtime === null) return;
|
|
16117
|
+
const key = runtime.workspaceCwd;
|
|
15655
16118
|
try {
|
|
15656
16119
|
const cursor = typeof req.query["cursor"] === "string" ? req.query["cursor"] : void 0;
|
|
15657
16120
|
const size = parseSessionPageSizeQuery(req.query["size"]);
|
|
@@ -15687,13 +16150,21 @@ function registerSessionRoutes(app, deps) {
|
|
|
15687
16150
|
}
|
|
15688
16151
|
archiveState = rawArchiveState;
|
|
15689
16152
|
}
|
|
15690
|
-
|
|
16153
|
+
if (!runtime.primary && (archiveState === "archived" || view)) {
|
|
16154
|
+
res.status(400).json({
|
|
16155
|
+
error: "Non-primary workspace session listing is live-only in Phase 2a.",
|
|
16156
|
+
code: "non_primary_live_sessions_only"
|
|
16157
|
+
});
|
|
16158
|
+
return;
|
|
16159
|
+
}
|
|
16160
|
+
const options = {
|
|
15691
16161
|
...cursor !== void 0 ? { cursor } : {},
|
|
15692
16162
|
...size !== void 0 ? { size } : {},
|
|
15693
16163
|
...archiveState !== void 0 ? { archiveState } : {},
|
|
15694
16164
|
...view !== void 0 ? { view } : {},
|
|
15695
16165
|
...group !== void 0 ? { group } : {}
|
|
15696
|
-
}
|
|
16166
|
+
};
|
|
16167
|
+
const result = runtime.primary ? await listWorkspaceSessionsForResponse(runtime.bridge, key, options) : listLiveWorkspaceSessionsForResponse(runtime.bridge, key, options);
|
|
15697
16168
|
res.status(200).json({
|
|
15698
16169
|
sessions: result.sessions,
|
|
15699
16170
|
...result.nextCursor != null ? { nextCursor: result.nextCursor } : {},
|
|
@@ -15851,10 +16322,16 @@ function registerSessionRoutes(app, deps) {
|
|
|
15851
16322
|
app.get("/session/:id/pending-prompts", (req, res) => {
|
|
15852
16323
|
const sessionId = requireSessionId(req, res);
|
|
15853
16324
|
if (sessionId === null) return;
|
|
16325
|
+
const runtime = resolveLiveSessionRuntime(
|
|
16326
|
+
sessionId,
|
|
16327
|
+
res,
|
|
16328
|
+
"GET /session/:id/pending-prompts"
|
|
16329
|
+
);
|
|
16330
|
+
if (!runtime) return;
|
|
15854
16331
|
const clientId = parseClientIdHeader(req, res);
|
|
15855
16332
|
if (clientId === null) return;
|
|
15856
16333
|
try {
|
|
15857
|
-
const pendingPrompts = bridge.getPendingPrompts(
|
|
16334
|
+
const pendingPrompts = runtime.bridge.getPendingPrompts(
|
|
15858
16335
|
sessionId,
|
|
15859
16336
|
clientId !== void 0 ? { clientId } : void 0
|
|
15860
16337
|
);
|
|
@@ -15869,9 +16346,9 @@ function registerSessionRoutes(app, deps) {
|
|
|
15869
16346
|
app.delete(
|
|
15870
16347
|
"/session/:id/pending-prompts/:promptId",
|
|
15871
16348
|
mutate(),
|
|
15872
|
-
|
|
16349
|
+
withOwnerMutableSession(
|
|
15873
16350
|
"DELETE /session/:id/pending-prompts/:promptId",
|
|
15874
|
-
(req, res, sessionId) => {
|
|
16351
|
+
(req, res, sessionId, runtime) => {
|
|
15875
16352
|
const clientId = parseClientIdHeader(req, res);
|
|
15876
16353
|
if (clientId === null) return;
|
|
15877
16354
|
const promptId = req.params["promptId"];
|
|
@@ -15879,7 +16356,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
15879
16356
|
res.status(400).json({ error: "`promptId` route parameter is required" });
|
|
15880
16357
|
return;
|
|
15881
16358
|
}
|
|
15882
|
-
const result = bridge.removePendingPrompt(
|
|
16359
|
+
const result = runtime.bridge.removePendingPrompt(
|
|
15883
16360
|
sessionId,
|
|
15884
16361
|
promptId,
|
|
15885
16362
|
clientId !== void 0 ? { clientId } : void 0
|
|
@@ -15997,7 +16474,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
15997
16474
|
);
|
|
15998
16475
|
app.post(
|
|
15999
16476
|
"/session/:id/approval-mode",
|
|
16000
|
-
mutate(
|
|
16477
|
+
mutate(),
|
|
16001
16478
|
withMutableSession(
|
|
16002
16479
|
"POST /session/:id/approval-mode",
|
|
16003
16480
|
async (req, res, sessionId) => {
|
|
@@ -16078,6 +16555,24 @@ var MAX_SCHEDULED_TASKS = MAX_JOBS;
|
|
|
16078
16555
|
var MAX_PROMPT_LENGTH = 1e5;
|
|
16079
16556
|
var MAX_NAME_LENGTH2 = 200;
|
|
16080
16557
|
var MAX_CRON_LENGTH = 200;
|
|
16558
|
+
var MAX_SESSION_NAME_LENGTH = 60;
|
|
16559
|
+
function scheduledTaskSessionName(label) {
|
|
16560
|
+
const cleaned = stripTerminalControlSequences(label).replace(/[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").trim().replace(/\s+/g, " ");
|
|
16561
|
+
let short = cleaned;
|
|
16562
|
+
if (cleaned.length > MAX_SESSION_NAME_LENGTH) {
|
|
16563
|
+
let cut = MAX_SESSION_NAME_LENGTH - 1;
|
|
16564
|
+
const boundary = cleaned.charCodeAt(cut - 1);
|
|
16565
|
+
if (boundary >= 55296 && boundary <= 56319) cut -= 1;
|
|
16566
|
+
short = `${cleaned.slice(0, cut)}\u2026`;
|
|
16567
|
+
}
|
|
16568
|
+
return `\u23F0 ${short}`;
|
|
16569
|
+
}
|
|
16570
|
+
__name(scheduledTaskSessionName, "scheduledTaskSessionName");
|
|
16571
|
+
function computeNextRunAt(task) {
|
|
16572
|
+
if (task.enabled === false) return null;
|
|
16573
|
+
return nextDurableFireMs(task);
|
|
16574
|
+
}
|
|
16575
|
+
__name(computeNextRunAt, "computeNextRunAt");
|
|
16081
16576
|
function toView(task) {
|
|
16082
16577
|
return {
|
|
16083
16578
|
id: task.id,
|
|
@@ -16088,7 +16583,14 @@ function toView(task) {
|
|
|
16088
16583
|
// Absent enabled defaults to enabled — tool-created tasks never write it.
|
|
16089
16584
|
enabled: task.enabled !== false,
|
|
16090
16585
|
createdAt: task.createdAt,
|
|
16091
|
-
lastFiredAt: task.lastFiredAt
|
|
16586
|
+
lastFiredAt: task.lastFiredAt,
|
|
16587
|
+
nextRunAt: computeNextRunAt(task),
|
|
16588
|
+
// The task's bound session (its run-history transcript), or null for an
|
|
16589
|
+
// unbound tool-created/legacy task.
|
|
16590
|
+
sessionId: typeof task.sessionId === "string" && task.sessionId.length > 0 ? task.sessionId : null,
|
|
16591
|
+
// Absent runs (tool-created / never-fired) normalizes to [] so the client
|
|
16592
|
+
// never special-cases undefined.
|
|
16593
|
+
runs: Array.isArray(task.runs) ? task.runs : []
|
|
16092
16594
|
};
|
|
16093
16595
|
}
|
|
16094
16596
|
__name(toView, "toView");
|
|
@@ -16102,8 +16604,26 @@ function validateCron(cron) {
|
|
|
16102
16604
|
}
|
|
16103
16605
|
}
|
|
16104
16606
|
__name(validateCron, "validateCron");
|
|
16607
|
+
function canonicalCron(cron) {
|
|
16608
|
+
try {
|
|
16609
|
+
const f = parseCron(cron);
|
|
16610
|
+
const s = /* @__PURE__ */ __name((set) => [...set].sort((a, b) => a - b).join(","), "s");
|
|
16611
|
+
return [
|
|
16612
|
+
s(f.minute),
|
|
16613
|
+
s(f.hour),
|
|
16614
|
+
s(f.dayOfMonth),
|
|
16615
|
+
s(f.month),
|
|
16616
|
+
s(f.dayOfWeek),
|
|
16617
|
+
f.domIsWild ? "W" : "",
|
|
16618
|
+
f.dowIsWild ? "W" : ""
|
|
16619
|
+
].join("|");
|
|
16620
|
+
} catch {
|
|
16621
|
+
return null;
|
|
16622
|
+
}
|
|
16623
|
+
}
|
|
16624
|
+
__name(canonicalCron, "canonicalCron");
|
|
16105
16625
|
function registerScheduledTasksRoutes(app, deps) {
|
|
16106
|
-
const { boundWorkspace, mutate, safeBody: safeBody2 } = deps;
|
|
16626
|
+
const { boundWorkspace, mutate, safeBody: safeBody2, bridge } = deps;
|
|
16107
16627
|
app.get("/scheduled-tasks", async (_req, res) => {
|
|
16108
16628
|
try {
|
|
16109
16629
|
const tasks = await readCronTasks(boundWorkspace);
|
|
@@ -16176,6 +16696,41 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16176
16696
|
}
|
|
16177
16697
|
const recurring = body["recurring"] !== false;
|
|
16178
16698
|
const enabled = body["enabled"] !== false;
|
|
16699
|
+
let boundSessionId;
|
|
16700
|
+
if (bridge) {
|
|
16701
|
+
try {
|
|
16702
|
+
if ((await readCronTasks(boundWorkspace)).length >= MAX_SCHEDULED_TASKS) {
|
|
16703
|
+
res.status(409).json({
|
|
16704
|
+
error: `Maximum number of scheduled tasks (${MAX_SCHEDULED_TASKS}) reached`,
|
|
16705
|
+
code: "max_tasks_reached"
|
|
16706
|
+
});
|
|
16707
|
+
return;
|
|
16708
|
+
}
|
|
16709
|
+
} catch {
|
|
16710
|
+
}
|
|
16711
|
+
try {
|
|
16712
|
+
const session = await bridge.spawnOrAttach({
|
|
16713
|
+
workspaceCwd: boundWorkspace,
|
|
16714
|
+
sessionScope: "thread"
|
|
16715
|
+
});
|
|
16716
|
+
boundSessionId = session.sessionId;
|
|
16717
|
+
try {
|
|
16718
|
+
bridge.updateSessionMetadata(boundSessionId, {
|
|
16719
|
+
displayName: scheduledTaskSessionName(nameResult.value ?? prompt)
|
|
16720
|
+
});
|
|
16721
|
+
} catch {
|
|
16722
|
+
}
|
|
16723
|
+
} catch (err) {
|
|
16724
|
+
writeStderrLine(
|
|
16725
|
+
`qwen serve: POST /scheduled-tasks failed to create the task's session: ${err instanceof Error ? err.message : String(err)}`
|
|
16726
|
+
);
|
|
16727
|
+
res.status(500).json({
|
|
16728
|
+
error: "Failed to create the task's session",
|
|
16729
|
+
code: "scheduled_tasks_session_failed"
|
|
16730
|
+
});
|
|
16731
|
+
return;
|
|
16732
|
+
}
|
|
16733
|
+
}
|
|
16179
16734
|
const now = Date.now();
|
|
16180
16735
|
const task = {
|
|
16181
16736
|
id: generateCronTaskId(),
|
|
@@ -16187,8 +16742,17 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16187
16742
|
// minute the task was created — same guard cronScheduler.create uses.
|
|
16188
16743
|
lastFiredAt: now - now % 6e4,
|
|
16189
16744
|
enabled,
|
|
16745
|
+
...boundSessionId !== void 0 ? { sessionId: boundSessionId } : {},
|
|
16190
16746
|
...nameResult.value !== void 0 ? { name: nameResult.value } : {}
|
|
16191
16747
|
};
|
|
16748
|
+
const rollbackSession = /* @__PURE__ */ __name(async () => {
|
|
16749
|
+
if (boundSessionId !== void 0 && bridge) {
|
|
16750
|
+
await bridge.closeSession(boundSessionId).catch(() => {
|
|
16751
|
+
});
|
|
16752
|
+
await new SessionService(boundWorkspace).removeSession(boundSessionId).catch(() => {
|
|
16753
|
+
});
|
|
16754
|
+
}
|
|
16755
|
+
}, "rollbackSession");
|
|
16192
16756
|
let overCap = false;
|
|
16193
16757
|
try {
|
|
16194
16758
|
await updateCronTasks(boundWorkspace, (tasks) => {
|
|
@@ -16199,6 +16763,7 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16199
16763
|
return [...tasks, task];
|
|
16200
16764
|
});
|
|
16201
16765
|
} catch (err) {
|
|
16766
|
+
await rollbackSession();
|
|
16202
16767
|
writeStderrLine(
|
|
16203
16768
|
`qwen serve: POST /scheduled-tasks failed: ${err instanceof Error ? err.message : String(err)}`
|
|
16204
16769
|
);
|
|
@@ -16209,6 +16774,7 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16209
16774
|
return;
|
|
16210
16775
|
}
|
|
16211
16776
|
if (overCap) {
|
|
16777
|
+
await rollbackSession();
|
|
16212
16778
|
res.status(409).json({
|
|
16213
16779
|
error: `Maximum number of scheduled tasks (${MAX_SCHEDULED_TASKS}) reached`,
|
|
16214
16780
|
code: "max_tasks_reached"
|
|
@@ -16294,17 +16860,32 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16294
16860
|
}
|
|
16295
16861
|
let found = false;
|
|
16296
16862
|
let updated;
|
|
16863
|
+
let blockedByArchive = false;
|
|
16297
16864
|
try {
|
|
16298
16865
|
await updateCronTasks(boundWorkspace, (tasks) => {
|
|
16299
16866
|
const idx = tasks.findIndex((t) => t.id === id);
|
|
16300
16867
|
if (idx === -1) return tasks;
|
|
16301
16868
|
found = true;
|
|
16302
16869
|
const current = tasks[idx];
|
|
16870
|
+
if (patch.enabled === true && current.disabledByArchive === true) {
|
|
16871
|
+
blockedByArchive = true;
|
|
16872
|
+
return tasks;
|
|
16873
|
+
}
|
|
16303
16874
|
const next = { ...current, ...patch };
|
|
16304
16875
|
if (clearName) delete next.name;
|
|
16305
|
-
|
|
16876
|
+
const justReEnabled = current.enabled === false && patch.enabled === true;
|
|
16877
|
+
const cronChanged = patch.cron !== void 0 && canonicalCron(patch.cron) !== canonicalCron(current.cron);
|
|
16878
|
+
const becameRecurring = patch.recurring === true && current.recurring !== true;
|
|
16879
|
+
const becameOneShot = patch.recurring === false && current.recurring !== false;
|
|
16880
|
+
{
|
|
16306
16881
|
const now = Date.now();
|
|
16307
|
-
|
|
16882
|
+
const minute = now - now % 6e4;
|
|
16883
|
+
if (next.recurring && (justReEnabled || cronChanged || becameRecurring)) {
|
|
16884
|
+
next.lastFiredAt = minute;
|
|
16885
|
+
} else if (!next.recurring && (justReEnabled || cronChanged || becameOneShot)) {
|
|
16886
|
+
next.createdAt = now;
|
|
16887
|
+
next.lastFiredAt = minute;
|
|
16888
|
+
}
|
|
16308
16889
|
}
|
|
16309
16890
|
updated = next;
|
|
16310
16891
|
return tasks.map((t, i) => i === idx ? next : t);
|
|
@@ -16319,10 +16900,26 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16319
16900
|
});
|
|
16320
16901
|
return;
|
|
16321
16902
|
}
|
|
16903
|
+
if (blockedByArchive) {
|
|
16904
|
+
res.status(409).json({
|
|
16905
|
+
error: "This task was disabled by archiving its session; unarchive the session to re-enable it.",
|
|
16906
|
+
code: "task_session_archived"
|
|
16907
|
+
});
|
|
16908
|
+
return;
|
|
16909
|
+
}
|
|
16322
16910
|
if (!found || !updated) {
|
|
16323
16911
|
res.status(404).json({ error: "Task not found", code: "task_not_found" });
|
|
16324
16912
|
return;
|
|
16325
16913
|
}
|
|
16914
|
+
const effectiveLabelChanged = patch.name !== void 0 || clearName || patch.prompt !== void 0 && updated.name === void 0;
|
|
16915
|
+
if (bridge && updated.sessionId && effectiveLabelChanged) {
|
|
16916
|
+
try {
|
|
16917
|
+
bridge.updateSessionMetadata(updated.sessionId, {
|
|
16918
|
+
displayName: scheduledTaskSessionName(updated.name ?? updated.prompt)
|
|
16919
|
+
});
|
|
16920
|
+
} catch {
|
|
16921
|
+
}
|
|
16922
|
+
}
|
|
16326
16923
|
res.status(200).json(toView(updated));
|
|
16327
16924
|
});
|
|
16328
16925
|
app.delete("/scheduled-tasks/:id", mutate(), async (req, res) => {
|
|
@@ -16331,9 +16928,19 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16331
16928
|
res.status(400).json({ error: "Task id is required", code: "invalid_id" });
|
|
16332
16929
|
return;
|
|
16333
16930
|
}
|
|
16334
|
-
let
|
|
16931
|
+
let boundSessionId;
|
|
16932
|
+
let removed = false;
|
|
16335
16933
|
try {
|
|
16336
|
-
|
|
16934
|
+
await updateCronTasks(boundWorkspace, (tasks) => {
|
|
16935
|
+
const idx = tasks.findIndex((t) => t.id === id);
|
|
16936
|
+
if (idx === -1) return tasks;
|
|
16937
|
+
const match = tasks[idx].sessionId;
|
|
16938
|
+
if (typeof match === "string" && match.length > 0) {
|
|
16939
|
+
boundSessionId = match;
|
|
16940
|
+
}
|
|
16941
|
+
removed = true;
|
|
16942
|
+
return tasks.filter((_, i) => i !== idx);
|
|
16943
|
+
});
|
|
16337
16944
|
} catch (err) {
|
|
16338
16945
|
writeStderrLine(
|
|
16339
16946
|
`qwen serve: DELETE /scheduled-tasks/${id} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -16344,12 +16951,76 @@ function registerScheduledTasksRoutes(app, deps) {
|
|
|
16344
16951
|
});
|
|
16345
16952
|
return;
|
|
16346
16953
|
}
|
|
16347
|
-
if (removed
|
|
16954
|
+
if (!removed) {
|
|
16348
16955
|
res.status(404).json({ error: "Task not found", code: "task_not_found" });
|
|
16349
16956
|
return;
|
|
16350
16957
|
}
|
|
16958
|
+
if (boundSessionId && bridge) {
|
|
16959
|
+
await bridge.closeSession(boundSessionId).catch(() => {
|
|
16960
|
+
});
|
|
16961
|
+
}
|
|
16351
16962
|
res.status(200).json({ deleted: true, id });
|
|
16352
16963
|
});
|
|
16964
|
+
app.post("/scheduled-tasks/:id/run", mutate(), async (req, res) => {
|
|
16965
|
+
const id = typeof req.params["id"] === "string" ? req.params["id"] : "";
|
|
16966
|
+
if (id.length === 0) {
|
|
16967
|
+
res.status(400).json({ error: "Task id is required", code: "invalid_id" });
|
|
16968
|
+
return;
|
|
16969
|
+
}
|
|
16970
|
+
const now = Date.now();
|
|
16971
|
+
let found = false;
|
|
16972
|
+
let blockedDisabled = false;
|
|
16973
|
+
let updated;
|
|
16974
|
+
try {
|
|
16975
|
+
await updateCronTasks(boundWorkspace, (tasks) => {
|
|
16976
|
+
const idx = tasks.findIndex((t) => t.id === id);
|
|
16977
|
+
if (idx === -1) return tasks;
|
|
16978
|
+
found = true;
|
|
16979
|
+
const current = tasks[idx];
|
|
16980
|
+
if (current.enabled === false) {
|
|
16981
|
+
blockedDisabled = true;
|
|
16982
|
+
return tasks;
|
|
16983
|
+
}
|
|
16984
|
+
const next = {
|
|
16985
|
+
...current,
|
|
16986
|
+
lastFiredAt: now,
|
|
16987
|
+
runs: appendCronRun(current.runs, {
|
|
16988
|
+
at: now,
|
|
16989
|
+
kind: "manual",
|
|
16990
|
+
...current.sessionId ? { sessionId: current.sessionId } : {}
|
|
16991
|
+
})
|
|
16992
|
+
};
|
|
16993
|
+
updated = next;
|
|
16994
|
+
if (!current.recurring) {
|
|
16995
|
+
return tasks.filter((_, i) => i !== idx);
|
|
16996
|
+
}
|
|
16997
|
+
return tasks.map((t, i) => i === idx ? next : t);
|
|
16998
|
+
});
|
|
16999
|
+
} catch (err) {
|
|
17000
|
+
writeStderrLine(
|
|
17001
|
+
`qwen serve: POST /scheduled-tasks/${id}/run failed: ${err instanceof Error ? err.message : String(err)}`
|
|
17002
|
+
);
|
|
17003
|
+
res.status(500).json({
|
|
17004
|
+
error: "Failed to record scheduled task run",
|
|
17005
|
+
code: "scheduled_tasks_write_failed"
|
|
17006
|
+
});
|
|
17007
|
+
return;
|
|
17008
|
+
}
|
|
17009
|
+
if (blockedDisabled) {
|
|
17010
|
+
res.status(409).json({
|
|
17011
|
+
error: "Cannot run a disabled task; enable it first (unarchive its session if it was archived).",
|
|
17012
|
+
code: "task_disabled"
|
|
17013
|
+
});
|
|
17014
|
+
return;
|
|
17015
|
+
}
|
|
17016
|
+
if (!found || !updated) {
|
|
17017
|
+
res.status(404).json({ error: "Task not found", code: "task_not_found" });
|
|
17018
|
+
return;
|
|
17019
|
+
}
|
|
17020
|
+
const view = toView(updated);
|
|
17021
|
+
if (!updated.recurring) view.nextRunAt = null;
|
|
17022
|
+
res.status(200).json(view);
|
|
17023
|
+
});
|
|
16353
17024
|
}
|
|
16354
17025
|
__name(registerScheduledTasksRoutes, "registerScheduledTasksRoutes");
|
|
16355
17026
|
function parseNameField(raw) {
|
|
@@ -16384,7 +17055,7 @@ function parseHeatmapDays(raw) {
|
|
|
16384
17055
|
}
|
|
16385
17056
|
__name(parseHeatmapDays, "parseHeatmapDays");
|
|
16386
17057
|
function registerUsageStatsRoutes(app, deps = {}) {
|
|
16387
|
-
const loadHistory = deps.loadHistory ?? (() =>
|
|
17058
|
+
const loadHistory = deps.loadHistory ?? (() => loadUsageHistoryWithLive());
|
|
16388
17059
|
const ttlMs = deps.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
16389
17060
|
let cache = null;
|
|
16390
17061
|
const getRecords = /* @__PURE__ */ __name(() => {
|
|
@@ -16424,6 +17095,320 @@ function registerUsageStatsRoutes(app, deps = {}) {
|
|
|
16424
17095
|
}
|
|
16425
17096
|
__name(registerUsageStatsRoutes, "registerUsageStatsRoutes");
|
|
16426
17097
|
|
|
17098
|
+
// packages/cli/src/serve/scheduled-task-keepalive.ts
|
|
17099
|
+
init_esbuild_shims();
|
|
17100
|
+
import * as fsSync from "node:fs";
|
|
17101
|
+
import * as path11 from "node:path";
|
|
17102
|
+
var log = createDebugLogger("SCHED_KEEPALIVE");
|
|
17103
|
+
function collectBoundSessionIds(tasks) {
|
|
17104
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17105
|
+
const ids = [];
|
|
17106
|
+
for (const task of tasks) {
|
|
17107
|
+
const sessionId = task.sessionId;
|
|
17108
|
+
if (task.enabled === false || // disabled (e.g. archived) — let it be reaped
|
|
17109
|
+
typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) {
|
|
17110
|
+
continue;
|
|
17111
|
+
}
|
|
17112
|
+
seen.add(sessionId);
|
|
17113
|
+
ids.push(sessionId);
|
|
17114
|
+
}
|
|
17115
|
+
return ids;
|
|
17116
|
+
}
|
|
17117
|
+
__name(collectBoundSessionIds, "collectBoundSessionIds");
|
|
17118
|
+
var KEEPALIVE_REVIVE_TIMEOUT_MS = 3e4;
|
|
17119
|
+
var KEEPALIVE_SPAWN_TIMEOUT_MS = 3e4;
|
|
17120
|
+
var MAX_REVIVE_BACKOFF_MS = 30 * 6e4;
|
|
17121
|
+
async function bindAndNameSessions(bridge, boundWorkspace, tasks, renamed, spawnTimeoutMs, binding) {
|
|
17122
|
+
const unbound = tasks.filter(
|
|
17123
|
+
(t) => !t.sessionId && t.enabled !== false && !binding.has(t.id)
|
|
17124
|
+
);
|
|
17125
|
+
const needsName = tasks.filter(
|
|
17126
|
+
(t) => t.sessionId && t.enabled !== false && !renamed.has(t.sessionId)
|
|
17127
|
+
);
|
|
17128
|
+
for (const task of unbound) {
|
|
17129
|
+
let spawnedSessionId;
|
|
17130
|
+
try {
|
|
17131
|
+
binding.add(task.id);
|
|
17132
|
+
const rawSpawn = bridge.spawnOrAttach({
|
|
17133
|
+
workspaceCwd: boundWorkspace,
|
|
17134
|
+
sessionScope: "thread"
|
|
17135
|
+
});
|
|
17136
|
+
let timedOut = false;
|
|
17137
|
+
rawSpawn.then(({ sessionId: sessionId2 }) => {
|
|
17138
|
+
if (timedOut) {
|
|
17139
|
+
log.debug(
|
|
17140
|
+
"keepalive: late spawn resolved, cleaning up",
|
|
17141
|
+
task.id,
|
|
17142
|
+
sessionId2
|
|
17143
|
+
);
|
|
17144
|
+
bridge.closeSession(sessionId2).catch(() => {
|
|
17145
|
+
});
|
|
17146
|
+
new SessionService(boundWorkspace).removeSession(sessionId2).catch(() => {
|
|
17147
|
+
});
|
|
17148
|
+
}
|
|
17149
|
+
}).catch(() => {
|
|
17150
|
+
}).finally(() => {
|
|
17151
|
+
binding.delete(task.id);
|
|
17152
|
+
});
|
|
17153
|
+
const { sessionId } = await withTimeout(
|
|
17154
|
+
rawSpawn,
|
|
17155
|
+
spawnTimeoutMs,
|
|
17156
|
+
`spawnOrAttach(${task.id})`
|
|
17157
|
+
).catch((err) => {
|
|
17158
|
+
timedOut = true;
|
|
17159
|
+
throw err;
|
|
17160
|
+
});
|
|
17161
|
+
spawnedSessionId = sessionId;
|
|
17162
|
+
try {
|
|
17163
|
+
bridge.updateSessionMetadata(sessionId, {
|
|
17164
|
+
displayName: scheduledTaskSessionName(task.prompt)
|
|
17165
|
+
});
|
|
17166
|
+
renamed.add(sessionId);
|
|
17167
|
+
} catch {
|
|
17168
|
+
}
|
|
17169
|
+
let matched = false;
|
|
17170
|
+
await updateCronTasks(boundWorkspace, (list) => {
|
|
17171
|
+
if (!list.some(
|
|
17172
|
+
(t) => t.id === task.id && !t.sessionId && t.enabled !== false
|
|
17173
|
+
)) {
|
|
17174
|
+
return list;
|
|
17175
|
+
}
|
|
17176
|
+
const result = list.map(
|
|
17177
|
+
(t) => t.id === task.id && !t.sessionId && t.enabled !== false ? { ...t, sessionId } : t
|
|
17178
|
+
);
|
|
17179
|
+
matched = true;
|
|
17180
|
+
return result;
|
|
17181
|
+
});
|
|
17182
|
+
if (!matched) {
|
|
17183
|
+
throw new Error(`task ${task.id} no longer on disk`);
|
|
17184
|
+
}
|
|
17185
|
+
log.debug(
|
|
17186
|
+
"keepalive: bound task",
|
|
17187
|
+
task.id,
|
|
17188
|
+
"to dedicated session",
|
|
17189
|
+
sessionId
|
|
17190
|
+
);
|
|
17191
|
+
} catch (err) {
|
|
17192
|
+
log.debug("keepalive: failed to bind task", task.id, err);
|
|
17193
|
+
if (spawnedSessionId !== void 0) {
|
|
17194
|
+
await bridge.closeSession(spawnedSessionId).catch(() => {
|
|
17195
|
+
});
|
|
17196
|
+
await new SessionService(boundWorkspace).removeSession(spawnedSessionId).catch(() => {
|
|
17197
|
+
});
|
|
17198
|
+
}
|
|
17199
|
+
}
|
|
17200
|
+
}
|
|
17201
|
+
for (const task of needsName) {
|
|
17202
|
+
const sessionId = task.sessionId;
|
|
17203
|
+
try {
|
|
17204
|
+
bridge.updateSessionMetadata(sessionId, {
|
|
17205
|
+
displayName: scheduledTaskSessionName(task.prompt)
|
|
17206
|
+
});
|
|
17207
|
+
renamed.add(sessionId);
|
|
17208
|
+
} catch (err) {
|
|
17209
|
+
log.debug("keepalive: failed to name session", sessionId, err);
|
|
17210
|
+
}
|
|
17211
|
+
}
|
|
17212
|
+
}
|
|
17213
|
+
__name(bindAndNameSessions, "bindAndNameSessions");
|
|
17214
|
+
function startScheduledTaskKeepalive(opts) {
|
|
17215
|
+
const { bridge, boundWorkspace, intervalMs } = opts;
|
|
17216
|
+
const reviveTimeoutMs = opts.reviveTimeoutMs ?? KEEPALIVE_REVIVE_TIMEOUT_MS;
|
|
17217
|
+
const spawnTimeoutMs = opts.spawnTimeoutMs ?? KEEPALIVE_SPAWN_TIMEOUT_MS;
|
|
17218
|
+
const reviveState = /* @__PURE__ */ new Map();
|
|
17219
|
+
const reviving = /* @__PURE__ */ new Set();
|
|
17220
|
+
const binding = /* @__PURE__ */ new Set();
|
|
17221
|
+
const renamed = /* @__PURE__ */ new Set();
|
|
17222
|
+
const tick = /* @__PURE__ */ __name(async () => {
|
|
17223
|
+
let tasks;
|
|
17224
|
+
try {
|
|
17225
|
+
tasks = await readCronTasks(boundWorkspace);
|
|
17226
|
+
} catch (err) {
|
|
17227
|
+
log.debug("keepalive: readCronTasks failed, skipping this pass", err);
|
|
17228
|
+
return;
|
|
17229
|
+
}
|
|
17230
|
+
for (const sessionId of collectBoundSessionIds(tasks)) {
|
|
17231
|
+
try {
|
|
17232
|
+
bridge.recordHeartbeat(sessionId);
|
|
17233
|
+
reviveState.delete(sessionId);
|
|
17234
|
+
} catch (err) {
|
|
17235
|
+
const state = reviveState.get(sessionId);
|
|
17236
|
+
if (reviving.has(sessionId)) {
|
|
17237
|
+
continue;
|
|
17238
|
+
}
|
|
17239
|
+
if (state && Date.now() < state.nextAttemptAt) {
|
|
17240
|
+
continue;
|
|
17241
|
+
}
|
|
17242
|
+
log.debug("keepalive: recordHeartbeat failed for", sessionId, err);
|
|
17243
|
+
const load = bridge.loadSession({
|
|
17244
|
+
sessionId,
|
|
17245
|
+
workspaceCwd: boundWorkspace,
|
|
17246
|
+
historyReplay: "response"
|
|
17247
|
+
});
|
|
17248
|
+
reviving.add(sessionId);
|
|
17249
|
+
void load.catch(() => {
|
|
17250
|
+
}).finally(() => {
|
|
17251
|
+
reviving.delete(sessionId);
|
|
17252
|
+
});
|
|
17253
|
+
try {
|
|
17254
|
+
await withTimeout(load, reviveTimeoutMs, `loadSession(${sessionId})`);
|
|
17255
|
+
log.debug("keepalive: revived non-resident session", sessionId);
|
|
17256
|
+
reviveState.delete(sessionId);
|
|
17257
|
+
} catch (loadErr) {
|
|
17258
|
+
const failures = (state?.failures ?? 0) + 1;
|
|
17259
|
+
const backoff = Math.min(
|
|
17260
|
+
intervalMs * 2 ** Math.min(failures - 1, 6),
|
|
17261
|
+
MAX_REVIVE_BACKOFF_MS
|
|
17262
|
+
);
|
|
17263
|
+
reviveState.set(sessionId, {
|
|
17264
|
+
failures,
|
|
17265
|
+
nextAttemptAt: Date.now() + backoff
|
|
17266
|
+
});
|
|
17267
|
+
log.debug(
|
|
17268
|
+
"keepalive: failed to revive session",
|
|
17269
|
+
sessionId,
|
|
17270
|
+
`(failure ${failures}, next retry in ${backoff}ms)`,
|
|
17271
|
+
loadErr
|
|
17272
|
+
);
|
|
17273
|
+
}
|
|
17274
|
+
}
|
|
17275
|
+
}
|
|
17276
|
+
if (reviveState.size > 0 || renamed.size > 0) {
|
|
17277
|
+
const live = new Set(tasks.map((t) => t.sessionId));
|
|
17278
|
+
for (const id of reviveState.keys()) {
|
|
17279
|
+
if (!live.has(id)) reviveState.delete(id);
|
|
17280
|
+
}
|
|
17281
|
+
for (const id of renamed) {
|
|
17282
|
+
if (!live.has(id)) renamed.delete(id);
|
|
17283
|
+
}
|
|
17284
|
+
}
|
|
17285
|
+
await bindAndNameSessions(
|
|
17286
|
+
bridge,
|
|
17287
|
+
boundWorkspace,
|
|
17288
|
+
tasks,
|
|
17289
|
+
renamed,
|
|
17290
|
+
spawnTimeoutMs,
|
|
17291
|
+
binding
|
|
17292
|
+
);
|
|
17293
|
+
}, "tick");
|
|
17294
|
+
let running = false;
|
|
17295
|
+
const timer = setInterval(() => {
|
|
17296
|
+
if (running) return;
|
|
17297
|
+
running = true;
|
|
17298
|
+
void tick().finally(() => {
|
|
17299
|
+
running = false;
|
|
17300
|
+
});
|
|
17301
|
+
}, intervalMs);
|
|
17302
|
+
timer.unref?.();
|
|
17303
|
+
let bindDebounce;
|
|
17304
|
+
const cronFilePath = getCronFilePath(boundWorkspace);
|
|
17305
|
+
const cronDir = path11.dirname(cronFilePath);
|
|
17306
|
+
const cronFileName = path11.basename(cronFilePath);
|
|
17307
|
+
let fileWatcher;
|
|
17308
|
+
try {
|
|
17309
|
+
fsSync.mkdirSync(cronDir, { recursive: true });
|
|
17310
|
+
fileWatcher = fsSync.watch(
|
|
17311
|
+
cronDir,
|
|
17312
|
+
{ persistent: false },
|
|
17313
|
+
(_event, filename) => {
|
|
17314
|
+
if (filename !== null && filename !== cronFileName) return;
|
|
17315
|
+
if (bindDebounce) clearTimeout(bindDebounce);
|
|
17316
|
+
bindDebounce = setTimeout(() => {
|
|
17317
|
+
if (running) return;
|
|
17318
|
+
running = true;
|
|
17319
|
+
void tick().finally(() => {
|
|
17320
|
+
running = false;
|
|
17321
|
+
});
|
|
17322
|
+
}, 500);
|
|
17323
|
+
bindDebounce.unref?.();
|
|
17324
|
+
}
|
|
17325
|
+
);
|
|
17326
|
+
fileWatcher.on("error", () => {
|
|
17327
|
+
});
|
|
17328
|
+
} catch {
|
|
17329
|
+
}
|
|
17330
|
+
let stopped = false;
|
|
17331
|
+
return {
|
|
17332
|
+
stop: /* @__PURE__ */ __name(() => {
|
|
17333
|
+
if (stopped) return;
|
|
17334
|
+
stopped = true;
|
|
17335
|
+
clearInterval(timer);
|
|
17336
|
+
if (bindDebounce) clearTimeout(bindDebounce);
|
|
17337
|
+
fileWatcher?.close();
|
|
17338
|
+
}, "stop"),
|
|
17339
|
+
tick
|
|
17340
|
+
};
|
|
17341
|
+
}
|
|
17342
|
+
__name(startScheduledTaskKeepalive, "startScheduledTaskKeepalive");
|
|
17343
|
+
var REHYDRATE_LOAD_TIMEOUT_MS = 3e4;
|
|
17344
|
+
var REHYDRATE_MAX_CONCURRENCY = 4;
|
|
17345
|
+
async function rehydrateScheduledTaskSessions(deps) {
|
|
17346
|
+
const { bridge, boundWorkspace } = deps;
|
|
17347
|
+
const timeoutMs = deps.loadTimeoutMs ?? REHYDRATE_LOAD_TIMEOUT_MS;
|
|
17348
|
+
let tasks;
|
|
17349
|
+
try {
|
|
17350
|
+
tasks = await readCronTasks(boundWorkspace);
|
|
17351
|
+
} catch (err) {
|
|
17352
|
+
log.debug("rehydrate: readCronTasks failed", err);
|
|
17353
|
+
return { loaded: [], failed: [] };
|
|
17354
|
+
}
|
|
17355
|
+
const sessionIds = collectBoundSessionIds(tasks);
|
|
17356
|
+
const loaded = [];
|
|
17357
|
+
const failed = [];
|
|
17358
|
+
const loadOne = /* @__PURE__ */ __name(async (sessionId) => {
|
|
17359
|
+
const load = bridge.loadSession({
|
|
17360
|
+
sessionId,
|
|
17361
|
+
workspaceCwd: boundWorkspace,
|
|
17362
|
+
historyReplay: "response"
|
|
17363
|
+
});
|
|
17364
|
+
void load.catch(() => {
|
|
17365
|
+
});
|
|
17366
|
+
try {
|
|
17367
|
+
await withTimeout(load, timeoutMs, `loadSession(${sessionId})`);
|
|
17368
|
+
loaded.push(sessionId);
|
|
17369
|
+
} catch (err) {
|
|
17370
|
+
failed.push(sessionId);
|
|
17371
|
+
try {
|
|
17372
|
+
deps.onError?.(sessionId, err);
|
|
17373
|
+
} catch {
|
|
17374
|
+
}
|
|
17375
|
+
}
|
|
17376
|
+
}, "loadOne");
|
|
17377
|
+
const queue = sessionIds.slice();
|
|
17378
|
+
const worker = /* @__PURE__ */ __name(async () => {
|
|
17379
|
+
for (let sessionId = queue.shift(); sessionId !== void 0; sessionId = queue.shift()) {
|
|
17380
|
+
await loadOne(sessionId);
|
|
17381
|
+
}
|
|
17382
|
+
}, "worker");
|
|
17383
|
+
await Promise.all(
|
|
17384
|
+
Array.from(
|
|
17385
|
+
{ length: Math.min(REHYDRATE_MAX_CONCURRENCY, queue.length) },
|
|
17386
|
+
() => worker()
|
|
17387
|
+
)
|
|
17388
|
+
);
|
|
17389
|
+
return { loaded, failed };
|
|
17390
|
+
}
|
|
17391
|
+
__name(rehydrateScheduledTaskSessions, "rehydrateScheduledTaskSessions");
|
|
17392
|
+
function withTimeout(p, ms, label) {
|
|
17393
|
+
return new Promise((resolve4, reject) => {
|
|
17394
|
+
const timer = setTimeout(() => {
|
|
17395
|
+
reject(new Error(`${label} timed out after ${ms}ms`));
|
|
17396
|
+
}, ms);
|
|
17397
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
17398
|
+
p.then(
|
|
17399
|
+
(v) => {
|
|
17400
|
+
clearTimeout(timer);
|
|
17401
|
+
resolve4(v);
|
|
17402
|
+
},
|
|
17403
|
+
(e) => {
|
|
17404
|
+
clearTimeout(timer);
|
|
17405
|
+
reject(e);
|
|
17406
|
+
}
|
|
17407
|
+
);
|
|
17408
|
+
});
|
|
17409
|
+
}
|
|
17410
|
+
__name(withTimeout, "withTimeout");
|
|
17411
|
+
|
|
16427
17412
|
// packages/cli/src/serve/routes/workspace-status.ts
|
|
16428
17413
|
init_esbuild_shims();
|
|
16429
17414
|
function registerWorkspaceStatusRoutes(app, deps) {
|
|
@@ -16543,6 +17528,7 @@ init_esbuild_shims();
|
|
|
16543
17528
|
|
|
16544
17529
|
// packages/cli/src/serve/server/serve-features.ts
|
|
16545
17530
|
init_esbuild_shims();
|
|
17531
|
+
var DEFAULT_MAX_SESSIONS = 20;
|
|
16546
17532
|
var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
|
|
16547
17533
|
var SERVE_LANGUAGE_CODES = [
|
|
16548
17534
|
...SUPPORTED_LANGUAGES.map((language) => language.code),
|
|
@@ -16554,13 +17540,20 @@ function advertisedMaxPendingPromptsPerSession(value) {
|
|
|
16554
17540
|
return value;
|
|
16555
17541
|
}
|
|
16556
17542
|
__name(advertisedMaxPendingPromptsPerSession, "advertisedMaxPendingPromptsPerSession");
|
|
17543
|
+
function advertisedMaxSessions(value) {
|
|
17544
|
+
if (value === void 0) return DEFAULT_MAX_SESSIONS;
|
|
17545
|
+
if (value === 0 || value === Number.POSITIVE_INFINITY) return null;
|
|
17546
|
+
return value;
|
|
17547
|
+
}
|
|
17548
|
+
__name(advertisedMaxSessions, "advertisedMaxSessions");
|
|
16557
17549
|
function createServeFeatures(deps) {
|
|
16558
17550
|
const {
|
|
16559
17551
|
opts,
|
|
16560
17552
|
boundWorkspace,
|
|
16561
17553
|
persistSettingAvailable,
|
|
16562
17554
|
reloadAvailable,
|
|
16563
|
-
sessionShellCommandEnabled
|
|
17555
|
+
sessionShellCommandEnabled,
|
|
17556
|
+
multiWorkspaceSessionsEnabled
|
|
16564
17557
|
} = deps;
|
|
16565
17558
|
let cachedVoiceTranscriptionAvailable;
|
|
16566
17559
|
const invalidateServeFeaturesCache = /* @__PURE__ */ __name(() => {
|
|
@@ -16583,6 +17576,7 @@ function createServeFeatures(deps) {
|
|
|
16583
17576
|
sessionShellCommandEnabled,
|
|
16584
17577
|
rateLimit: opts.rateLimit === true,
|
|
16585
17578
|
reloadAvailable,
|
|
17579
|
+
multiWorkspaceSessionsEnabled,
|
|
16586
17580
|
clientMcpOverWsEnabled: opts.clientMcpOverWs === true,
|
|
16587
17581
|
cdpTunnelOverWsEnabled: opts.cdpTunnelOverWs === true,
|
|
16588
17582
|
voiceTranscriptionAvailable: getCachedVoiceTranscriptionAvailable(),
|
|
@@ -16612,6 +17606,8 @@ __name(isWorkspaceVoiceTranscriptionAvailable, "isWorkspaceVoiceTranscriptionAva
|
|
|
16612
17606
|
// packages/cli/src/serve/routes/capabilities.ts
|
|
16613
17607
|
function registerCapabilitiesRoutes(app, deps) {
|
|
16614
17608
|
app.get("/capabilities", (_req, res) => {
|
|
17609
|
+
const runtimes = deps.workspaceRegistry.list();
|
|
17610
|
+
const multiWorkspace = runtimes.length > 1;
|
|
16615
17611
|
const envelope = {
|
|
16616
17612
|
v: CAPABILITIES_SCHEMA_VERSION,
|
|
16617
17613
|
protocolVersions: getServeProtocolVersions(),
|
|
@@ -16619,8 +17615,8 @@ function registerCapabilitiesRoutes(app, deps) {
|
|
|
16619
17615
|
mode: deps.mode,
|
|
16620
17616
|
features: deps.currentServeFeatures(),
|
|
16621
17617
|
modelServices: [],
|
|
16622
|
-
// Surface the
|
|
16623
|
-
//
|
|
17618
|
+
// Surface the primary workspace so clients can omit `cwd` on
|
|
17619
|
+
// `POST /session`; multi-workspace clients use `workspaces[]`.
|
|
16624
17620
|
workspaceCwd: deps.boundWorkspace,
|
|
16625
17621
|
// Advertise supported transport families so SDK clients can
|
|
16626
17622
|
// auto-negotiate the best available transport via negotiateTransport().
|
|
@@ -16630,8 +17626,22 @@ function registerCapabilitiesRoutes(app, deps) {
|
|
|
16630
17626
|
limits: {
|
|
16631
17627
|
maxPendingPromptsPerSession: advertisedMaxPendingPromptsPerSession(
|
|
16632
17628
|
deps.maxPendingPromptsPerSession
|
|
16633
|
-
)
|
|
17629
|
+
),
|
|
17630
|
+
...multiWorkspace ? {
|
|
17631
|
+
maxSessionsPerWorkspace: advertisedMaxSessions(
|
|
17632
|
+
deps.maxSessionsPerWorkspace
|
|
17633
|
+
),
|
|
17634
|
+
maxTotalSessions: deps.maxTotalSessions === void 0 || deps.maxTotalSessions === 0 || deps.maxTotalSessions === Number.POSITIVE_INFINITY ? null : deps.maxTotalSessions
|
|
17635
|
+
} : {}
|
|
16634
17636
|
},
|
|
17637
|
+
...multiWorkspace ? {
|
|
17638
|
+
workspaces: runtimes.map((runtime) => ({
|
|
17639
|
+
id: runtime.workspaceId,
|
|
17640
|
+
cwd: runtime.workspaceCwd,
|
|
17641
|
+
primary: runtime.primary,
|
|
17642
|
+
trusted: runtime.trusted
|
|
17643
|
+
}))
|
|
17644
|
+
} : {},
|
|
16635
17645
|
supportedLanguages: deps.languageCodes
|
|
16636
17646
|
};
|
|
16637
17647
|
res.status(200).json(envelope);
|
|
@@ -17155,7 +18165,28 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
17155
18165
|
res.status(503).json({
|
|
17156
18166
|
error: err.message,
|
|
17157
18167
|
code: "session_limit_exceeded",
|
|
17158
|
-
limit: err.limit
|
|
18168
|
+
limit: err.limit,
|
|
18169
|
+
scope: "workspace"
|
|
18170
|
+
});
|
|
18171
|
+
return;
|
|
18172
|
+
}
|
|
18173
|
+
if (err instanceof TotalSessionLimitExceededError) {
|
|
18174
|
+
const totalSessionError = err;
|
|
18175
|
+
daemonLog?.warn("total session admission rejected", {
|
|
18176
|
+
...ctx?.route ? { route: ctx.route } : {},
|
|
18177
|
+
...ctx?.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
18178
|
+
limit: totalSessionError.limit,
|
|
18179
|
+
scope: totalSessionError.scope,
|
|
18180
|
+
...totalSessionError.operation ? { operation: totalSessionError.operation } : {},
|
|
18181
|
+
...totalSessionError.workspaceCwd ? { workspaceCwd: totalSessionError.workspaceCwd } : {},
|
|
18182
|
+
...totalSessionError.sourceSessionId ? { sourceSessionId: totalSessionError.sourceSessionId } : {}
|
|
18183
|
+
});
|
|
18184
|
+
res.set("Retry-After", "5");
|
|
18185
|
+
res.status(503).json({
|
|
18186
|
+
error: err.message,
|
|
18187
|
+
code: "session_limit_exceeded",
|
|
18188
|
+
limit: err.limit,
|
|
18189
|
+
scope: err.scope
|
|
17159
18190
|
});
|
|
17160
18191
|
return;
|
|
17161
18192
|
}
|
|
@@ -17357,7 +18388,7 @@ data: ${dataJson}
|
|
|
17357
18388
|
}
|
|
17358
18389
|
__name(formatSseFrame, "formatSseFrame");
|
|
17359
18390
|
function registerSseEventsRoutes(app, deps) {
|
|
17360
|
-
const {
|
|
18391
|
+
const { workspaceRegistry, daemonLog, sendBridgeError: sendBridgeError2, writerIdleTimeoutMs } = deps;
|
|
17361
18392
|
app.get("/session/:id/events", (req, res) => {
|
|
17362
18393
|
const sessionId = req.params["id"];
|
|
17363
18394
|
const lastEventId = parseLastEventId(req.headers["last-event-id"]);
|
|
@@ -17366,8 +18397,16 @@ function registerSseEventsRoutes(app, deps) {
|
|
|
17366
18397
|
let iter;
|
|
17367
18398
|
const abort = new AbortController();
|
|
17368
18399
|
try {
|
|
18400
|
+
const runtime = requireSessionRuntime({
|
|
18401
|
+
sessionId,
|
|
18402
|
+
route: "GET /session/:id/events",
|
|
18403
|
+
res,
|
|
18404
|
+
workspaceRegistry,
|
|
18405
|
+
daemonLog
|
|
18406
|
+
});
|
|
18407
|
+
if (!runtime) return;
|
|
17369
18408
|
const snapshot = req.query["snapshot"] === "1";
|
|
17370
|
-
const iterable = bridge.subscribeEvents(sessionId, {
|
|
18409
|
+
const iterable = runtime.bridge.subscribeEvents(sessionId, {
|
|
17371
18410
|
signal: abort.signal,
|
|
17372
18411
|
lastEventId,
|
|
17373
18412
|
...maxQueued !== void 0 ? { maxQueued } : {},
|
|
@@ -17573,10 +18612,13 @@ __name(registerSseEventsRoutes, "registerSseEventsRoutes");
|
|
|
17573
18612
|
// packages/cli/src/serve/routes/a2ui-action.ts
|
|
17574
18613
|
init_esbuild_shims();
|
|
17575
18614
|
import * as fsp4 from "node:fs/promises";
|
|
17576
|
-
import * as
|
|
18615
|
+
import * as path12 from "node:path";
|
|
17577
18616
|
var A2UI_MIME = "application/a2ui+json";
|
|
17578
18617
|
var ACTION_TOOL = "action";
|
|
17579
18618
|
var CALL_TIMEOUT_MS = 15e3;
|
|
18619
|
+
var SCRUBBED_STDIO_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
18620
|
+
QWEN_SERVER_TOKEN_ENV
|
|
18621
|
+
]);
|
|
17580
18622
|
function usableServerConfig(cfg) {
|
|
17581
18623
|
if (!cfg) return false;
|
|
17582
18624
|
if (typeof cfg.httpUrl === "string") {
|
|
@@ -17593,7 +18635,7 @@ __name(usableServerConfig, "usableServerConfig");
|
|
|
17593
18635
|
async function findFromSettingsFile(workspaceCwd) {
|
|
17594
18636
|
try {
|
|
17595
18637
|
const raw = await fsp4.readFile(
|
|
17596
|
-
|
|
18638
|
+
path12.join(workspaceCwd, ".qwen", "settings.json"),
|
|
17597
18639
|
"utf8"
|
|
17598
18640
|
);
|
|
17599
18641
|
const settings = JSON.parse(raw);
|
|
@@ -17607,22 +18649,34 @@ async function findFromSettingsFile(workspaceCwd) {
|
|
|
17607
18649
|
return null;
|
|
17608
18650
|
}
|
|
17609
18651
|
__name(findFromSettingsFile, "findFromSettingsFile");
|
|
17610
|
-
function buildTransport(cfg) {
|
|
18652
|
+
function buildTransport(cfg, baseEnv = snapshotProcessEnv()) {
|
|
17611
18653
|
if (typeof cfg.httpUrl === "string") {
|
|
17612
18654
|
return new StreamableHTTPClientTransport(new URL(cfg.httpUrl));
|
|
17613
18655
|
}
|
|
17614
18656
|
return new StdioClientTransport({
|
|
17615
18657
|
command: cfg.command,
|
|
17616
18658
|
args: cfg.args ?? [],
|
|
17617
|
-
//
|
|
17618
|
-
//
|
|
17619
|
-
|
|
17620
|
-
// unset, let the SDK apply its safe default environment.
|
|
17621
|
-
...cfg.env ? { env: { ...process.env, ...cfg.env } } : {},
|
|
18659
|
+
// Passing `env` prevents the SDK from reading live process.env while
|
|
18660
|
+
// keeping daemon MCP stdio behavior aligned with the core CLI client.
|
|
18661
|
+
env: buildStdioServerEnv(baseEnv, cfg.env),
|
|
17622
18662
|
cwd: cfg.cwd
|
|
17623
18663
|
});
|
|
17624
18664
|
}
|
|
17625
18665
|
__name(buildTransport, "buildTransport");
|
|
18666
|
+
function buildStdioServerEnv(baseEnv, serverEnv) {
|
|
18667
|
+
const env = {};
|
|
18668
|
+
for (const [key, value] of Object.entries(baseEnv)) {
|
|
18669
|
+
if (value !== void 0 && !key.startsWith("BASH_FUNC_") && !value.startsWith("()") && !SCRUBBED_STDIO_ENV_KEYS.has(key)) {
|
|
18670
|
+
env[key] = value;
|
|
18671
|
+
}
|
|
18672
|
+
}
|
|
18673
|
+
const merged = { ...env, ...serverEnv ?? {} };
|
|
18674
|
+
for (const key of SCRUBBED_STDIO_ENV_KEYS) {
|
|
18675
|
+
delete merged[key];
|
|
18676
|
+
}
|
|
18677
|
+
return merged;
|
|
18678
|
+
}
|
|
18679
|
+
__name(buildStdioServerEnv, "buildStdioServerEnv");
|
|
17626
18680
|
function extractA2uiActionResult(result) {
|
|
17627
18681
|
if (result.isError) {
|
|
17628
18682
|
const errMsg2 = (result.content ?? []).filter(
|
|
@@ -17648,8 +18702,8 @@ function extractA2uiActionResult(result) {
|
|
|
17648
18702
|
return { commands, fallback };
|
|
17649
18703
|
}
|
|
17650
18704
|
__name(extractA2uiActionResult, "extractA2uiActionResult");
|
|
17651
|
-
async function callA2uiAction(cfg, args) {
|
|
17652
|
-
const transport = buildTransport(cfg);
|
|
18705
|
+
async function callA2uiAction(cfg, args, env) {
|
|
18706
|
+
const transport = buildTransport(cfg, env);
|
|
17653
18707
|
const client = new Client({ name: "qwen-serve-a2ui", version: "0.0.1" });
|
|
17654
18708
|
try {
|
|
17655
18709
|
await client.connect(transport, { timeout: CALL_TIMEOUT_MS });
|
|
@@ -17669,7 +18723,7 @@ async function callA2uiAction(cfg, args) {
|
|
|
17669
18723
|
__name(callA2uiAction, "callA2uiAction");
|
|
17670
18724
|
function registerA2uiActionRoutes(app, opts) {
|
|
17671
18725
|
const { boundWorkspace, mutate, safeBody: safeBody2, getMcpServers } = opts;
|
|
17672
|
-
const callAction = opts.callAction ?? callA2uiAction;
|
|
18726
|
+
const callAction = opts.callAction ?? ((cfg, args) => callA2uiAction(cfg, args, opts.env));
|
|
17673
18727
|
app.post(
|
|
17674
18728
|
"/session/:id/a2ui-action",
|
|
17675
18729
|
mutate(),
|
|
@@ -17718,7 +18772,7 @@ __name(registerA2uiActionRoutes, "registerA2uiActionRoutes");
|
|
|
17718
18772
|
|
|
17719
18773
|
// packages/cli/src/serve/server/fs-factory.ts
|
|
17720
18774
|
init_esbuild_shims();
|
|
17721
|
-
import * as
|
|
18775
|
+
import * as path13 from "node:path";
|
|
17722
18776
|
var IDE_WORKSPACE_PATH_ENV_VAR = "QWEN_CODE_IDE_WORKSPACE_PATH";
|
|
17723
18777
|
function createDefaultFsAuditEmit() {
|
|
17724
18778
|
const WARN_EVERY = 100;
|
|
@@ -17792,14 +18846,14 @@ function parseIdeWorkspacePathEnv(value) {
|
|
|
17792
18846
|
const parsed = JSON.parse(value);
|
|
17793
18847
|
if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
|
|
17794
18848
|
return parsed.filter(
|
|
17795
|
-
(workspace) => workspace.length > 0 &&
|
|
18849
|
+
(workspace) => workspace.length > 0 && path13.isAbsolute(workspace)
|
|
17796
18850
|
);
|
|
17797
18851
|
}
|
|
17798
18852
|
throw new Error("IDE workspace path JSON must be a string array");
|
|
17799
18853
|
} catch {
|
|
17800
18854
|
}
|
|
17801
18855
|
}
|
|
17802
|
-
return value.split(
|
|
18856
|
+
return value.split(path13.delimiter).filter((workspace) => workspace.length > 0 && path13.isAbsolute(workspace));
|
|
17803
18857
|
}
|
|
17804
18858
|
__name(parseIdeWorkspacePathEnv, "parseIdeWorkspacePathEnv");
|
|
17805
18859
|
function dropNestedWorkspacesPreservingPrimary(workspaces) {
|
|
@@ -17825,17 +18879,17 @@ __name(dropNestedWorkspacesPreservingPrimary, "dropNestedWorkspacesPreservingPri
|
|
|
17825
18879
|
// packages/cli/src/serve/server/telemetry.ts
|
|
17826
18880
|
init_esbuild_shims();
|
|
17827
18881
|
function resolveDaemonTelemetryRoute(req) {
|
|
17828
|
-
const
|
|
17829
|
-
if (req.method === "POST" &&
|
|
18882
|
+
const path14 = req.path.replace(/\/$/, "") || "/";
|
|
18883
|
+
if (req.method === "POST" && path14 === "/session") {
|
|
17830
18884
|
return { route: "POST /session" };
|
|
17831
18885
|
}
|
|
17832
|
-
if (req.method === "POST" &&
|
|
18886
|
+
if (req.method === "POST" && path14 === "/sessions/delete") {
|
|
17833
18887
|
return { route: "POST /sessions/delete" };
|
|
17834
18888
|
}
|
|
17835
|
-
if (req.method === "GET" &&
|
|
18889
|
+
if (req.method === "GET" && path14 === "/daemon/status") {
|
|
17836
18890
|
return { route: "GET /daemon/status" };
|
|
17837
18891
|
}
|
|
17838
|
-
const sessionAction =
|
|
18892
|
+
const sessionAction = path14.match(
|
|
17839
18893
|
/^\/session\/([^/]+)\/(load|resume|prompt|cancel|recap|btw|mid-turn-message|model|shell|detach|rewind|approval-mode|language|a2ui-action)$/
|
|
17840
18894
|
);
|
|
17841
18895
|
const sessionActionId = sessionAction?.[1];
|
|
@@ -17846,14 +18900,14 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
17846
18900
|
sessionId: sessionActionId
|
|
17847
18901
|
};
|
|
17848
18902
|
}
|
|
17849
|
-
const sessionMetadata =
|
|
18903
|
+
const sessionMetadata = path14.match(/^\/session\/([^/]+)\/metadata$/);
|
|
17850
18904
|
if (sessionMetadata?.[1] && req.method === "PATCH") {
|
|
17851
18905
|
return {
|
|
17852
18906
|
route: "PATCH /session/:id/metadata",
|
|
17853
18907
|
sessionId: sessionMetadata[1]
|
|
17854
18908
|
};
|
|
17855
18909
|
}
|
|
17856
|
-
const sessionArtifacts =
|
|
18910
|
+
const sessionArtifacts = path14.match(/^\/session\/([^/]+)\/artifacts$/);
|
|
17857
18911
|
if (sessionArtifacts?.[1]) {
|
|
17858
18912
|
if (req.method === "GET") {
|
|
17859
18913
|
return {
|
|
@@ -17868,7 +18922,7 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
17868
18922
|
};
|
|
17869
18923
|
}
|
|
17870
18924
|
}
|
|
17871
|
-
const sessionArtifact =
|
|
18925
|
+
const sessionArtifact = path14.match(
|
|
17872
18926
|
/^\/session\/([^/]+)\/artifacts\/([^/]+)$/
|
|
17873
18927
|
);
|
|
17874
18928
|
if (sessionArtifact?.[1] && req.method === "DELETE") {
|
|
@@ -17877,7 +18931,7 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
17877
18931
|
sessionId: sessionArtifact[1]
|
|
17878
18932
|
};
|
|
17879
18933
|
}
|
|
17880
|
-
const sessionPermission =
|
|
18934
|
+
const sessionPermission = path14.match(
|
|
17881
18935
|
/^\/session\/([^/]+)\/permission\/([^/]+)$/
|
|
17882
18936
|
);
|
|
17883
18937
|
if (sessionPermission?.[1] && sessionPermission?.[2] && req.method === "POST") {
|
|
@@ -17888,7 +18942,7 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
17888
18942
|
...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
|
|
17889
18943
|
};
|
|
17890
18944
|
}
|
|
17891
|
-
const globalPermission =
|
|
18945
|
+
const globalPermission = path14.match(/^\/permission\/([^/]+)$/);
|
|
17892
18946
|
if (globalPermission?.[1] && req.method === "POST") {
|
|
17893
18947
|
const rawRequestId = globalPermission[1];
|
|
17894
18948
|
return {
|
|
@@ -17896,66 +18950,66 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
17896
18950
|
...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
|
|
17897
18951
|
};
|
|
17898
18952
|
}
|
|
17899
|
-
const deleteSession =
|
|
18953
|
+
const deleteSession = path14.match(/^\/session\/([^/]+)$/);
|
|
17900
18954
|
const deleteSessionId = deleteSession?.[1];
|
|
17901
18955
|
if (deleteSessionId && req.method === "DELETE") {
|
|
17902
18956
|
return { route: "DELETE /session/:id", sessionId: deleteSessionId };
|
|
17903
18957
|
}
|
|
17904
|
-
if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(
|
|
18958
|
+
if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(path14)) {
|
|
17905
18959
|
return { route: "GET /workspace/:id/sessions" };
|
|
17906
18960
|
}
|
|
17907
|
-
if (req.method === "POST" &&
|
|
18961
|
+
if (req.method === "POST" && path14 === "/workspace/init") {
|
|
17908
18962
|
return { route: "POST /workspace/init" };
|
|
17909
18963
|
}
|
|
17910
|
-
if (req.method === "POST" &&
|
|
18964
|
+
if (req.method === "POST" && path14 === "/workspace/setup-github") {
|
|
17911
18965
|
return { route: "POST /workspace/setup-github" };
|
|
17912
18966
|
}
|
|
17913
|
-
if (req.method === "POST" &&
|
|
18967
|
+
if (req.method === "POST" && path14 === "/workspace/reload") {
|
|
17914
18968
|
return { route: "POST /workspace/reload" };
|
|
17915
18969
|
}
|
|
17916
|
-
const mcpRestart =
|
|
18970
|
+
const mcpRestart = path14.match(/^\/workspace\/mcp\/([^/]+)\/restart$/);
|
|
17917
18971
|
if (mcpRestart?.[1] && req.method === "POST") {
|
|
17918
18972
|
return { route: "POST /workspace/mcp/:server/restart" };
|
|
17919
18973
|
}
|
|
17920
|
-
if (req.method === "POST" &&
|
|
18974
|
+
if (req.method === "POST" && path14 === "/workspace/mcp/servers") {
|
|
17921
18975
|
return { route: "POST /workspace/mcp/servers" };
|
|
17922
18976
|
}
|
|
17923
|
-
const mcpDelete =
|
|
18977
|
+
const mcpDelete = path14.match(/^\/workspace\/mcp\/servers\/([^/]+)$/);
|
|
17924
18978
|
if (mcpDelete?.[1] && req.method === "DELETE") {
|
|
17925
18979
|
return { route: "DELETE /workspace/mcp/servers/:name" };
|
|
17926
18980
|
}
|
|
17927
|
-
if (req.method === "POST" &&
|
|
18981
|
+
if (req.method === "POST" && path14 === "/workspace/auth/device-flow") {
|
|
17928
18982
|
return { route: "POST /workspace/auth/device-flow" };
|
|
17929
18983
|
}
|
|
17930
|
-
const deviceFlowDelete =
|
|
18984
|
+
const deviceFlowDelete = path14.match(
|
|
17931
18985
|
/^\/workspace\/auth\/device-flow\/([^/]+)$/
|
|
17932
18986
|
);
|
|
17933
18987
|
if (deviceFlowDelete?.[1] && req.method === "DELETE") {
|
|
17934
18988
|
return { route: "DELETE /workspace/auth/device-flow/:id" };
|
|
17935
18989
|
}
|
|
17936
|
-
const toolEnable =
|
|
18990
|
+
const toolEnable = path14.match(/^\/workspace\/tools\/([^/]+)\/enable$/);
|
|
17937
18991
|
if (toolEnable?.[1] && req.method === "POST") {
|
|
17938
18992
|
return { route: "POST /workspace/tools/:name/enable" };
|
|
17939
18993
|
}
|
|
17940
|
-
if (
|
|
18994
|
+
if (path14 === "/workspace/settings") {
|
|
17941
18995
|
if (req.method === "GET") return { route: "GET /workspace/settings" };
|
|
17942
18996
|
if (req.method === "POST") return { route: "POST /workspace/settings" };
|
|
17943
18997
|
}
|
|
17944
|
-
if (
|
|
18998
|
+
if (path14 === "/workspace/permissions") {
|
|
17945
18999
|
if (req.method === "GET") return { route: "GET /workspace/permissions" };
|
|
17946
19000
|
if (req.method === "POST") return { route: "POST /workspace/permissions" };
|
|
17947
19001
|
}
|
|
17948
|
-
if (
|
|
19002
|
+
if (path14 === "/workspace/trust") {
|
|
17949
19003
|
if (req.method === "GET") return { route: "GET /workspace/trust" };
|
|
17950
19004
|
}
|
|
17951
|
-
if (req.method === "POST" &&
|
|
19005
|
+
if (req.method === "POST" && path14 === "/workspace/trust/request") {
|
|
17952
19006
|
return { route: "POST /workspace/trust/request" };
|
|
17953
19007
|
}
|
|
17954
|
-
if (
|
|
19008
|
+
if (path14 === "/workspace/voice") {
|
|
17955
19009
|
if (req.method === "GET") return { route: "GET /workspace/voice" };
|
|
17956
19010
|
if (req.method === "POST") return { route: "POST /workspace/voice" };
|
|
17957
19011
|
}
|
|
17958
|
-
if (req.method === "POST" &&
|
|
19012
|
+
if (req.method === "POST" && path14 === "/workspace/voice/transcribe") {
|
|
17959
19013
|
return { route: "POST /workspace/voice/transcribe" };
|
|
17960
19014
|
}
|
|
17961
19015
|
return void 0;
|
|
@@ -18323,10 +19377,10 @@ function installRateLimiter(app, opts, daemonLog) {
|
|
|
18323
19377
|
{ tier, key: key.slice(0, 64) }
|
|
18324
19378
|
);
|
|
18325
19379
|
} : void 0,
|
|
18326
|
-
onError: daemonLog ? (err,
|
|
19380
|
+
onError: daemonLog ? (err, path14) => {
|
|
18327
19381
|
daemonLog.warn(
|
|
18328
19382
|
`rate limiter error (fail-open): ${err instanceof Error ? err.message : String(err)}`,
|
|
18329
|
-
{ path:
|
|
19383
|
+
{ path: path14 }
|
|
18330
19384
|
);
|
|
18331
19385
|
} : void 0
|
|
18332
19386
|
});
|
|
@@ -18377,73 +19431,6 @@ function installSelfOriginStripMiddleware(app, getPort) {
|
|
|
18377
19431
|
}
|
|
18378
19432
|
__name(installSelfOriginStripMiddleware, "installSelfOriginStripMiddleware");
|
|
18379
19433
|
|
|
18380
|
-
// packages/cli/src/serve/workspace-registry.ts
|
|
18381
|
-
init_esbuild_shims();
|
|
18382
|
-
function createWorkspaceRegistry(inputRuntimes) {
|
|
18383
|
-
if (inputRuntimes.length === 0) {
|
|
18384
|
-
throw new Error(
|
|
18385
|
-
"WorkspaceRegistry requires at least one workspace runtime."
|
|
18386
|
-
);
|
|
18387
|
-
}
|
|
18388
|
-
const primaryRuntimes = inputRuntimes.filter((runtime) => runtime.primary);
|
|
18389
|
-
if (primaryRuntimes.length !== 1) {
|
|
18390
|
-
throw new Error(
|
|
18391
|
-
"WorkspaceRegistry requires exactly one primary workspace runtime."
|
|
18392
|
-
);
|
|
18393
|
-
}
|
|
18394
|
-
const byCwd = /* @__PURE__ */ new Map();
|
|
18395
|
-
const byId = /* @__PURE__ */ new Map();
|
|
18396
|
-
for (const runtime of inputRuntimes) {
|
|
18397
|
-
if (byCwd.has(runtime.workspaceCwd)) {
|
|
18398
|
-
throw new Error(
|
|
18399
|
-
`Duplicate workspace runtime cwd ${JSON.stringify(
|
|
18400
|
-
runtime.workspaceCwd
|
|
18401
|
-
)}.`
|
|
18402
|
-
);
|
|
18403
|
-
}
|
|
18404
|
-
byCwd.set(runtime.workspaceCwd, runtime);
|
|
18405
|
-
if (byId.has(runtime.workspaceId)) {
|
|
18406
|
-
throw new Error(
|
|
18407
|
-
`Duplicate workspace runtime id ${JSON.stringify(
|
|
18408
|
-
runtime.workspaceId
|
|
18409
|
-
)}.`
|
|
18410
|
-
);
|
|
18411
|
-
}
|
|
18412
|
-
byId.set(runtime.workspaceId, runtime);
|
|
18413
|
-
}
|
|
18414
|
-
const runtimes = Object.freeze([...inputRuntimes]);
|
|
18415
|
-
const primary = primaryRuntimes[0];
|
|
18416
|
-
return {
|
|
18417
|
-
primary,
|
|
18418
|
-
list: /* @__PURE__ */ __name(() => runtimes, "list"),
|
|
18419
|
-
getByWorkspaceCwd: /* @__PURE__ */ __name((workspaceCwd) => byCwd.get(workspaceCwd), "getByWorkspaceCwd"),
|
|
18420
|
-
getByWorkspaceId: /* @__PURE__ */ __name((workspaceId) => byId.get(workspaceId), "getByWorkspaceId"),
|
|
18421
|
-
resolveWorkspaceCwd: /* @__PURE__ */ __name((workspaceCwd) => workspaceCwd === void 0 ? primary : byCwd.get(workspaceCwd), "resolveWorkspaceCwd"),
|
|
18422
|
-
resolveLiveSessionOwner: /* @__PURE__ */ __name((sessionId) => {
|
|
18423
|
-
const matches = [];
|
|
18424
|
-
for (const runtime of runtimes) {
|
|
18425
|
-
try {
|
|
18426
|
-
runtime.bridge.getSessionSummary(sessionId);
|
|
18427
|
-
matches.push(runtime);
|
|
18428
|
-
} catch (err) {
|
|
18429
|
-
if (err instanceof SessionNotFoundError) continue;
|
|
18430
|
-
throw err;
|
|
18431
|
-
}
|
|
18432
|
-
}
|
|
18433
|
-
if (matches.length === 0) return { kind: "not_found" };
|
|
18434
|
-
if (matches.length === 1) {
|
|
18435
|
-
return { kind: "found", runtime: matches[0] };
|
|
18436
|
-
}
|
|
18437
|
-
return { kind: "ambiguous", runtimes: matches };
|
|
18438
|
-
}, "resolveLiveSessionOwner")
|
|
18439
|
-
};
|
|
18440
|
-
}
|
|
18441
|
-
__name(createWorkspaceRegistry, "createWorkspaceRegistry");
|
|
18442
|
-
function createSingleWorkspaceRegistry(runtime) {
|
|
18443
|
-
return createWorkspaceRegistry([runtime]);
|
|
18444
|
-
}
|
|
18445
|
-
__name(createSingleWorkspaceRegistry, "createSingleWorkspaceRegistry");
|
|
18446
|
-
|
|
18447
19434
|
// packages/cli/src/serve/routes/workspace-lifecycle.ts
|
|
18448
19435
|
init_esbuild_shims();
|
|
18449
19436
|
function registerWorkspaceLifecycleRoutes(app, deps) {
|
|
@@ -18746,6 +19733,22 @@ function describeRegistryPrimaryForConflict(registry) {
|
|
|
18746
19733
|
return `registry primary cwd=${JSON.stringify(registry.primary.workspaceCwd)}, workspaceId=${JSON.stringify(registry.primary.workspaceId)}`;
|
|
18747
19734
|
}
|
|
18748
19735
|
__name(describeRegistryPrimaryForConflict, "describeRegistryPrimaryForConflict");
|
|
19736
|
+
function getRuntimeEffectiveEnv(metadata) {
|
|
19737
|
+
return metadata?.effectiveEnv;
|
|
19738
|
+
}
|
|
19739
|
+
__name(getRuntimeEffectiveEnv, "getRuntimeEffectiveEnv");
|
|
19740
|
+
var DEFAULT_SESSION_IDLE_TIMEOUT_MS = 30 * 6e4;
|
|
19741
|
+
var KEEPALIVE_MIN_INTERVAL_MS = 3e4;
|
|
19742
|
+
var KEEPALIVE_MAX_INTERVAL_MS = 10 * 6e4;
|
|
19743
|
+
function computeKeepaliveIntervalMs(idleTimeoutMs) {
|
|
19744
|
+
if (idleTimeoutMs <= 0) return KEEPALIVE_MAX_INTERVAL_MS;
|
|
19745
|
+
const target = Math.min(
|
|
19746
|
+
Math.max(KEEPALIVE_MIN_INTERVAL_MS, Math.floor(idleTimeoutMs / 3)),
|
|
19747
|
+
KEEPALIVE_MAX_INTERVAL_MS
|
|
19748
|
+
);
|
|
19749
|
+
return Math.max(1, Math.min(target, Math.floor(idleTimeoutMs / 2)));
|
|
19750
|
+
}
|
|
19751
|
+
__name(computeKeepaliveIntervalMs, "computeKeepaliveIntervalMs");
|
|
18749
19752
|
function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
18750
19753
|
const app = (0, import_express4.default)();
|
|
18751
19754
|
const injectedWorkspaceRegistry = deps.workspaceRegistry;
|
|
@@ -18814,6 +19817,8 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18814
19817
|
);
|
|
18815
19818
|
}
|
|
18816
19819
|
const clientMcpSenderRegistry = injectedWorkspaceRegistry?.primary.clientMcpSenderRegistry ?? deps.clientMcpSenderRegistry ?? new ClientMcpSenderRegistry();
|
|
19820
|
+
const primaryRuntimeEnvMetadata = injectedWorkspaceRegistry?.primary.env ?? deps.primaryRuntimeEnv;
|
|
19821
|
+
const primaryEffectiveEnv = getRuntimeEffectiveEnv(primaryRuntimeEnvMetadata);
|
|
18817
19822
|
const { languageCodes, currentServeFeatures, invalidateServeFeaturesCache } = createServeFeatures({
|
|
18818
19823
|
opts,
|
|
18819
19824
|
boundWorkspace,
|
|
@@ -18821,13 +19826,23 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18821
19826
|
// Registry injection supplies the primary workspace service through the
|
|
18822
19827
|
// runtime, so it has the same reload surface as legacy deps.workspace.
|
|
18823
19828
|
reloadAvailable: deps.workspace !== void 0 || injectedWorkspaceRegistry !== void 0,
|
|
18824
|
-
sessionShellCommandEnabled
|
|
19829
|
+
sessionShellCommandEnabled,
|
|
19830
|
+
multiWorkspaceSessionsEnabled: (injectedWorkspaceRegistry?.list().length ?? 1) > 1
|
|
18825
19831
|
});
|
|
18826
|
-
const statusProvider = deps.statusProvider ?? createDaemonStatusProvider(
|
|
19832
|
+
const statusProvider = deps.statusProvider ?? createDaemonStatusProvider(
|
|
19833
|
+
primaryEffectiveEnv ? { env: primaryEffectiveEnv } : {}
|
|
19834
|
+
);
|
|
19835
|
+
let defaultBridgeForAdmission;
|
|
19836
|
+
const totalSessionAdmission = !deps.bridge && !injectedWorkspaceRegistry ? createTotalSessionAdmissionController({
|
|
19837
|
+
maxTotalSessions: opts.maxTotalSessions,
|
|
19838
|
+
getBridges: /* @__PURE__ */ __name(() => defaultBridgeForAdmission ? [defaultBridgeForAdmission] : [], "getBridges")
|
|
19839
|
+
}) : void 0;
|
|
18827
19840
|
const bridge = injectedWorkspaceRegistry?.primary.bridge ?? deps.bridge ?? createAcpSessionBridge({
|
|
18828
19841
|
maxSessions: opts.maxSessions,
|
|
19842
|
+
...totalSessionAdmission ? { freshSessionAdmission: totalSessionAdmission.admit } : {},
|
|
18829
19843
|
maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
|
|
18830
19844
|
eventRingSize: opts.eventRingSize,
|
|
19845
|
+
compactedReplayMaxBytes: opts.compactedReplayMaxBytes,
|
|
18831
19846
|
permissionResponseTimeoutMs: opts.permissionResponseTimeoutMs,
|
|
18832
19847
|
boundWorkspace,
|
|
18833
19848
|
sessionShellCommandEnabled,
|
|
@@ -18841,6 +19856,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18841
19856
|
// ext-method by reaching the WS connection that hosts the named server.
|
|
18842
19857
|
clientMcpSender: clientMcpSenderRegistry.lookup
|
|
18843
19858
|
});
|
|
19859
|
+
if (!injectedWorkspaceRegistry && !deps.bridge) {
|
|
19860
|
+
defaultBridgeForAdmission = bridge;
|
|
19861
|
+
}
|
|
18844
19862
|
const archiveCoordinator = new SessionArchiveCoordinator();
|
|
18845
19863
|
installSelfOriginStripMiddleware(app, getPort);
|
|
18846
19864
|
app.locals.fsFactory = fsFactory;
|
|
@@ -18858,7 +19876,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18858
19876
|
boundWorkspace,
|
|
18859
19877
|
contextFilename: deps.contextFilename ?? "QWEN.md",
|
|
18860
19878
|
statusProvider,
|
|
18861
|
-
workspaceProvidersStatusProvider: createWorkspaceProvidersStatusProvider(
|
|
19879
|
+
workspaceProvidersStatusProvider: createWorkspaceProvidersStatusProvider(
|
|
19880
|
+
primaryEffectiveEnv ? { env: primaryEffectiveEnv } : {}
|
|
19881
|
+
),
|
|
18862
19882
|
workspaceSkillsStatusProvider: createWorkspaceSkillsStatusProvider(),
|
|
18863
19883
|
isChannelLive: /* @__PURE__ */ __name(() => bridge.isChannelLive(), "isChannelLive"),
|
|
18864
19884
|
persistDisabledTools: deps.persistDisabledTools ?? (async () => {
|
|
@@ -18883,7 +19903,10 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18883
19903
|
workspaceCwd: boundWorkspace,
|
|
18884
19904
|
primary: true,
|
|
18885
19905
|
trusted: deps.primaryWorkspaceTrusted ?? false,
|
|
18886
|
-
env:
|
|
19906
|
+
env: primaryRuntimeEnvMetadata ?? {
|
|
19907
|
+
mode: "parent-process",
|
|
19908
|
+
overlayKeys: []
|
|
19909
|
+
},
|
|
18887
19910
|
bridge,
|
|
18888
19911
|
workspaceService: workspace,
|
|
18889
19912
|
routeFileSystemFactory: fsFactory,
|
|
@@ -18949,6 +19972,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18949
19972
|
opts,
|
|
18950
19973
|
boundWorkspace: primaryBoundWorkspace,
|
|
18951
19974
|
bridge: primaryBridge,
|
|
19975
|
+
workspaceRegistry,
|
|
18952
19976
|
workspace: primaryWorkspace,
|
|
18953
19977
|
daemonLog,
|
|
18954
19978
|
startup: deps.startup,
|
|
@@ -18962,14 +19986,18 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
18962
19986
|
sessionShellCommandEnabled,
|
|
18963
19987
|
getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot,
|
|
18964
19988
|
getPerfSnapshot: deps.getPerfSnapshot,
|
|
18965
|
-
getMetricsSeries: deps.getMetricsSeries
|
|
19989
|
+
getMetricsSeries: deps.getMetricsSeries,
|
|
19990
|
+
getTotalSessionAdmissionSnapshot: deps.getTotalSessionAdmissionSnapshot ?? totalSessionAdmission?.snapshot
|
|
18966
19991
|
});
|
|
18967
19992
|
registerCapabilitiesRoutes(app, {
|
|
18968
19993
|
qwenCodeVersion: deps.qwenCodeVersion,
|
|
18969
19994
|
mode: opts.mode,
|
|
18970
19995
|
currentServeFeatures,
|
|
18971
19996
|
boundWorkspace: primaryBoundWorkspace,
|
|
19997
|
+
workspaceRegistry,
|
|
18972
19998
|
permissionPolicy: primaryBridge.permissionPolicy,
|
|
19999
|
+
maxSessionsPerWorkspace: opts.maxSessions,
|
|
20000
|
+
maxTotalSessions: opts.maxTotalSessions,
|
|
18973
20001
|
maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
|
|
18974
20002
|
languageCodes
|
|
18975
20003
|
});
|
|
@@ -19080,6 +20108,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
19080
20108
|
boundWorkspace: primaryBoundWorkspace,
|
|
19081
20109
|
mutate,
|
|
19082
20110
|
safeBody,
|
|
20111
|
+
env: getRuntimeEffectiveEnv(primaryRuntime.env),
|
|
19083
20112
|
// UI-server discovery uses the daemon's workspace MCP status, which
|
|
19084
20113
|
// includes servers registered at runtime.
|
|
19085
20114
|
getMcpServers: /* @__PURE__ */ __name(async () => {
|
|
@@ -19100,6 +20129,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
19100
20129
|
registerSessionRoutes(app, {
|
|
19101
20130
|
boundWorkspace: primaryBoundWorkspace,
|
|
19102
20131
|
bridge: primaryBridge,
|
|
20132
|
+
workspaceRegistry,
|
|
19103
20133
|
archiveCoordinator,
|
|
19104
20134
|
mutate,
|
|
19105
20135
|
sendBridgeError: sendBridgeError2,
|
|
@@ -19137,16 +20167,49 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
19137
20167
|
registerScheduledTasksRoutes(app, {
|
|
19138
20168
|
boundWorkspace: primaryBoundWorkspace,
|
|
19139
20169
|
mutate,
|
|
19140
|
-
safeBody
|
|
20170
|
+
safeBody,
|
|
20171
|
+
bridge: deps.manageScheduledTaskSessions ? bridge : void 0
|
|
19141
20172
|
});
|
|
19142
20173
|
registerUsageStatsRoutes(app);
|
|
20174
|
+
if (deps.manageScheduledTaskSessions) {
|
|
20175
|
+
const idleTimeoutMs = opts.sessionIdleTimeoutMs ?? DEFAULT_SESSION_IDLE_TIMEOUT_MS;
|
|
20176
|
+
const keepalive = startScheduledTaskKeepalive({
|
|
20177
|
+
bridge,
|
|
20178
|
+
boundWorkspace,
|
|
20179
|
+
intervalMs: computeKeepaliveIntervalMs(idleTimeoutMs)
|
|
20180
|
+
});
|
|
20181
|
+
app.locals.stopScheduledTaskKeepalive = keepalive.stop;
|
|
20182
|
+
void rehydrateScheduledTaskSessions({
|
|
20183
|
+
bridge,
|
|
20184
|
+
boundWorkspace,
|
|
20185
|
+
onError: /* @__PURE__ */ __name((sessionId, err) => {
|
|
20186
|
+
process.stderr.write(
|
|
20187
|
+
`qwen serve: failed to rehydrate scheduled-task session ${sessionId}: ${err instanceof Error ? err.message : String(err)}
|
|
20188
|
+
`
|
|
20189
|
+
);
|
|
20190
|
+
}, "onError")
|
|
20191
|
+
// Outer catch is defense-in-depth: rehydrateScheduledTaskSessions already
|
|
20192
|
+
// catches readCronTasks failures and per-session load errors internally
|
|
20193
|
+
// (returning { loaded, failed }), so this only guards an unexpected throw
|
|
20194
|
+
// from the function entry itself. Log rather than swallow it — a silent
|
|
20195
|
+
// failure here leaves every bound task dormant with no diagnostic.
|
|
20196
|
+
}).catch((err) => {
|
|
20197
|
+
process.stderr.write(
|
|
20198
|
+
`qwen serve: unexpected scheduled-task rehydration failure: ${err instanceof Error ? err.message : String(err)}
|
|
20199
|
+
`
|
|
20200
|
+
);
|
|
20201
|
+
});
|
|
20202
|
+
}
|
|
19143
20203
|
registerPermissionRoutes(app, {
|
|
19144
20204
|
bridge: primaryBridge,
|
|
20205
|
+
workspaceRegistry,
|
|
20206
|
+
daemonLog,
|
|
19145
20207
|
mutate,
|
|
19146
20208
|
sendPermissionVoteError: sendPermissionVoteError2
|
|
19147
20209
|
});
|
|
19148
20210
|
registerSseEventsRoutes(app, {
|
|
19149
20211
|
bridge: primaryBridge,
|
|
20212
|
+
workspaceRegistry,
|
|
19150
20213
|
daemonLog,
|
|
19151
20214
|
writerIdleTimeoutMs: opts.writerIdleTimeoutMs,
|
|
19152
20215
|
sendBridgeError: sendBridgeError2
|
|
@@ -19189,7 +20252,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
19189
20252
|
extraWsRoutes: [
|
|
19190
20253
|
{
|
|
19191
20254
|
path: "/voice/stream",
|
|
19192
|
-
onConnection: createVoiceWsConnectionHandler(primaryBoundWorkspace
|
|
20255
|
+
onConnection: createVoiceWsConnectionHandler(primaryBoundWorkspace, {
|
|
20256
|
+
env: getRuntimeEffectiveEnv(primaryRuntime.env)
|
|
20257
|
+
})
|
|
19193
20258
|
}
|
|
19194
20259
|
]
|
|
19195
20260
|
});
|
|
@@ -19209,6 +20274,7 @@ __name(createServeApp, "createServeApp");
|
|
|
19209
20274
|
export {
|
|
19210
20275
|
InvalidCursorError,
|
|
19211
20276
|
PromptDeadlineExceededError,
|
|
20277
|
+
computeKeepaliveIntervalMs,
|
|
19212
20278
|
createDefaultFsAuditEmit,
|
|
19213
20279
|
createServeApp,
|
|
19214
20280
|
detectFromLoopback,
|