Windows 下驱动微信开发者工具 CLI
何时用
- 需要
wechatide -c <client> <tool>做云函数部署 / 云库操作 / 预览上传 / 自动化 - 遇到「IDE 启动后立即退出」「
cli_unavailable」「GPU process isn't usable. Goodbye.」 - 路径含空格(
D:\Program Files (x86)\...)导致 CLI 调不起来
读法:先看 §0 按报错跳转;第一次上手按 §1 → §2 → §3 顺序走。
0. 报错速查(症状 → 去哪一节)
| 现象 / 报错 | 去看 |
|---|---|
cli_unavailable、'D:\Program' 不是内部或外部命令 |
§1.1(脚本自身没加引号的误报) |
启动后立即退出、GPU process isn't usable. Goodbye. |
§1.3 启动三条件 |
wait WechatIDE authorization timeout |
§1.3(CLI 无法自行拉起 GUI,必须先手启) |
'wechatide.cmd' 不是内部或外部命令 |
§6(cwd 必须是 IDE 安装目录) |
Unknown argument: i |
§6(--command 的值带空格被拆成 token) |
Expected ':' after property name in JSON |
§6(--patch 的引号被吃掉) |
Uncaught Invalid or unexpected token |
§6(--fn-source) |
Cannot find module 'wx-server-sdk' |
§3.2① 缺 --remote-npm-install |
Cannot find module './lib/x.js' |
§3.2② 子目录没补 inc_deploy |
-504002 functions execute fail |
§3.2(函数内部异常,多为缺模块) |
当前函数处于Creating状态 / 当前函数处于Updating状态 |
§3.3 |
FailedOperation.UpdateFunctionCode |
§3.3(不是失败,等 Active 重发) |
missing required arguments: --names |
§3.1 / §8 |
| 「服务未就绪」「兑换服务不可用」这类模糊前端文案 | 前端把错误吞了 → §5.2 读 console |
真机调试 Error: Timeout |
§7(虚拟支付必须走体验版) |
timeout waiting for automator response |
§5.6(模拟器/IDE 没在跑,或偶发) |
Cannot read properties of undefined (reading 'winId') |
§5.6(项目窗口没打开) |
no such element |
§5.6(先疑 wx:if 没命中,不是选择器写错) |
错误码 -504002 / -404011 / -501000 |
§8 错误码表 |
1. 环境准备
1.1 安装诊断(只在首次 / 调不起来时)
cd <wechatide-skill 根>
node skills/installer/scripts/check-installation.mjs --install-root "<安装目录>"
⚠️ 该脚本在 Windows 路径含空格时会误报 cli_unavailable。判据:返回的 error 里出现
'D:\Program' 不是内部或外部命令 —— 这是脚本自身未给路径加引号的 bug,不是 CLI 不可用。
正确做法:不信 error 字段,直接实测 CLI。
// 用 cmd /s /c + cwd=安装目录(避开引号地狱)
const { spawnSync } = require('child_process');
const cwd = 'D:/Program Files (x86)/Tencent/微信web开发者工具';
const r = spawnSync('cmd.exe', ['/d', '/s', '/c', 'wechatide.cmd -h'], {
cwd, encoding: 'buffer', timeout: 30000, windowsHide: true
});
// status=0 且 stdout 含「skill-cli 用法」→ CLI 可用
1.2 版本兼容判定
不要看 version 文件(那里是 NW.js 的版本号,会误导)。判据:
| 看什么 | 判据 |
|---|---|
resources/app.asar.unpacked/package.json 的 version |
与 skills/installer/scripts/install-root.mjs 的 MIN_COMPATIBLE_VERSION 比较 |
code/package.nw/package.json |
存在 = NW.js 旧版(不兼容);不存在 = Electron 版 |
1.3 启动 IDE(三个条件缺一不可)
CLI 无法自行拉起 IDE(会报 wait WechatIDE authorization timeout)。必须先手动启动 GUI:
const { spawn } = require('child_process');
const env = Object.assign({}, process.env);
delete env.ELECTRON_RUN_AS_NODE; // ① 必须清!否则 Electron 跑成纯 Node、GUI 直接退出
const c = spawn(exe, [
'--no-sandbox', // ② 必须加!否则 GPU 进程崩溃:
'--disable-gpu', // FATAL: GPU process isn't usable. Goodbye.
'--disable-software-rasterizer'
], { cwd: root, env, detached: true, stdio: 'ignore' });
c.unref();
③ 父进程必须常驻:detached + unref 仍会随工具调用结束被回收。可靠做法是写一个长驻脚本
(setInterval 保活),用 run_in_background: true 跑它,IDE 才活得下来:
// .ide-launch.js —— 用 run_in_background 启动
const c = spawn(exe, ['--no-sandbox','--disable-gpu','--disable-software-rasterizer'],
{ cwd: root, env, stdio: ['ignore', out, out] });
let n = 0;
const t = setInterval(() => { if (++n >= 3600) { clearInterval(t); process.exit(0); } }, 1000);
启动后验证:Get-Process | Where-Object { $_.ProcessName -like "*微信开发者工具*" } 应有多进程 + 主窗口。
1.4 本机 shell 限制(Windows + Git Bash)
ls/head/tail/dirname等 coreutils 不存在 → 用node -e、Grep 工具、或 PowerShell 工具- 禁止从 Bash 调
powershell(被安全策略拦),要用 PowerShell 工具 reg.exe被沙箱黑名单拦截 → 需dangerouslyDisableSandbox: true- PowerShell 工具不回传 stdout → 结果
Out-File到临时文件再用 Read 读 - 调用 CLI / 启动 GUI 一律
dangerouslyDisableSandbox: true(需访问本机桌面)
2. 连接与门禁
2.1 授权握手(一次即可)
node .wxi.js -c <clientName> auth # 返回 taskId,status:pending → IDE 弹授权窗
node .wxi.js -c <clientName> polling_task_result --task-id <taskId>
# authorized:true 即成功;注意 tokenRequired 字段
clientName 用当前 agent 产品简称(如 WorkBuddy、CodeBuddy),须与授权弹窗一致。
2.2 门禁状态检查
node .wxi.js -c <clientName> check_wechatide_status --skill-version <SKILL.md 的 version>
可继续业务仅当 versionRelation 为 equal/agent_ahead 且 loginExpired:false。
tokenRequired:true → 需向用户要「设置 → 安全」里的 CLI 访问令牌。
2.3 异步任务模型(先懂这个,再发命令)
部署/写库不返回结果,返回 pending + taskId,需用户在 IDE 内点确认,然后轮询
polling_task_result。要区分两类异步:
| 类型 | 工具 | 处理 |
|---|---|---|
| 主动轮询 | login / auth |
直接轮询,会阻塞后续 |
| 等用户确认 | 部署、云写、预览确认 | 提醒用户确认 + 记录 pendingTask,不要主动重发 |
已知瞬时状态(都不是失败,等状态回落再重发):
- 新函数首次部署 → FailedOperation.UpdateFunctionCode: 当前函数处于Creating状态 → 等 30s 重试
- 函数正在更新 → 当前函数处于Updating状态 → 见 §3.3
3. 云函数部署
3.1 命令
# 列环境(不明确时先查,禁止自动选)
node .wxi.js -c <c> cloud_env_list --appid <appid>
# 部署(目录名即函数名;一次一个)
node .wxi.js -c <c> cloud_fn_deploy --appid <appid> --env <envId> --path "<函数目录绝对路径>" --remote-npm-install
# ⚠️ 若函数有子目录(如 lib/),完整部署后必须再补一次增量部署(见 3.2②)
node .wxi.js -c <c> cloud_fn_inc_deploy --appid <appid> --env <envId> --path "<函数目录>" --file "lib"
# 复核
node .wxi.js -c <c> cloud_fn_info --appid <appid> --env <envId> --names "a,b,c"
3.2 🚨 依赖与子目录:部署报成功 ≠ 能跑(2026-09-18 实测,血泪)
cloud_fn_deploy 报 execution_success 只代表代码上传成功,不代表函数能执行。两类缺失都会
让运行时直接崩(-504002 functions execute fail),而前端只会显示「服务未就绪」这类模糊文案:
| 缺什么 | 运行时报错 | 修复 |
|---|---|---|
依赖(node_modules) |
Cannot find module 'wx-server-sdk' |
部署必须带 --remote-npm-install |
子目录(lib/) |
Cannot find module './lib/xpay.js' |
完整部署后补一次 cloud_fn_inc_deploy --file lib |
① 必须带 --remote-npm-install
不带该参数时,部署照样报成功,但云端没有依赖 → 一调用就 Cannot find module 'wx-server-sdk'。
⚠️ 不要在函数目录里 npm install 出本地 node_modules:node_modules/ 通常已在
.gitignore,上传 19.6MB / 6544 文件既慢又仍然不可靠(实测传完依旧缺模块),
且会让云端跳过远程安装。正确姿势是保持目录干净(无 node_modules)+ --remote-npm-install,
部署包只有几十 KB(filesCount: 6 / packSize: 26.3 KB 是健康值)。
若已误装本地依赖,先删掉再重部署:
Remove-Item "<函数目录>\node_modules" -Recurse -Force -Confirm:$false
(⚠️ 用 PowerShell 工具删;bash 里 rmSync 大目录会被 SIGTERM 打断。)
② 子目录必须补增量部署
完整部署报告显示 filesCount 含子目录文件、但云端实际没有 → require('./lib/x.js') 崩:
errCode: -504002 functions execute fail
errMsg: Error: Cannot find module './lib/xpay.js'
Require stack:
- /var/user/index.js
修复:完整部署后,对每个含子目录的函数补一次增量部署:
node .wxi.js -c <c> cloud_fn_inc_deploy --appid <appid> --env <envId> --path "<函数目录>" --file "lib"
# 返回 filesCount: 2 / 4.1 KB(= lib 下 2 个文件)即成功
3.3 🚨 部署是异步的:Creating / Updating 期间验证会误判
polling_task_result 拿到 execution_success 后,函数可能仍是 Updating 状态。
此时调用会命中「上一版」代码,返回的错误与本次部署无关 —— 极易误判。
# 部署后先确认回到 Active,再验证
node .wxi.js -c <c> cloud_fn_info --appid <appid> --env <envId> --names <函数名>
# → {"status":"Active"} 才算就绪;仍是 Updating 就再等(本次等了约 45–90s)
两种表现,同一个根因,都不是失败:
| 表现 | 处理 |
|---|---|
FailedOperation.UpdateFunctionCode: 当前函数处于Updating状态,无法进行此操作,请稍后重试 |
等 Active 后原样重发 cloud_fn_deploy(再次等用户确认) |
FailedOperation.UpdateFunctionCode: 当前函数处于Creating状态 |
等 30s 重试即可 |
🚨 别把 status: "success" 当部署成功——polling_task_result 的外层 status 是任务状态,
要看 result 里的细节:
{ "status": "success", "detail": "execution_success",
"result": { "feedback": { "error": { "message": "...FailedOperation.UpdateFunctionCode:
当前函数处于Updating状态,无法进行此操作,请稍后重试..." } } } }
成功时 result 里是 filesCount / packSize(如 "filesCount": 3, "packSize": "3.1 KB");
出现 error.message 就是本次代码上传被拒(确认前云端恰好有别的更新在跑)。修复:cloud_fn_info
确认状态回 Active 后原样重发,成功后用 filesCount 复核,最后按 §3.6 用报错演进 / 云端数据验证。
3.4 🚨 CLI 部署不下发 config.json(timeout / triggers 均无效)
实测:函数目录里写了 config.json → {"timeout": 20},cloud_fn_deploy 报成功且
filesCount 含 config.json,但 cloud_fn_info 返回的 timeout 仍是默认 3。
定时触发器(triggers)同理。
→ 结论:config.json 只是文件被上传,云开发平台不会自动应用它。
timeout / triggers 这类「函数配置」必须在 云开发控制台手动设置
(控制台 → 云函数 → 该函数 → 配置 / 触发器)。用开发者工具 GUI 右键「上传并部署」
走的是另一条路径,会应用 config.json。
⚠️ 因此:改 timeout 后不要指望 CLI 部署生效,必须去控制台改;反过来,
控制台手改的值也不会被 CLI 部署覆盖(实测保持)。
3.5 环境变量同样无法由 CLI 下发
CLI 没有环境变量工具(cloud_fn_deploy 只有 --remote-npm-install)。云函数读
process.env.XXX 的参数必须由用户在云开发控制台 → 云函数 → 配置 → 环境变量手动填写。
→ 密钥(AppKey / AppSecret)只配在云函数,绝不入小程序包。
3.6 验证口径:看报错内容的变化,别信部署报告
逐步修的过程就体现在报错演进上 —— 每一步都证明前一步已生效:
Cannot find module 'wx-server-sdk' → 依赖没装(补 --remote-npm-install)
Cannot find module './lib/skus.js' → 依赖好了,子目录没传(补 inc_deploy --file lib)
{"ok":false,"errCode":"INVALID"} → ✅ 函数正常执行,只是码不对(业务语义)
出现业务错误码(INVALID/USED/THROTTLED)就说明云通道已打通。
最终判定要看云端数据落库(如 bk_entitlements 出现记录、bk_cdk_attempts 计数增长),
而不是看客户端提示文案。
4. 云数据库
4.1 命令
# 建集合(需一次 IDE 确认)
node .wxi.js -c <c> cloud_db_write_struct --appid <appid> --env <envId> \
--action createCollection --collection-name <表>
# 列集合
node .wxi.js -c <c> cloud_db_read_struct --appid <appid> --env <envId> --action listCollections
# 列表索引
node .wxi.js -c <c> cloud_db_read_struct --appid <appid> --env <envId> \
--action listIndexes --collection-name <表>
# 读回文档(唯一能证明「真的落库了」的手段)
node .wxi.js -c <c> cloud_db_read_doc --appid <appid> --env <envId> --collection-name <表> --limit 2
4.2 加索引与批量插文档:必须走文件
内联 JSON 会被 cmd.exe 吃掉双引号(见 §6),报参数错误。文件要放 ASCII 目录(如 %TEMP%):
路径含中文 + cmd.exe 有风险。
# ② 加索引 —— idx.json 内容:
# {"CreateIndexes":[{"IndexName":"code_unique","MgoKeySchema":
# {"MgoIndexKeys":[{"Name":"code","Direction":"1"}],"MgoIsUnique":true}}]}
node .wxi.js -c <c> cloud_db_write_struct --appid <appid> --env <envId> \
--action updateCollection --collection-name <表> --update-options-file "C:\...\Temp\idx.json"
# ③ 批量插文档:同样用文件传数组
node .wxi.js -c <c> cloud_db_write_doc --appid <appid> --env <envId> \
--action insert --collection-name <表> --documents-file "C:\...\Temp\docs.json"
4.3 约束与复核表
| 项 | 说明 |
|---|---|
cloud_fn_info |
必须带 --names a,b,否则报 missing required arguments: --names |
| 写操作确认次数 | createCollection / updateCollection / deploy / inc_deploy / insert 各自都要一次 IDE 确认;任务是排队的,逐个 polling_task_result 收结果 |
| 文件参数 | --update-options-file / --documents-file 远优于内联 JSON;路径放 ASCII 目录 |
| 没有「调用云函数」的工具 | 工具清单里没有 invoke/call → 云函数逻辑的端到端只能靠真机/体验版。不要声称「已实测跑通某云函数」 |
| 🔴 部署后必须复核 timeout | config.json 的 timeout CLI 不下发(见 §3.4),cloud_fn_info 会显示默认 3s。写完函数务必 cloud_fn_info --names 看一眼;超时被平台强杀会跳过一切 catch 回滚逻辑,可能留下脏数据 |
| 写操作后复核 | 集合用 listCollections 看 Count,索引用 listIndexes 看 Unique —— 别只看「task 成功」 |
5. 运行时取证
5.1 通道优先级(照这个顺序试)
| 顺序 | 通道 | 需要 automator? | 能拿到什么 |
|---|---|---|---|
| 1 | get_simulator_console |
否 | 前端 console 文本(见 §5.2) |
| 2 | onLaunch 自打探针 |
否(只需重编译) | 真实运行时 + 原始错误全文(见 §5.3) |
| 3 | automation_evaluate |
是 | 在真实运行时执行代码(见 §5.4) |
| 4 | simulator_screenshot |
否 | 模拟器真实画面,自动化通道失准时的兜底(见 §5.6) |
| — | automation_element_action --action offset/size |
是 | 元素几何数值,样式的硬证据(见 §5.5) |
5.2 console 是第一手段
前端代码通常把错误吞成模糊文案(如「服务未就绪」)。取证的第一步永远是 console ——
它不需要 automator(2026-09-18 实测:automation_* 全系报 timeout waiting for automator
response 时,get_simulator_console 照样可用):
# ⚠️ --command 的值含空格 → 必须走 §6 的「带引号转发器」,否则报 Unknown argument: i
wechatide.cmd -c <c> get_simulator_console --project "<项目路径>" --command "grep -i cloud"
# → [ " [Perf] App.initCloud took 102ms" ] ← 证明云初始化成功
判读要点:返回 "" 只代表无命中,不代表 console 为空 —— 换个 pattern(grep -n . 取全部)。
自己在模块里打的 console.error('[模块名] ...') 是最省事的取证面:先设计好日志,再出问题。
⚠️ 反例教训:console 里 grep 不到某个关键词(如 cdk)时,不要直接下「功能没被调用」的结论 ——
也可能是缓冲区被清过、或代码路径根本没打日志。要与云端数据交叉印证
(例:bk_cdk_attempts Count=0 才能证明「从未调用过云函数」)。
5.3 ✅ 无 automation 通道时的取证绝招:让 App 自己在 onLaunch 打探针
当 automation_evaluate / automation_wx_api 在本机不可用(§5.4 末、§6),但你又必须拿到
云函数的原始 errCode 时,最可靠的办法是让小程序自己发一发请求并把结果打进 console:
// diag-probe.js(临时文件,用完必删)
try {
const cp = require('./utils/cloud-pay.js');
console.log('[DIAG] cloud.available = ' + cp.available());
cp.call('cdkRedeem', { code: '__DIAG_PROBE__' }).then(function (r) {
console.log('[DIAG] cdkRedeem raw = ' + JSON.stringify(r));
});
// 顺带探测一个已知可用的函数作**对照组**
cp.call('payQuery', { action: 'entitlement' }).then(function (r) {
console.log('[DIAG] payQuery raw = ' + JSON.stringify(r));
});
} catch (e) { console.log('[DIAG] probe threw: ' + (e && e.message)); }
// app.js onLaunch 末尾临时加一行(用完删)
try { require('./diag-probe.js'); } catch (e) { /* 忽略 */ }
然后:simulator_open_page 触发重编译 → get_simulator_console --command "grep -n DIAG"。
为什么这招比什么都硬:
- 跑在真实小程序运行时,wx.cloud 是真的(automation_wx_api 的上下文里没有 wx.cloud)。
- 一次拿到原始错误全文(含 -504002 + Cannot find module 'xxx' + Require stack)。
- 对照组的价值:本次 payQuery 返回正常、cdkRedeem 报 Cannot find module ⇒ 直接锁定
「环境没问题,是这一个函数的包缺东西」,排除了「云环境没绑 / 整体不可用」等一整片猜测。
⚠️ 五个必踩的坑:
simulator_refresh只重载、不重编译 → 新加的探针文件不会被加载(console 里什么都看不到)。
必须用simulator_open_page(它会触发编译)。- 探针是异步的:
open_page返回后要等几秒再读 console;且 console 缓冲区会被清,
多读几次、必要时用grep -n .拿全量。 - 用完必须删掉探针 + app.js 里的 require 行(这是会随包发布的调试代码)。
删完用new Function(appSrc)校验 app.js 语法、对比字节数确认恢复原样。 - IDE 长时间运行后重编译可能不加载新代码(2026-09-19 实测):
open_page连同步日志
都不再输出,探针静默。此时不要反复 open/refresh 纠缠——先让用户手动在 IDE 点一次「编译」
或重启 IDE 再验;部署修复的最终验证可改用云端数据判据(如bk_feedbacksCount 变化),
不依赖 console。 get_simulator_console/automation_*报Cannot read properties of undefined (reading 'winId')
→ 项目窗口没打开(IDE 主进程在跑但无项目窗口),不是工具坏了。先
open_project_window --project <路径>再重试;顺带project_list确认项目已注册。
5.4 automation_evaluate(第三选择,先掂量引号地狱)
只有当 console 确实没有、又必须拿原始 errCode 时,才用它在真实小程序运行时直接调云函数:
# --fn-source 必须是**单行**函数字符串(cmd.exe 会破坏多行源码)
wechatide.cmd -c WorkBuddy automation_evaluate --project "<项目绝对路径>" \
--fn-source "function () { return new Promise(function (resolve) { wx.cloud.callFunction({ name: 'payOrder', data: { action: 'login', code: 'PROBE' }, config: { env: '<envId>' }, success: function (r) { resolve({ ok: true, result: r && r.result }); }, fail: function (e) { resolve({ ok: false, errCode: e && e.errCode, errMsg: String(e && e.errMsg || '').slice(0, 400) }); } }); }); }"
坑:
automation_wx_api的执行上下文没有wx.cloud(报wx.cloud.callFunction is not a function),
必须用automation_evaluate;automation_wx_api的--args已废弃、且要求数组。--fn-source传多行源码会被 cmd.exe 破坏(Uncaught Unexpected token ')')→ 压成单行。- 🚨 别经
.wxi.js之类的本地包装脚本传带引号的参数(见 §6):包装脚本把参数拼进
cmd.exe /d /s /c <字符串>时会把"吃掉,--fn-source里的字符串字面量直接坏掉。
--fn-source 'function(){return 40+2;}'(无引号无空格)都可能报
Uncaught Invalid or unexpected token→ 此时automation_evaluate在本环境不可用,
不要在这条路上反复试;先试 §6 的直连写法,仍失败就换 §5.3 的探针。 - 报
timeout waiting for automator response且errorType: MCP_TOOL_ERROR→ 不是代码问题,
是模拟器/IDE 没在跑(自动化通道无人应答)。先用 §1.3 的三条件把 IDE 拉起来并确认模拟器已就绪。
该报错也可能偶发(同一选择器重试即成功),值得重试 1–2 次再下结论。
5.5 布局取证:用元素 offset/size 做数值化验证
改了样式想知道「到底生效没有」时,不要靠看截图(模型可能读不了图、人也容易看错)。
直接量元素的几何:
node .wxi.js -c <c> automation_element_action --project "<项目>" --action offset --selector ".sub__head"
# → { "left": 0, "top": 65 }
node .wxi.js -c <c> automation_element_action --project "<项目>" --action size --selector ".sub__head"
# → { "success": true, "width": 390, "height": 47 }
判定间距 = 后一块的 top − (前一块的 top + 前一块的 height)。
再把该值与样式换算核对:rpx × (视口宽 / 750),例如视口 390 时 23.1rpx → 12.01px。
实测例:.sub__head 底 112 → 下一块顶 124 ⇒ gap 12px,与 23.1rpx 完全吻合 ⇒ 覆写确实生效。
配套动作:
automation_page_action --action getData --data-path <字段>读渲染态数据(如planKey),
判断页面当前处于哪个分支。automation_element_action --action querySelector|text|style|wxml读元素/样式/结构。- 元素查不到(
no such element)优先怀疑wx:if没命中,而不是选择器写错。
例:付费墙的整块购买区在block wx:if="{{planKey !== 'pro'}}"内,页面若是pro态则
.pw-pay/.pw-cdk/.pw-ad全都查不到。 - 想切分支态但
setData/evaluate走不通(见 §6)时,改走设计里本来就有同款结构的另一页:
同一份骨架类被两个页面共用时,在无需改状态的那一页量一次即可证明规则生效。
5.6 自动化通道失准时:截图是唯一兜底
症状:上下文不跟随 simulator_open_page(2026-09-18 实测)
simulator_open_page --page pages/foo/foo 返回 {success:true},但紧接着
automation_page_action getData 读到的仍是上一个页面的字段;automation_element_action
对新页的选择器一律报 no such element(例如 .fb-editor)。重复 open 多次也不切换。
判据(关键):看 getData 里出现了别的页面的字段(如打开 feedback 页却拿到
planName/morning/exams 等 home 页字段)⇒ 是通道上下文问题,不是页面缺陷、也不是选择器写错。
别在这条路上反复试选择器。
此时截图可靠
simulator_screenshot 取的是模拟器真实画面,不受该上下文问题影响。所以 §5.5 那句「不要靠看截图」
要这样理解——量间距优先用 offset/size(更硬,且能避开「模型读不了图」的风险),
但当自动化通道失准时,截图是唯一还能取证的通道,配合肉眼/多模态读图仍能确认
「页面渲染出来了、元素在、颜色对不对」。
# 注意:--output 不被支持(会被忽略),返回 macOS/Linux 风格 Temp 路径,需自行 copy 到项目
node .wxi.js -c <c> simulator_screenshot --project "<项目>"
# → { "path": "C:\\Users\\...\\Temp\\wechatide-simulator-screenshot-<ts>-<rnd>.jpg",
# "imageWidth": 401, "imageHeight": 863 }
截图能抓到 offset 抓不到的东西:实测即靠截图发现提交按钮用了 --btn-bg(浅青 #5BCFC5)
配白字对比度不足 —— 这是纯数值量取(top/height)看不出来的视觉缺陷。
6. 参数传递:本地包装脚本 .wxi.js 的引用地狱(2026-09-18 实测)
项目里常见的简化包装脚本长这样(D:\...\<项目父目录>\.wxi.js):
const ROOT = 'D:/Program Files (x86)/Tencent/微信web开发者工具';
const args = process.argv.slice(2);
spawnSync('cmd.exe', ['/d', '/s', '/c', 'wechatide.cmd ' + args.map(a =>
/[\s"]/.test(a) ? '"' + String(a).replace(/"/g, '""') + '"' : a
).join(' ')]);
它能安全传的:无空格、无引号的参数(工具名、--project <路径>、--selector .cls、--action offset)。
日常的 open_page / screenshot / element_action 都没问题。
它不能安全传的(参数里必须含 " 或含空格的场景):
| 工具 | 参数 | 现象 |
|---|---|---|
get_simulator_console |
--command "grep -i cloud" |
空格被拆成 3 个 token → Unknown argument: i(最常见的翻车点,只想读个日志就中招) |
automation_page_action |
--patch '{"k":"v"}' |
到达时已成 {k:v} → Expected ':' after property name in JSON |
automation_evaluate |
--fn-source 'function(){...}' |
字符串字面量被剥引号 → Uncaught Invalid or unexpected token |
cloud_fn_deploy |
路径含空格 | 见 §1.1(同源问题) |
本质:脚本把整条命令拼成一个字符串交给 cmd.exe /s /c,而 cmd 对嵌套引号的规则
("" 折叠、/s 剥外层引号)与 JS 的转义假设对不上 → 双引号必然丢失,单引号也保不住。
✅ 推荐做法:自己拼行 + windowsVerbatimArguments: true
给含空格的值补上 ASCII 双引号,再让 Node 不要插手转义:
const line = ['wechatide.cmd'].concat(argv.map(a =>
a.indexOf('"') > -1 ? a : (/\s/.test(a) ? '"' + a + '"' : a)
)).join(' ');
spawnSync('cmd.exe', ['/d', '/s', '/c', line], {
cwd: 'D:/Program Files (x86)/Tencent/微信web开发者工具', // ← wechatide.cmd 只在这里找得到
windowsVerbatimArguments: true, // ← 关键,见下
encoding: 'buffer', timeout: 180000, windowsHide: true
});
cwd必须是 IDE 安装目录:wechatide.cmd不在 PATH 上,靠 cwd 解析;
否则报'wechatide.cmd' 不是内部或外部命令,也不是可运行的程序。windowsVerbatimArguments: true是关键:不加的话 Node 会为每个参数自行加引号、
并把我们补的"转义成\"字面量 → 引号照样失效,现象与完全不处理时一模一样
(Unknown argument: i)。踩过一次:以为补引号就够了,其实 Node 在后面又破坏了一遍。- 实测可用:
--command "grep -i cloud"、--args-file <绝对路径>均正常。
其他绕过办法(任一)
- 直接自己 spawn,用数组传参,不要拼字符串:
js spawnSync('wechatide.cmd', ['-c', c, 'automation_page_action', '--project', proj, '--action', 'setData', '--patch', JSON.stringify({planKey:'free'})], { cwd: ROOT, shell: true });
(shell: true由 Node 负责转义,比手拼 cmd 字符串可靠。) - 改走不需要引号的等价工具:能用
automation_element_action/simulator_open_page/getData
达成的目的,就别用setData/evaluate(见 §5.5)。 - 必须执行代码时,把源码写成无引号形式,或走
--args-file(若该工具支持),并先验证一次;
仍不行就用 §5.3 的 onLaunch 探针代替evaluate。
⚠️ 不要在没有验证的情况下把「参数没传到」误判成「功能坏了」——先用一个不涉及引号的
同族调用确认通道可用(如 --action offset),再判定是参数问题还是环境问题。
7. 特殊场景:虚拟支付必须在体验版验证
症状:开发版真机调试上报 Error: Timeout(带 appid/openid/ideVersion/osType 字段的工具层错误)。
这不是代码问题。三条判据:
- 看订单表:若云库订单集合已有新单(
createdAt就在上报时刻前一分钟),说明云函数调用成功、
下单链路已通,卡点在之后的「调起支付」环节。 - 官方错误码表里没有
Timeout:wx.requestVirtualPayment的码是
1001 / -1 / -2 / -4 / -5 / -15001~-15021,没有 Timeout → 报错来自 IDE/框架调用层,不是业务返回。 - 环境隔离规则:虚拟支付按
env严格分区 ——env:1沙箱只能读「开发版道具」,
env:0现网只能读「线上版道具」。服务端把env硬编码为0(现网正确做法,填 1 报-15011)时,
开发版/真机调试必然对不上道具版本,属设计使然。
→ 正确验证路径:开发者工具上传代码 → MP 后台设为体验版 → (可选)虚拟支付「沙箱配置」
加测试微信号白名单 → 体验成员扫码验证。判定成功不要看客户端 success 回调
(用户中途退出即丢),要看服务端:订单状态 pending → delivered、权益表落库、发货云函数日志。
> 同源坑:消息推送未订阅时,权益只能靠定时查单补发(如每 5 分钟),体验上是「付了钱过几分钟才生效」。
8. 速查表
8.1 工具参数硬约束
| 工具 | 硬约束 |
|---|---|
cloud_fn_info |
必须带 --names a,b,否则 missing required arguments: --names |
cloud_fn_deploy |
部署必须带 --remote-npm-install;函数目录不要留 node_modules;一次一个函数 |
cloud_fn_inc_deploy |
只传指定子目录(--file lib);每个含子目录的函数都要补一次 |
cloud_db_write_struct / cloud_db_write_doc |
索引与批量文档必须走文件(--update-options-file / --documents-file),文件放 ASCII 目录 |
automation_evaluate |
--fn-source 必须单行;不要经 .wxi.js 传(引号会被吃) |
automation_wx_api |
上下文没有 wx.cloud;--args 已废弃且要求数组 |
get_simulator_console |
--command 的值含空格 → 必须走 §6 的转发器 |
simulator_screenshot |
不支持 --output(会被忽略),返回 Temp 路径需自行 copy |
cloud_db_read_doc / cloud_db_read_struct |
读回是唯一能证明「真的落库了」的手段 |
8.2 云函数错误码
| 码 | 含义 |
|---|---|
-504002 |
函数内部异常(最常见为缺模块,见 §3.2) |
-404011 |
函数不存在 |
-501000 |
环境错误 |
> 返回业务错误码(如 SESSION_FAIL / INVALID / USED)说明函数已正常执行,
> 问题在业务参数而非部署(见 §3.6)。
8.3 平台配置不由 CLI 下发(三处,都只能去控制台)
| 配置 | 位置 | 见 |
|---|---|---|
timeout |
云函数 → 配置 | §3.4 |
triggers(定时触发器) |
云函数 → 触发器 | §3.4 |
环境变量(process.env.*) |
云函数 → 配置 → 环境变量 | §3.5 |