如何给 Qwen Code 桌面客户端换品牌:从 brand.json 到安装包
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
你手上有一个 AI 产品,想出一个带自己 logo、自己名字的桌面客户端,而不是一个 Qwen Code 的换皮版。好消息是 Qwen Code 仓库内置了一条完整通路:桌面壳换到 Tauri 底座之后,packages/desktop-shell/ 成为当前唯一的桌面实现,品牌化构建的挂载点只有三处——src-tauri/tauri.conf.json、src-tauri/icons/图标目录,以及bootstrap/启动 UI。一条 brand-create 品牌构建脚本负责把这三处一次性换掉,你最终拿到的是一个白标桌面应用安装包。
你只需要交两样东西:brandId 和 logo
输入规则的设计目标是最少输入,脚本自己算,不问你。brandId和logo必填,website可选,appName、appId、artifactPrefix、updaterEndpoints、updaterPubkey、target 这些字段全都可以缺省——脚本会派生;你只想覆盖其中某几项时,也可以直接在配置里给值。技能文档对这一点有硬性要求:必填项不全时只允许询问一次,字段一旦齐全就直接进入构建,不追加任何确认步骤。
校验规则很简单:
brandId必须匹配^[a-z][a-z0-9-]*$,即小写字母开头,后接小写字母、数字、短横线logo是本地图片文件路径,必须真实存在,推荐 1024px 以上的方形 PNGwebsite可选,只影响 appId 的派生- 其余字段缺失时由脚本补齐,不需要你填
默认值是怎么自动算出来的
脚本自己算,不问你。拿acme-ai走一遍完整推导:
appName:短横线切段、逐段首字母大写、空格连接 →Acme AIartifactPrefix:同一套规则,用短横线连接 →Acme-AIappId:取website的 host,剥掉www.前缀,把标签反转后追加.desktop,https://acme.ai→ai.acme.desktop
host 反转有一个隐含条件:反转前 host 至少要剩两段,否则不算"合法 host",直接落回退逻辑。website没给、格式不对、或者 host 只有一段时,appId回退为app.<brandId>.desktop,例如app.acme-ai.desktop。
还有一个小细节:脚本内置了一组常见缩写词(ai、api、cli、ide、sdk、ui、url),派生名称时整体大写而不是首字母大写——brandId: acme-cli得到的是Acme CLI,不是Acme Cli。
从零到安装包:一条命令链
整条工作流是线性的,按顺序走即可。先隔离克隆:每个品牌一个全新 clone,克隆到独立目录再开brand-<brandId>分支。
BUILD_ROOT="$PWD/brand-builds/acme-ai-$(date +%s)" git clone --branch main --single-branch \ https://gitcode.com/GitHub_Trending/qw/qwen-code "$BUILD_ROOT/qwen-code" git -C "$BUILD_ROOT/qwen-code" checkout -B brand-acme-ai origin/mainclone 或 checkout 失败就必须停在这里报告失败,绝不能假装分支已经创建而继续往下走。在构建目录里写一份最小的 brand.json(logo 用绝对路径):
{ "brandId": "acme-ai", "logo": "/abs/path/to/logo.png", "website": "https://acme.ai" }装依赖要装两层:根部的npm install(build:runtime会调用根部的cross-env、esbuild 等 devDependencies),加上 desktop-shell 自己的依赖:
npm install cd packages/desktop-shell npm install --workspaces=false最后运行 brand-create.mjs:这是一个不需要装任何第三方包的 Node 脚本(Node 18 以上即可)。技能文档明确要求:内置脚本可用时,不要手工去编辑tauri.conf.json、图标文件或 bootstrap 品牌字符串,脚本才是补丁配置与生成资源的唯一权威来源。
node packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs \ --shell-root /abs/path/to/qwen-code/packages/desktop-shell \ --config /abs/path/to/brand.json跑完输出一份 JSON 报告:brandId、appName、appId、artifactPrefix、updaterEndpoints,外加被补丁的 tauriConfig 路径、图标生成结果和 bootstrap 文件列表。
脚本改了哪三处文件
按"用户最先看到什么"的顺序讲,先说换肤。
第一步:bootstrap 启动 UI。脚本把你的 logo 复制为bootstrap/brand-logo<ext>,然后把bootstrap/index.html和bootstrap/bootstrap.js里所有Qwen Code字面量替换成品牌名——页面标题、启动大标题、"Starting …" 文案,以及qwen-code-logo.svg的引用换成品牌 logo 文件名。为什么这样设计:启动 UI 是用户打开应用第一眼看到的东西,而品牌字符串就硬编码在这些字面量里,整体替换最可靠。
第二步:图标全家桶。脚本用 Tauri CLI 的icon命令从一张 logo 重新生成全部尺寸和平台格式,覆盖src-tauri/icons/。CLI 跑不起来时的回退是:logo 为 PNG 就复制成icons/icon.png并警告"其余尺寸仍是旧 logo";非 PNG 则一个图标都不动,提示转成 PNG 重试。
第三步:tauri.conf.json 配置。改四个字段:productName(应用名)、identifier(bundle identifier,应用在各平台的唯一身份)、bundle.shortDescription,以及plugins.updater.endpoints。为什么这样设计:这四个字段分别决定应用显示名、系统层面的身份标识、商店/描述文案,以及应用从哪里检查更新。
几个值得留意的设计细节:
- 这个脚本一次只能跑一次。它靠检测
productName是否还是默认值Qwen Code Desktop来判断是否首次运行,已打过补丁的克隆会被直接拒绝——bootstrap 补丁依赖原始字面量,重跑会把品牌字符串重复拼接进去 - 品牌方给了更新源配置、但目标
tauri.conf.json里根本没有 updater 段时,脚本会直接报错拦下来,不偷偷跳过——否则等于悄悄丢弃你校验过的更新配置,交付一个永远无法更新的应用;清空更新端点时同步把bundle.createUpdaterArtifacts置 false、plugins.updater.pubkey置空字符串而不是删除字段(schema 要求该字段是 String 且无默认值,删了启动即反序列化失败,而端点为空时空字符串无害) - 生成图标时 logo 路径以普通命令行参数传递、不经过任何命令解释器,路径里就算有
$()或反引号也不会被 shell 执行
换个平台打包:交叉编译 Tauri 应用最容易翻车
默认按宿主平台打包:npm run build:runtime --workspaces=false然后npx tauri build。要交叉编译时,问题集中在build:runtime上。prepare-runtime.js 按QWEN_DESKTOP_TARGET环境变量(不设置时取宿主平台)下载对应平台的 Node.js 运行时并捆绑进包。取值会被归一化为五种受支持目标:aarch64-apple-darwin→darwin-arm64、x86_64-apple-darwin→darwin-x64、aarch64-unknown-linux-gnu→linux-arm64、x86_64-unknown-linux-gnu→linux-x64、x86_64-pc-windows-msvc→win32-x64,不在其中的直接抛错。
⚠️目标平台与宿主不同时,必须在每次tauri build --target之前带着环境变量重跑build:runtime:
QWEN_DESKTOP_TARGET=aarch64-apple-darwin npm run build:runtime --workspaces=false npx tauri build --target aarch64-apple-darwin不重跑的代价:产物里内嵌的是宿主架构的 Node 二进制,用户一打开就是 exec format error(操作系统拒绝执行架构不符的二进制)。target: all时逐目标执行"build:runtime → tauri build",且只跑当前机器或 CI 环境支持的目标——文件真实存在才算产出,别声称造出了跑不了的包。产物统一落在src-tauri/target/下,按平台进dmg/、nsis/、appimage/、deb/四个 bundle 子目录(DMG 是 macOS 光盘镜像,AppImage 是 Linux 免安装单文件)。
想让品牌客户端能收到更新:桌面应用更新签名必须用自己的密钥
品牌构建默认不签名。上游发布流水线的签名私钥与更新密钥只属于官方 Qwen Code 发布;品牌方要发签名版或应用内更新,必须自建独立密钥对加独立更新源,绝不复用上游的。密钥对一行命令生成:
npx @tauri-apps/cli signer generate -w ~/.tauri/my-brand.key.key是私钥(配进构建 CI 的TAURI_SIGNING_PRIVATE_KEY),配套.pub里的 base64 公钥填进 brand.json 的updaterPubkey。
公钥和私钥必须配对。updater 插件用这把公钥校验每次更新的签名,所以脚本在加载配置时强制校验:updaterEndpoints非空而没有updaterPubkey直接报错——配错或不配,每次更新检查都会校验失败,应用将永远无法更新。方向反过来同样成立:品牌构建与官方更新源各走各的更新通道,updaterEndpoints默认空数组意味着品牌包永不轮询官方源,官方源也不会更新品牌包。另外注意:appName不能写成Qwen Code Desktop,加载配置时就会拒绝——"一次只能跑一次"的判断依赖productName偏离默认值,名字撞了默认值,守卫就失效了。
打包完,先别急着交付
验证按四步走:
- 确认产物落在
src-tauri/target/release/bundle/(交叉编译时是src-tauri/target/<triple>/release/bundle/),平台对应dmg/、nsis/、appimage/、deb/ - 对每个产物计算 SHA-256(
sha256sum,macOS 用shasum -a 256) - macOS 上对生成的 DMG 执行
hdiutil verify - 把产物路径、SHA-256、应用名、appId 和构建目录一起报告给对方
常见翻车点速查:
| 场景 | 处理 |
|---|---|
brandId不合法 | 摆出正则^[a-z][a-z0-9-]*$,请用户改 |
| logo 缺失或不是图片 | 请用户给一个存在的本地图片路径 |
| brand-create.mjs 不存在 | 报告脚本缺失,附上预期命令 |
| shell-root 已品牌化 | 脚本会拒绝运行,从全新克隆重来 |
两条铁律:
失败时不要删构建目录——它是事后排查的依据,留着它,只把最后几行有用报错和日志路径带回来。
禁止在同一克隆重跑 brand-create——脚本一次只能跑一次,配置错了就丢弃这个克隆、重新克隆重来。
一份 brand.json,加一个不需要装任何第三方包的 Node 脚本,就是 Qwen Code 桌面客户端品牌化构建的全部核心抽象:输入两样东西,输出三处补丁和一份可核验的安装包清单。下次"帮我做一个带自己 logo 的白标桌面应用"这种需求来的时候,你只需要把acme-ai换成自己的 brandId。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考