Tokenless Windows 集成
本文面向维护者,说明 Windows Adapter 的宿主契约和路径查找规则。 安装与日常使用见千问办公用户指南。
千问办公宿主契约
QwenWorkCN 1.3.2-26101704 在安装目录的
resources/agent-sdk/engine-runtime-manifest.json 中,通过 opencode 和 qwencode
的 env 项提供 QWENWORK_PLUGIN_HOOK_COMPATIBILITY=managed-tool-hooks-v1。
此标记用于识别在 Windows PowerShell 中执行的 Bash 事件。Adapter 读取宿主值,
安装器不应强制设置:旧版 Qoder CLI 会话使用 Git Bash,缺少此标记时需要保留 POSIX 改写。
Windows 安装器将每个命令 Hook 的 shell 设为 powershell。同一版本的千问办公在
resources/agent-sdk/opencode/qoderwork-plugin-adapter.mjs 中,将
configuration.shell ?? true 传给 Node 的 spawn。这段宿主代码负责选择执行 Python
Hook 命令的 PowerShell;前述 manifest 标记识别的是工具使用的 Shell。忽略 shell
字段的宿主尚未验证:生成的命令使用 PowerShell 语法,无法在 cmd.exe 下执行。
已验证的宿主会忽略普通 Hook 执行失败,保持工具输入不变;退出码 2 专用于阻断决定。
因此,Python 解释器或共享 Hook 资源缺失时,该次调用会失去优化,无需额外分发脚本。
PreTool 匹配 Bash、PowerShell、mcp__workspace__bash 和
mcp__qw-builtin__mcp_workspace_bash,由 RTK 决定每条命令是否改写。PowerShell 调用
绑定原生 RTK,并在该次调用内设置归属环境变量。PostTool 将 PowerShell 输出归类为
命令输出,保留文件内容保护,避免对 RTK 输出重复压缩。不支持的命令透传。
PowerShell 包装脚本在子作用域内设置 rtk 别名。在被改写的复合命令中,原本已有的
裸 rtk 调用也会绑定到托管二进制;POSIX 绑定只修改本次改写插入的调用。显式可执行
文件路径不受影响。包装脚本恢复归属环境变量后保留 $? 和 $LASTEXITCODE,包括
原生命令非零退出的状态,且不会新增 stderr 输出或 $Error 项。
当此包装脚本是 powershell -Command 或 pwsh -Command 执行的最后一条命令时,
$? 为 $false 时进程退出码为 1;原生退出码仍可通过会话内的 $LASTEXITCODE 获取。
生命周期脚本从 %LOCALAPPDATA%/Programs/QwenWorkCN 及其版本子目录查找应用。
TOKENLESS_QWENWORK_APP 覆盖安装目录,TOKENLESS_QWENWORK_HOME 覆盖数据目录
(默认 ~/.qwenworkcn)。插件复制到 plugins-custom/tokenless,Hook 记录 Python
解释器的绝对路径。安装器不修改 settings,也不启用已禁用插件;被修改及非自身管理的
插件不会被覆盖或删除。桌面应用的 PATH 需要包含 npm 命令目录。
原生 Hook 测试覆盖改写、执行、压缩、文件读取保护、统计、去重及安装生命周期。 可选的已安装宿主测试读取两个引擎的 manifest 环境,并调用已安装的 OpenCode Adapter 验证 Hook Shell 选择、改写、PostTool、Read 保护、统计和共享资源缺失时的透传行为。 这些测试不能证明某个应用会话 已加载或启用插件,仍需在千问办公本地会话中单独确认。
二进制查找与支持边界
原生二进制从当前可执行文件同级目录和 PATH 的绝对路径项中查找。与 Unix 不同,
Windows 查找不检查文件和目录的 ACL、所有者或重解析点,而是信任配置的安装目录和
PATH。RTK 只接受 .exe;一般依赖查找还接受 PATHEXT 中的 .com、.bat 和 .cmd 项。
Windows 支持原生 CLI 和千问办公 Adapter。npm 会复制其他 Adapter 资源,但跳过 Claude 自动启用;其他 Windows Adapter 尚未验证。不支持安全工作区/VM 会话和 Windows ARM64。
在 Windows 上构建和测试
在原生 PowerShell 中构建,需安装 Git、Rust 的 x86_64-pc-windows-msvc 工具链、
Visual Studio C++ Build Tools(含 Windows SDK)。打包需要 Node.js/npm 和 Python 3.10+,
千问办公 Adapter 同样使用 Python。不需要 Git Bash、WSL 或 make,不支持 Windows ARM64。
Linux CI 构建入口见下文。
cd src/tokenless
./scripts/build-windows.ps1
node npm/scripts/package-npm.js --target win32-x64
$packages = Get-ChildItem npm/dist/tokenless-win32-x64/*.tgz, npm/dist/tokenless/*.tgz
npm install -g $packages.FullName
构建脚本拉取并修补固定版本的 RTK,将两个可执行文件写入
target/npm-prebuilt/win32-x64。添加 -Debug 可构建开发版本。生成的 npm 包为本地
制品,此本地命令不会发布 Windows 软件包。
统一发布时,将 Windows 构建主机的完整 target/npm-prebuilt/win32-x64 目录复制到
Linux 打包主机 src/tokenless 下的同一位置。两台主机使用相同源码版本,备齐另外
四个平台目录后运行 make npm-package-all。--all 要求五个平台的制品齐全,缺少
任一二进制都会失败;--target x64 也包含 Windows。仅打包 Linux 或 macOS 时,
使用 --target linux 或 --target darwin。Linux CI builder 也会构建 Windows
二进制,因此生成产物不再必须使用原生 Windows 构建主机。
本地验证运行 cargo fmt --all -- --check、
cargo clippy --workspace --all-targets -- -D warnings、cargo test --workspace,
并在打包后运行 python -X utf8 -B tests/test_windows.py。测试 Python workspace
成员时,可通过 PYO3_PYTHON 指定 Python 可执行文件。
Windows smoke 测试调用真实 PowerShell Hook 和原生二进制;应用界面的插件加载与激活
需要另行在千问办公本地会话中验证。
将 TOKENLESS_TEST_QWENWORK_APP 指向包含 QwenWorkCN.exe 的已安装版本目录,
可用真实宿主 manifest 环境测试两个引擎的托管 Bash 及两个 MCP 别名,并通过 PATH
中的 Node 调用已安装的 OpenCode Hook 执行器。设置 TOKENLESS_TEST_BASH 为 Git Bash
可执行文件可加入旧版执行测试。数据库拒绝测试使用系统临时目录(Windows 为
%SystemRoot%/Temp,Unix 为 /tmp),不依赖可能位于用户目录中的 TEMP、TMP 或
TMPDIR;若系统临时目录不可写或处于受信用户目录内,测试会提示 fixture 不可用。
允许写入受信用户目录的行为由另一项独立测试覆盖。设置 TOKENLESS_TEST_POWERSHELL
为 pwsh.exe,可在 PowerShell 7 上重跑 Rust powershell_binding 测试,以及已安装
宿主 smoke 测试中改写后的工具命令;默认使用 Windows PowerShell 5.1。宿主仍按安装器
的 powershell 设置执行已注册的 Python Hook。
验证 npm 安装后的启动器时,将 TOKENLESS_TEST_BIN 指向已安装的
anolisa-tokenless/bin,并将 TOKENLESS_TEST_NODE 指向待验证的 Node 可执行文件
(包括最低支持版本 16.7.0)。
Linux CI 与发布产物
tokenless-prebuilt-ci.yaml 使用自部署 Linux 发布 runner
(anolisa-release-cross-x64)交叉编译 x86_64-pc-windows-msvc,并运行 Wine
冒烟测试,触发方式为 push 或手动调度;PR 在通用 CI runner 上运行 action 回归测试。
同一构建 action 进入正式发布和预览矩阵。指定历史源码 tag 的预览
继续使用该 tag 中的矩阵。
runner 必须预先具备 anolisa/rust-release-builder:windows-msvc-x86_64,配置摘要为
sha256:7649bcb55d8c5632232c7f27e3c2191b71fb7c31149de87412c447fc7a997303。
Docker 的 containerd 镜像存储返回对应清单摘要作为镜像 ID:
sha256:1392a2684a4853a3da0219caef07c4b47f8833775b1559f18d54cd656abefb88。
这两个固定标识对应同一个已验证的 builder。
action 在构建前校验镜像身份和镜像内 SDK 清单,不构建或拉取镜像;启用任务前须先部署。
冻结镜像包含 Rust 1.93.0、cargo-xwin 0.23.1、maturin 1.15.0、LLVM 18.1.3、
Wine 9.0,以及带导入库的 Windows Python 3.11.9。宿主需要已有发布 runner 工具,
包括 Docker、Node.js/npm 和 cargo-sbom 0.10.0。源码、Cargo 下载缓存、编译输出
与 Wine 状态分别挂载,不将宿主编译器 wrapper 和 Cargo 配置传入容器。
在仓库根目录运行正式构建入口,输出目录须为空。默认构建当前检出的提交,
指定 --tag 时构建该精确 tag:
root="$PWD"
mkdir -p /tmp/tokenless-windows-release
RUSTC_WRAPPER= RUSTC_WORKSPACE_WRAPPER= \
TOKENLESS_SOURCE_WORKTREE=/tmp/tokenless-windows-release/source \
bash .github/actions/build-tokenless-prebuilt/build.sh \
--source-repo "$root" \
--output-dir /tmp/tokenless-windows-release/artifacts \
--wheel-output-dir /tmp/tokenless-windows-release/wheels \
--version 0.9.0 --target-os windows --target-arch x86_64 \
--profile windows-msvc-x86_64
Windows ZIP 包含 tokenless.exe、rtk.exe、LICENSE 和 npm/ 下的两个 npm
tarball(仅面向 Windows 的根包与 win32-x64 平台包),附带与其他平台相同的
SHA-256 和 CycloneDX 文件。CLI 与 RTK 静态链接 CRT,并检查导入表,拒绝依赖
外部 VC++ runtime DLL。独立 Python wheel 使用 cp311-abi3-win_amd64,运行库
由 Windows Python 提供。
发布工作流将这些产物上传 GitHub,不发布到 npm 或 PyPI。准备跨平台 npm registry
发布时应合并全部五个平台,不应将仅 Windows 的根 tarball 作为跨平台根包发布。
重复构建目前存在 PE 构建标识差异;校验和标识本次生成的具体文件,尚不保证
字节级一致重建。
Wine 冒烟将 ZIP 和 wheel 解压到包含空格与中文的路径,验证 CLI、RTK、SDK、
压缩、Stash 字节一致恢复、越权检索拒绝和统计归属,同时检查 npm 元数据及内置
二进制。冒烟执行禁用网络并设置外层超时,编译或 Wine 失败都会使任务失败。
构建诊断保存在 <output-dir>.build/out,CI 任务会上传这些记录。
Wine 不验证原生 Windows ACL、PowerShell hook、Node 启动器或 QwenWork 激活。
这些验收仍需在 Windows 10 和 Windows 11 上运行前述原生测试。