qwen-code 多工作区制品归属:/workspaces/:workspace 限定路由与 Web Shell e2e 验证
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
导读
本文围绕 qwen-code 仓库中的端到端验证文档 .qwen/e2e-tests/2026-08-04-secondary-artifact-workspace-ownership.md 展开,系统梳理"次级工作区(secondary workspace)制品归属"这一多工作区能力的设计目标、三组核心验证场景、源码级路由实现以及最终的自动化与生产环境双重验证结果。读完本文,你将掌握qwen serve多--workspace注册下/workspaces/:workspace前缀限定路由的工作原理,理解制品面板与定时任务如何按工作区隔离归属,并了解如何用"同路径哨兵文件"与"隔离定时任务夹具"完成一次可复现的多工作区 e2e 验证。
一、问题背景:多工作区下的制品归属缺失
qwen-code 的qwen serve自 0.21.3 起支持重复--workspace注册,使单个 daemon 可同时托管多个工作区,并通过 Web Shell 对外提供服务(基线信息见 .qwen/e2e-tests/2026-08-04-secondary-artifact-workspace-ownership.md#baseline)。但在该 e2e 任务开展前,组件行为是"失败优先(failure-first)"的,存在两个明确的归属缺陷:
- 制品面板无归属所有者:当一个制品标签页无法确定它属于哪个工作区时,会退回到根工作区(primary workspace)的动作与路由,导致次级工作区中打开的制品可能被错误地以主工作区身份读写。
- 定时任务详情缺少次级
workspaceId:调度任务明细没有携带次级工作区标识,前端无法把该任务路由到其真正所属的工作区,跨工作区管理调度任务(不止主工作区,而是每个已注册项目)的能力不完整。
这两点正是本次 e2e 验证要修复并锁定的核心目标:让"次级会话的制品"与"次级工作区的定时任务"严格走自己的工作区路由,任何归属不明或归属丢失的情况都必须失败关闭(fail-closed),而不是静默降级到主工作区。
二、验证方案:本地双工作区搭建
验证的第一步是构造一个完全隔离、可对照的双工作区环境。文档给出了四个步骤:
- 创建相互隔离的主/次临时工作区:两个工作区在磁盘上完全独立。
- 在两个工作区放置同名相对路径哨兵文件、但内容不同:例如
artifact-owner.txt,主工作区写入PRIMARY_WORKSPACE_SENTINEL_8494,次级工作区写入SECONDARY_WORKSPACE_SENTINEL_8494。用同路径不同内容的方式,可以精确检验路由是否把请求送达了正确的工作区——只要返回字节不属于当前工作区,就能立刻暴露路由泄漏。 - 为两个运行时创建各自独立的持久化定时任务夹具(distinct durable scheduled-task fixtures):主/次各有一个独立的 cron 任务,用于验证任务 CRUD 是否只影响各自工作区。
- 以两个
--workspace标志启动生产构建的dist/cli.js serve,监听 loopback,并挂载生产 Web Shell 产物:
node dist/cli.js serve \ --workspace /path/to/primary \ --workspace /path/to/secondary \ --port <loopback-port>注意:本仓库为只读环境,以上命令仅用于说明可复现验证的启动方式;实际执行需在本地检出中完成。
三、场景一:次级工作区文件操作
操作:打开一个次级会话的制品(artifact),并下载/预览其中的哨兵文件。
预期:
- 请求必须携带工作区限定前缀,即目标是
/workspaces/<secondary>/file...,而不是根路由/file。 - 返回的字节必须是次级哨兵内容,永远不能是主工作区的哨兵。
这一预期的实现落点在 packages/cli/src/serve/routes/workspace-file-read.ts 中。从源码可见,registerWorkspaceQualifiedFileReadRoutes注册了一整组工作区限定读路由:
app.get('/workspaces/:workspace/file', ...); // 读取文件内容 app.get('/workspaces/:workspace/file/bytes', ...); // 读取文件原始字节 app.get('/workspaces/:workspace/stat', ...); // 文件状态 app.get('/workspaces/:workspace/list', ...); // 目录列表 app.get('/workspaces/:workspace/glob', ...); // glob 匹配每个请求首先通过resolveWorkspaceRuntimeFromParam把路径参数:workspace(工作区 id 或绝对路径)解析为已注册运行时,解析失败即短路返回,随后通过setWorkspaceRouteContext把运行时绑定到本次请求上下文,再进入真正的文件处理逻辑。配套的写路由在 packages/cli/src/serve/routes/workspace-file-write.ts,同样以/workspaces/:workspace/file/write、/file/edit、/file/upload的限定前缀形式注册。
而路径前缀的解析/保护逻辑集中在 packages/cli/src/serve/acp-http/index.ts,其中定义了PLURAL_WS_PREFIX = '/workspaces/',并实现了对/workspaces/<selector><suffix>的匹配与选择器提取,同时显式拒绝%2e%2e之类的编码穿越(如/workspaces/%2e%2e/acp这类会塌缩到/acp、从而静默绑定到主挂载点的路径)——这正是"次级文件永不落入主工作区"的底层保障之一。
四、场景二:次级工作区定时任务
操作:打开次级工作区的持久化定时任务,依次执行切换启用状态、编辑、删除测试副本。
预期:
- 每一次请求都必须指向
/workspaces/<secondary>/scheduled-tasks...。 - 主工作区的任务与任务文件必须保持原样,不受任何影响。
该能力的后端实现在 packages/cli/src/serve/routes/scheduled-tasks.ts 的registerWorkspaceQualifiedScheduledTasksRoutes:它以/workspaces/:workspace为前缀复用registerScheduledTaskCrudRoutes,并通过resolveWorkspaceRuntimeWithLiveCompatibilityFromParam解析目标运行时,随后调用requireTrustedWorkspaceRuntime执行"必须为受信任工作区"的同一门禁,最后把任务目标指向该工作区的 cron 文件与(启用会话管理时的)bridge,从而让多工作区 Web Shell 可以管理每个已注册项目的调度,而不只是主工作区。文档注释也明确写着:任何读写前都要求该工作区受信任,这一门禁与其他限定路由一致。
前端侧,定时任务的面板与 CRUD 交互位于 packages/web-shell/client/components/dialogs/ScheduledTasksDialog.tsx,它通过@qwen-code/web-shell/daemon-react-sdk的useWorkspaceActions拿到按工作区隔离的动作集合,并基于DaemonWorkspaceCapability等类型感知当前工作区能力,从而把"次级任务"与"主任务"在 UI 上区分开。
五、场景三:归属丢失(fail-closed)
操作:保持一个制品标签页打开,在延迟读取尚未返回时,通过能力夹具(capability fixture)将次级工作区移除或标记为不可用。
预期:
- 面板显示本地化的"工作区不可用"(workspace-unavailable)状态。
- 延迟到达的响应被直接忽略。
- 不会有任何请求被重试到主工作区路由上——即绝不静默降级。
这是三条预期中最关键的一条,它定义了归属丢失时的失败语义:与其把请求错误地落到主工作区造成数据污染,不如放弃本次响应并向用户明确展示工作区已不可用。验证文档同时要求,生产构建的 Web Shell 产物中必须包含新的 stale-owner(陈旧所有者)守卫逻辑,并且GET /capabilities必须分别广播主/次两个运行时的独立 id(见 packages/cli/src/serve/capabilities.ts 中按工作区切分的能力声明结构),前端才能依据能力变化判断"我打开的这个制品所属工作区是否还在"。
六、自动化验证结果
验证文档记录了 2026-08-04 的完整结果,结论为PASS,分为三层:
6.1 Failure-first 预演(3 个预期失败)
在修复前先跑一轮,捕获 3 个预期失败与 29 个既有通过用例,精确复现了三类缺陷:
- 主哨兵泄漏到了无所有者的制品标签页(即归属缺失时误用根工作区路由);
- 次级限定客户端未被使用(qualified client 没有接入次级会话);
- 定时任务列表缺少
workspaceId字段。
6.2 聚焦归属套件(413/413 通过)
修复后的聚焦测试覆盖了六处受影响测试文件(解析器、turn 输出、制品面板、嵌套 subagent、应用标签页传播等),413 个用例全部通过;全量 Web Shell 套件为167 个文件、2,777 个测试全部通过;Web Shell lint 与 TypeScript typecheck、包级 Web Shell 构建、仓库根生产构建均通过;改动文件通过 Prettier(包级格式检查仍报告 5 个未改动的基线文件:BranchPickerPopover.module.css、GitModePopover.module.css、GitDialog.module.css、PlanExecutionView.module.css、index.html,与本次改动无关)。
七、生产双工作区验证
自动化之外,文档还记录了在生产构建上的真实双工作区验证——用构建后的 CLI 以两个受信任工作区加载复制出的生产 bundle:
GET /capabilities广播了主、次两个不同的运行时 id,且产物中的 JS 包含新的 stale-owner 守卫。- 同路径哨兵对照:
GET /file?path=artifact-owner.txt返回PRIMARY_WORKSPACE_SENTINEL_8494(SHA-256818d5f4f…);GET /workspaces/<secondary-id>/file?path=artifact-owner.txt返回SECONDARY_WORKSPACE_SENTINEL_8494(SHA-2560e9fac79…);次级字节路由只返回次级哨兵字节。主/次哈希不同,直接证明两条路由落到了不同的磁盘目录。 - 持久任务 CRUD 隔离:次级任务的 list/update/delete 全部走
/workspaces/<secondary-id>/scheduled-tasks...;更新只改变次级任务的名称与启用状态,主任务保持存在、启用且未变更;删除次级任务后只有次级列表被清空,主任务仍在;验证完毕后两个测试任务均被清理。 - daemon 请求日志独立记录了限定的 file、bytes 以及 scheduled-task 的 POST/GET/PATCH/DELETE 路由及成功状态,作为第三方证据。
八、关于视觉证据的说明
按文档记录,本次验证中应用内浏览器运行时未报告可用的浏览器实例,因此没有截取到 UI 截图,也没有用合成截图替代——这是刻意为之的严谨做法:宁可缺失也不伪造证据。生产服务器对当前 Web Shell bundle 返回 HTTP 200,DOM 行为则由上述聚焦套件与全量套件覆盖。基于此,本文亦不额外插入任何与"次级工作区制品面板"相关的演示图片,避免引入无法核实的视觉材料。
九、总结
"Secondary artifact workspace ownership" 这次 e2e 验证为 qwen-code 的多工作区 Web Shell 钉住了三项可验证的行为契约:
- 次级制品严格走
/workspaces/<secondary>/file...路由,同路径哨兵文件 + SHA-256 哈希是防止"读错工作区"的最直观证据; - 次级定时任务 CRUD 严格走
/workspaces/<secondary>/scheduled-tasks...,主工作区任务零影响; - 归属丢失必须 fail-closed:显示本地化"工作区不可用"、忽略延迟响应、绝不重试到主路由。
对应实现可分别回溯到 packages/cli/src/serve/routes/workspace-file-read.ts、packages/cli/src/serve/routes/workspace-file-write.ts、packages/cli/src/serve/routes/scheduled-tasks.ts 与 packages/cli/src/serve/acp-http/index.ts 中的PLURAL_WS_PREFIX解析保护逻辑。对于希望自行复现或扩展该验证的读者,可直接以本文第二节的本地搭建步骤为起点,复用"双哨兵文件 + 隔离任务夹具 + 限定路由日志"三件套,在qwen serve的多工作区能力上继续加固。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考