ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

qwen-code VS Code Web Shell 全面接管方案:从混合 UI 到 workspace 级 daemon 会话的架构迁移

2026/9/12 19:51:33 拓冰建站 浏览量
qwen-code VS Code Web Shell 全面接管方案:从混合 UI 到 workspace 级 daemon 会话的架构迁移 qwen-code VS Code Web Shell 全面接管方案从混合 UI 到 workspace 级 daemon 会话的架构迁移【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文基于 qwen-code 仓库中的设计文档 2026-08-23-vscode-web-shell-cutover.md完整解读 VS Code 插件packages/vscode-ide-companion如何将聊天体验从「旧版消息时间线 WebUI 组件」的混合 UI全面迁移到由qwen-code/web-shell组件包负责的 Web Shell 会话。你将掌握为什么要在工作区级启动独立 daemon、WebShellWithProviders标准入口的挂载方式与 Props 契约、会话vscode来源标记的意义、双进程架构的取舍以及该迁移的验证门与完成标准。文中所有实现细节均有仓库源码佐证可直接作为阅读源码或评估同类架构迁移的参考。迁移背景混合 UI 的现状与问题在设计文档落笔之时PR #9719 已经用WebShellTranscript替换了旧版的 VS Code 消息时间线但插件的整体形态仍是「混合 UI」VS Code 插件侧仍有15 个生产源码文件导入qwen-code/webuicomposer输入框、补全菜单、权限抽屉、Ask User Question 对话框、会话选择器、头部、引导onboarding、图片预览、模型控制、图标、共享类型与工具函数仍全部来自packages/webuiWeb Shell 侧有69 个生产源码文件导入qwen-code/webui/daemon-react-sdkqwen-code/web-shell包把qwen-code/webui同时声明为 peer dependency 和 development dependency。更深层的矛盾在于运行时模型不匹配WebShellWithProviders期望的是 daemon 的 HTTP/SSE 运行时而当时的插件持有的是 ACP 连接并通过postMessage与 webview 交换消息。设计文档明确指出此前曾基于postMessage用 ACP bridge 重新实现 Web Shell 从 daemon 派生的各种状态transcript、流式输出、权限、提问、会话历史这种「对第二套协议重复实现」的做法正是问题持续回归的根源。核心决策一把会话运行在 workspace 级 daemon 上设计文档给出的第一个关键决策是放弃第二套协议的重复实现让会话直接运行在 workspace 级 daemon 上。具体分工如下插件扩展端extension host通过QwenDaemonProcess在回环端口上拉起qwen serve并将该 daemon 绑定到当前工作区扩展端把 daemon 的 base URL 交给 webviewwebview直接与 daemon 通信Web Shell 自身的会话、transcript、权限机制成为唯一实现不再有平行的一套状态机ACP 连接继续保留但职责收窄为认证状态与/auth流程不再承载 prompt。源码佐证扩展端如何拉起 daemon这一「受控 host 入口」在 qwenDaemonProcess.ts 中有完整实现。start()方法使用node:child_process.spawn启动进程参数组合为cliEntryPath serve --hostname 127.0.0.1 # 绑定回环地址 --port 0 # 使用操作系统分配的临时端口 --workspace workspaceCwd # 将 daemon 绑定到当前工作区 --no-web --require-auth # 即使回环也强制鉴权 --allow-origin *关键安全设计每进程随机 tokenrandomBytes(32).toString(hex)生成 64 位十六进制 token通过环境变量QWEN_SERVER_TOKEN传入子进程webview 侧则拿到baseUrl与token作为 Bearer 凭证避免任何匿名访问启动探测通过正则LISTENING_URL /qwen serve listening on (http:\/\/[^\s])/解析 stdout 中的监听地址并以 30 秒STARTUP_TIMEOUT_MS为启动超时stdio 处理启动完成后立即移除 stdout/stderr 监听器避免 daemon 持续日志导致内存累积生命周期daemon 随扩展 context 订阅一起dispose()。--require-auth的语义对应 CLI 侧serve.ts 对--require-auth的说明是即使在回环地址上也拒绝无 token 启动用于加固共享开发主机、CI runner、多租户工作站等场景——任何本地用户都可能访问 127.0.0.1 上的服务。它要求--token、QWEN_SERVER_TOKEN或--open-with-auth三者之一提供凭证启用后/health探针也需要携带 Authorization回环无豁免。这与插件侧「随机 token 环境变量传递」的方案正好配套qwen serve的默认回环模式是免鉴权的「可信模式」而插件显式开启--require-auth收紧了这一默认。值得明说的架构后果设计文档对该方案的影响做了非常坦诚的列举这些是理解架构取舍的关键每个工作区运行两个 Qwen 进程一个 ACP agent负责认证一个 daemon负责对话daemon 与 CLI、浏览器 Web Shell 共享同一工作区的 CLI 会话、浏览器会话与插件会话共用同一个 daemon 实例。因此插件创建的会话必须携带vscode来源类型source type历史列表只展示该来源的会话否则面板会把用户在终端里开启的对话也列出来单 daemon 绑定单工作区多根窗口multi-root在激活根变化时会重启 daemon。这一逻辑在 qwenDaemonProcess.ts 中有注释说明——复用同一个 daemon 服务不同 root会把所有会话、历史与 prompt 静默作用到第一个 root 上功能降级是显式决策由 ACP agent 事件驱动的轮次生命周期特性——编辑器标签页状态点tab status dot、长任务/注意通知、/insight进度上报——不再触发Web Shell 渲染自己的 insight 卡片。恢复标签点与通知需要 webview 向 host 上报 turn 与权限转换不在本次变更范围内。核心决策二使用 Web Shell 的标准入口而非定制组件第二个决策是插件通过Props 定制WebShellWithProviders而不是为嵌入场景专门做一个 bespoke 组件。文档列出的定制点包括composer 工具栏动作、仅 host 的斜杠命令条目、活动编辑器上下文注入、review-diff 与 insight-report 打开处理器、以及会话来源类型。daemon 无法提供的 VS Code 宿主外观view 头部、历史会话下拉、onboarding、账户对话框保留在扩展端并使用 VS Code token 主题化本地化则复用 Web Shell 的同一语言信号。Host 契约状态与动作的完整映射设计文档用一张表定义了宿主extension host与 Web Shell 之间的契约覆盖了现有全部能力宿主提供的状态发送给宿主的动作Active session 与会话摘要提交与取消 promptTranscript blocks 与流式状态创建或切换会话待处理的权限与提问请求响应权限请求或提问模型、审批模式、命令与技能切换模型或审批模式上下文用量、认证与账户状态请求补全与认证工作区文件与粘贴图片打开文件、diff、报告或外部链接契约由qwen-code/web-shell包拥有不新增 workspace 包也不引入另一套共享的Message[]模型——transcript 数据继续使用 canonical SDK 的DaemonTranscriptBlock[]契约。这一契约设计的直接收益嵌入方与 Web Shell 之间没有协议翻译层状态只存在一份。源码佐证EmbeddedApp 中的实际挂载EmbeddedApp.tsx 是契约落地的直接样例WebShellWithProviders baseUrl{runtime.baseUrl} token{runtime.token} clientId{runtime.clientId} lockWorkspaceCwd{runtime.workspaceCwd} sessionId{runtime.sessionId} classNameqwen-code-vscode-web-shell style{SHELL_STYLE} theme{theme} language{language} sessionSourceType{VSCODE_SESSION_SOURCE_TYPE} shellRef{shellRef} header{{ items: [] }} onSessionIdChange{(sessionId) { /* 通知 host 会话变化 */ }} onSessionInfoChange{({ sessionId, sessionName }) { /* 更新面板标题 */ }} /其中baseUrl/token/clientId来自QwenDaemonProcess启动后返回的 runtimelockWorkspaceCwd将 Web Shell 锁定到插件当前工作区路径隐藏其他工作区及添加/移除/选择入口sessionSourceType{VSCODE_SESSION_SOURCE_TYPE}即会话来源标记见下文onSessionIdChange回调会vscode.postMessage({ type: webShellSessionChanged, ... })通知扩展端并带有会话切换防抖与「拒绝上一会话的迟到更新」保护对应文档验证门中的 session-switch 测试。会话来源标记vscodesessionSource.ts 定义并解释了该常量export const VSCODE_SESSION_SOURCE_TYPE vscode;注释说明daemon 是共享的——CLI、浏览器 Web Shell 与本插件都对着同一工作区的同一个qwen serve实例通信而 Web Shell 默认会给每个来源记录default。若不区分VS Code 通道将与终端或浏览器会话无法区分面板历史就会列出用户从未在本插件中开启的对话。vscode来源标记是「面板只显示本插件会话」这一完成标准的实现基石。核心决策三让 Web Shell 自包含文档第三个决策是把目前位于packages/webui/src/daemon下的 daemon React providers 与 hooks属于 Web Shell 运行时集成的部分迁移到packages/web-shell包内Web Shell 从此不再 import 或声明qwen-code/webui依赖。若低层 provider API 仍需对外的 Web Shell 嵌入方可用则从 Web Shell 的子路径导出不新建包内置完整 provider 的WebShellWithProviders入口保持为推荐的 daemon 集成方式。这与 packages/web-shell/README.md 中「WebShell 提供两种接入形态」的描述一致独立接入自带DaemonWorkspaceProviderDaemonSessionProvider与共享 Provider 接入纯消费者宿主自行提供 Provider。当前仓库的packages/目录中已不存在webui包说明随后的依赖清理 PR 已经完成落地。WebShellWithProviders 的关键 Props来自包 README嵌入方包括本插件实际可用的 Provider 配置项如下属性类型说明baseUrlstringdaemon API 地址未传时使用window.location.origintokenstringdaemon API Bearer tokensessionIdstring要连接的 session id未传或undefined时保持空页面workspaceIdstring已注册工作区 id主要用于定位已有 session不会注册或锁定工作区workspaceCwdstring已注册工作区路径语义同workspaceId优先于workspaceIdsessionContextDaemonProductSessionContext显式产品上下文standalone 或 Live 上下文不能同时传workspaceId/workspaceCwd/lockWorkspaceCwdlockWorkspaceCwdstring锁定到指定工作区路径未注册时自动持久注册并隐藏其他工作区及添加、移除和选择入口restartSseOnPromptboolean每次 prompt 被 daemon 接收后重建存活 SSE 流流断开时提交 prompt 总会立即重建默认关闭对应WebShell组件还有theme默认dark、languageen | zh-CN | zh | zh-cn、onSessionIdChange、onSessionCreated最长阻塞等待 30 秒、onSlashCommand返回true时宿主接管等 Props插件迁移后可继续沿用这些能力。核心决策四同一变更内删除被替换的 VS Code 代码文档强调只要两套实现同时存在cutover 就不算完成。因此当受控的 Web Shell 表面接管一个能力时对应的插件组件、hook、状态分支、兼容性 re-export、样式 import 与测试必须在同一个 PR 内删除。插件可以保留的代码仅限真正的 VS Code 宿主能力例如打开文件展示原生 diff读取活动编辑器工作区文件搜索剪贴板集成扩展生命周期管理。范围必须验证的用户流程设计文档为本次 PR 圈定了完整的验证范围覆盖从引导到收尾的端到端流程引导onboarding与已认证空状态会话的启动、取消与恢复流式 assistant 文本与可展开的思考thought内容全部 tool-call 状态与 plan 渲染Bash/Edit 权限请求包括每一种响应选项Ask User Question包括多问题回答历史打开、会话选择、新会话与晚到帧隔离late-frame isolationcomposer 输入、斜杠命令、文件补全、技能、图片与活动编辑器上下文审批模式、模型选择、思考模式、上下文用量与账户访问复制动作、文件链接、报告链接与 VS Code diff 动作错误、取消、认证与重连状态。其中「最新用户消息保持可编辑」是一个容易被忽略的细节宿主把选中的 transcript 轮次映射为 daemon 的 rewind 快照先回卷会话再重新提交。显式声明的不在范围内为避免范围蔓延文档明确列出本次不做的事项完全移除 ACP 连接它仍负责认证恢复标签状态点、完成通知与 host 侧基于 daemon turn 事件的/insight进度迁移桌面端特定导航或原生窗口外观创建qwen-code/chat-panel、另一个 UI 包或平行的消息模型删除packages/webui那是后续依赖清理 PR 的事删除packages/desktop/apps/webui它是独立命名的桌面应用不是本次针对的旧共享包。实施顺序与验证门PR 内的实施顺序同属一个评审单元不是多个 PR把 daemon React 层迁移到 Web Shell并更新其内部 imports、构建别名、公共入口、测试与 README从扩展端拉起 workspace 级 daemon并用 base URL、token、client id 引导 webview让 webview 基于该 daemon 挂载WebShellWithProviders添加插件所需 Props含会话来源类型删除被替换的插件组件、hooks、兼容工具、WebUI 样式、Tailwind preset 用法、包依赖与 bundler 例外添加import gate证明packages/web-shell与packages/vscode-ide-companion都不再依赖qwen-code/webui。自动化验证门Web Shell 的单元测试、DOM 测试、typecheck、lint 与库构建检查VS Code 插件的单元测试、typecheck、lint 与生产 bundle 检查每一个 host 状态/动作映射的契约测试会话切换测试拒绝属于上一会话的更新对应EmbeddedApp中switchingSessionId防抖与晚到帧隔离仓库级检查拒绝 Web Shell 与 VS Code 中新增的 WebUI import。真实 VS Code UI E2E 方法论文档要求用一个真实 VS Code Extension Development Host 稳定测试 profile一次性跑完全部矩阵而不是每个用例重启一次 VS Code专用 profile 避免污染用户正常的编辑器进程必须能跳过 onboarding且不要求 GitHub 或 Copilot 登录profile 的扩展状态必须在各用例间保持。需要截图的 9 类场景明暗主题 transcript 对比、带文件/斜杠命令/技能/图片附件的 composer、权限请求响应前后、Ask User Question 提交前后、历史选择器与会话切换、五态 tool callpending/running/completed/failed/cancelled、流式思考与取消、模型与审批模式控件、认证/空/错误/重连状态。测试报告还必须区分「仅 WebView 内断言」与「端到端观察到 VS Code host 或 daemon 副作用」的动作。完成标准Completion Criteria最后文档给出了可客观判定的完成标准VS Code 为整个聊天流程挂载 Web Shell插件创建的会话可归属到vscode来源且面板历史只列出这些会话Web Shell 与 VS Code 不含任何qwen-code/webui的生产 importqwen-code/web-shell包对qwen-code/webui没有任何 peer、development、build 或 Vite 依赖被替换的插件 UI 与交互代码已删除全部列出的真实宿主 E2E 流程都有断言与截图证据仓库中剩余的packages/webui引用已在依赖删除 PR 中逐项枚举。总结与延伸阅读这份设计文档的价值在于它示范了一次彻底的 UI 能力迁移应当如何做先统一运行时让会话跑在 daemon 上消除第二套协议的状态重实现再统一入口用标准WebShellWithProviders加 Props 定制取代定制组件最后用 import gate 与删除动作保证「没有两套实现并存」并用来源标记解决共享 daemon 下的会话归属问题。仓库中可继续深入的路径设计文档原文docs/design/2026-08-23-vscode-web-shell-cutover.mddaemon 拉起的完整实现qwenDaemonProcess.tswebview 挂载与 host 契约落地EmbeddedApp.tsx、sessionSource.tsqwen serve的--require-auth/--workspace/--port 0参数语义serve.tsWeb Shell 包的接入方式、Props 全表与架构说明packages/web-shell/README.md【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考