wkgeo-cli 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -47
- package/SKILL.md +85 -57
- package/lib/commands/auth.mjs +114 -98
- package/lib/commands/billing.mjs +34 -10
- package/lib/device.mjs +24 -4
- package/lib/login.mjs +228 -116
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
* 技术形态:Node.js,**零第三方依赖**(只用 Node 内置模块),Node ≥ 20
|
|
7
7
|
* 授权方式:OAuth 授权码 + **PKCE**(公共客户端,**代码里没有任何 client_secret**)
|
|
8
|
+
* 默认授权:**浏览器打开授权页 + 本地 `127.0.0.1` 回调**(等多久都不会自动改成设备码);设备码只在
|
|
9
|
+
显式 `--device`、检测到系统代理、或本地回调端口真的起不来时兜底(见 §3.2)
|
|
8
10
|
* 另一种授权:**设备码**(`login --device`,不需要回调地址,**对代理/防火墙免疫**;见 §3.2)
|
|
9
11
|
* ⭐ **开着代理也能成功**:**只要检测到系统代理就直接改用设备码**(不看代理绕过列表),用户什么都不用做(见 §3.4)
|
|
10
12
|
* 输出:人类可读 + **稳定 JSON**(默认非终端环境自动 JSON),失败也是结构化 JSON
|
|
@@ -85,7 +87,7 @@ cd cli && node scripts/build-exe.mjs --sea # 跑 Node SEA 的离线两步(
|
|
|
85
87
|
|
|
86
88
|
### 版本与升级策略
|
|
87
89
|
|
|
88
|
-
* 当前版本 **`0.
|
|
90
|
+
* 当前版本 **`0.2.3`**(2026-10-21:CI/非交互环境不再自动打开浏览器;稳定后升 `1.0.0`)。
|
|
89
91
|
* **同一个版本号不能覆盖**:改了代码必须 `npm version patch|minor|major` 再发布。
|
|
90
92
|
* AI 端统一用 **`npx -y wkgeo-cli@latest`** → 每次自动拿最新版;
|
|
91
93
|
**全局安装的用户不会自动升级**(要手动 `npm i -g wkgeo-cli@latest`)。
|
|
@@ -133,7 +135,7 @@ wkgeo login
|
|
|
133
135
|
|
|
134
136
|
会发生什么(**用户实际会看到的东西**):
|
|
135
137
|
|
|
136
|
-
1.
|
|
138
|
+
1. 终端打印一条授权网址(**人在终端时**会尽力自动打开浏览器;**CI/管道/AI 子进程只输出链接**,见下);
|
|
137
139
|
2. 浏览器里是**我们自己的登录页**:支持「账密登录 / 手机号登录 / 邮箱登录」三个页签
|
|
138
140
|
(手机号/邮箱是**验证码登录**,验证码由平台下发到已绑定的手机/邮箱);
|
|
139
141
|
3. 登录后进入**同意页**,显示应用名「官方 CLI」和将获得的权限,点「同意」;
|
|
@@ -143,12 +145,18 @@ wkgeo login
|
|
|
143
145
|
|
|
144
146
|
于是"点同意"这件事**只需要 30 天做一次**(之后令牌过期或被吊销才需要重来)。
|
|
145
147
|
|
|
148
|
+
> ★ **0.2.2 起:默认就是这条本地回调路径**,`wkgeo login` **不会**因为"等了一会儿"
|
|
149
|
+
> 自动变成设备码。设备码只在三种情况出现:① 显式 `--device`;② 检测到系统代理
|
|
150
|
+
> (自动改道,`--no-auto-device` 可关,见 §3.4);③ 本地回调端口**真的起不来**
|
|
151
|
+
> (自动兜底,`--no-auto-device` 可关)。整段等待超时只报 `kind:"timeout"`,不再自动改道。
|
|
152
|
+
|
|
146
153
|
选项:
|
|
147
154
|
|
|
148
155
|
| 选项 | 说明 |
|
|
149
156
|
| --- | --- |
|
|
150
|
-
| `--
|
|
151
|
-
| `--
|
|
157
|
+
| `--device` | 用**设备码模式**(RFC 8628):不需要本地回调地址/端口;本地读不了回调时的兜底(见 §3.2) |
|
|
158
|
+
| `--no-browser` | **不打开浏览器**(优先级最高,压过 `--open-browser`)。⚠️ 非交互/CI 环境本来就不会打开,这个开关主要用于终端/脚本显式表态 |
|
|
159
|
+
| `--open-browser` | 强制尝试打开系统浏览器(⚠️ **只在人类终端生效**;CI/非交互环境仍不打开;保留显式语义与兼容) |
|
|
152
160
|
| `--scope "site:read site:write billing:self"` | 申请的能力范围(默认就是这三个,够建站+下单+支付) |
|
|
153
161
|
| `--client-id <id>` | 换一个 OAuth 客户端(默认 `wkgeo-cli`) |
|
|
154
162
|
| `--callback-port <n>` | 本地回调端口(默认 53682;被占用会自动退到随机端口) |
|
|
@@ -164,26 +172,36 @@ wkgeo login
|
|
|
164
172
|
> `/admin/oauth/authorize`(页面显示「授权请求不完整」)。
|
|
165
173
|
> 全部失败才提示,且**只 warn**(`⚠️ 浏览器没能自动打开…` 后面紧跟**完整 URL** 供复制),**绝不中断**授权流程。
|
|
166
174
|
>
|
|
167
|
-
> ★
|
|
168
|
-
>
|
|
169
|
-
>
|
|
170
|
-
> *
|
|
175
|
+
> ★ **2026-10-21 起:非交互 / CI 环境默认不弹浏览器**(老板真机反馈「别人在 CI 里执行
|
|
176
|
+
> `wkgeo-cli` 的 login,默认弹出了系统默认浏览器」):
|
|
177
|
+
> * **人类终端**(`stdout.isTTY` 为真、且不在 CI 里)→ 照旧**尝试自动打开**系统浏览器(老体验不变);
|
|
178
|
+
> * **非交互 / CI** → **只输出授权链接/JSON,一个浏览器进程都不 spawn**。判定(满足任一):
|
|
179
|
+
> * `stdout.isTTY` 为假(管道 / 重定向 / AI 子进程 / CI 日志采集);
|
|
180
|
+
> * 存在任一 CI 环境变量(**存在即判定**):`CI`、`GITHUB_ACTIONS`、`GITLAB_CI`、`JENKINS_URL`、`TF_BUILD`、`BUILDKITE`、`TEAMCITY_VERSION`;
|
|
181
|
+
> * `WKONGAI_CLI_NO_BROWSER=1`(`true/yes/on` 也认)。
|
|
182
|
+
> 此时 `open_browser.attempted=false`、`skipped_reason:"non_interactive"`(字段结构不变),
|
|
183
|
+
> 并打印「把授权链接转给有浏览器的用户 / 改用 `wkgeo login --device`」的可照做提示;
|
|
184
|
+
> * `--no-browser` → **不打开**(`skipped_reason:"no_browser"`,**优先级最高**,压过 `--open-browser`);
|
|
185
|
+
> * `--open-browser` **保留**,但只在终端里保留显式语义(**压不过**非交互判定:CI 里写它也不弹);
|
|
186
|
+
> * `--json` 的 `open_browser.skipped_reason` 合法值:`null`(真尝试过)/ `"no_browser"` / `"non_interactive"`
|
|
187
|
+
> (2026-10-01 那版的 `"non_tty_agent_mode"` **已弃用**);
|
|
171
188
|
> * 打开成功 → `✅ 已为你打开浏览器,请在那里完成授权。`;打开失败 → ⚠️ 一段**含完整 URL** 的醒目提示
|
|
172
189
|
> (AI 场景再多一句"客户端支持内置浏览器也可以在那里打开"的**可选**提示,不是主路径);
|
|
173
|
-
> *
|
|
174
|
-
>
|
|
175
|
-
> * `--json` 的 `open_browser.skipped_reason` 现在只有 `"no_browser"` 或 `null` 两种
|
|
176
|
-
> (2026-10-01 那版的 `"non_tty_agent_mode"` **已弃用**);
|
|
177
|
-
> * 为什么走传统路线:**稳定**(各平台都有成熟的系统启动器)+ **`window.close()` 在独立浏览器里生效**
|
|
178
|
-
> (授权页/收银台完成后的那一屏能自己关掉);
|
|
190
|
+
> * 非交互时 `paste_text` 是「请打开以下链接完成授权:<url>」**而不是**「已为你打开浏览器」——
|
|
191
|
+
> 屏幕上什么都没弹,说"已打开"会让用户以为链接坏了;
|
|
179
192
|
> * ⚠️ **不弹浏览器 ≠ 失败**、**打开失败也 ≠ 失败**:地址照旧完整打印、流程照旧等回调/照旧返回 `pay_url`,
|
|
180
|
-
>
|
|
193
|
+
> 命令**绝不会**因此失败(回调 / 设备码的判定逻辑也完全不受影响)。
|
|
181
194
|
|
|
182
195
|
> ⚠️ 回调**只监听 `127.0.0.1`**(绝不 `0.0.0.0`);`state` 每次随机并在本地回调时**强校验**
|
|
183
196
|
> (服务端只负责原样回传,防 CSRF 是客户端自己的责任)。
|
|
184
197
|
> 端口被占用会自动换随机端口 —— 服务端按 RFC 8252 对回环地址**忽略端口**比对。
|
|
185
198
|
|
|
186
|
-
### 3.2 设备码模式(`--device`)——
|
|
199
|
+
### 3.2 设备码模式(`--device`)—— **本地读不了回调 / 代理 / 远程机器时的兜底**
|
|
200
|
+
|
|
201
|
+
> ★ **默认不是设备码**:`wkgeo login` 默认走浏览器回调(§3.1)。设备码只在:
|
|
202
|
+
> ① 显式 `--device` / `--no-wait` / `--device-continue`;② 检测到系统代理(CLI 自动,§3.4);
|
|
203
|
+
> ③ 本地回调端口真的起不来(CLI 自动兜底,`reason:"callback_listen_failed"`)。
|
|
204
|
+
> **等待超时不会**自动改设备码(0.2.2 起)。
|
|
187
205
|
|
|
188
206
|
```bash
|
|
189
207
|
wkgeo login --device # 申请短码 → 打印网址/短码 → 轮询到出结果(人盯着终端时用这条)
|
|
@@ -280,7 +298,7 @@ wkgeo login --device-continue --json
|
|
|
280
298
|
多数代理默认绕过回环地址,但**全局/系统代理 + 绕过列表为空**时,浏览器会把这条回环请求
|
|
281
299
|
也交给代理 → CLI 的本地服务永远收不到 `code`。**问题在浏览器那一侧,不在 CLI 出网**。
|
|
282
300
|
|
|
283
|
-
**CLI 的处理(2026-10-02
|
|
301
|
+
**CLI 的处理(2026-10-02 第二版加固;2026-10-21 默认回调调整)**:
|
|
284
302
|
|
|
285
303
|
1. **检测**(只读,绝不改你的代理设置):Windows 读注册表 `…\Internet Settings` 的
|
|
286
304
|
`ProxyEnable`/`ProxyServer`/`ProxyOverride`/`AutoConfigURL`;macOS 读 `scutil --proxy`;
|
|
@@ -300,27 +318,25 @@ wkgeo login --device-continue --json
|
|
|
300
318
|
(检测详情:源=windows-registry 代理=127.0.0.1:7890 绕过列表=localhost,127.*,<local>;
|
|
301
319
|
⚠️ 绕过列表里就算写了 127.*/localhost 也不再作为放行回调的依据 —— 真机上出现过"写了却仍被代理接管"。)
|
|
302
320
|
```
|
|
303
|
-
4. ⭐
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
⚠️
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
「本次登录已经完成」的页面。
|
|
318
|
-
本地回环端口**起不来**(防火墙/安全软件)时同样自动降级;
|
|
321
|
+
4. ⭐ **等待超时不再自动改设备码(0.2.2,老板 2026-10-21 反馈)**:
|
|
322
|
+
老板原话「默认的也不应该是设备授权,默认的就是那个传统的授权方式(浏览器打开授权页 +
|
|
323
|
+
本地 localhost 回调)。设备码是本地读不了/代理时才用的。」
|
|
324
|
+
⇒ 没有代理时 `login` 会**一路等回调到 `--timeout`**(默认 300 秒);超时只报
|
|
325
|
+
`kind:"timeout"`,并在 `hint` 里给出"显式改用 `--device`"的下一步,**不会**偷偷改道。
|
|
326
|
+
⚠️ 历史行为(0.2.1 及更早):有代理时"首轮等待 180 秒"到点会改设备码、整段超时也会改。
|
|
327
|
+
现在这个"首轮等待"**默认关闭**:只有你**显式**写 `--callback-first-wait 90` 或
|
|
328
|
+
`WKONGAI_CALLBACK_FIRST_WAIT_MS=90000` 才启用(**显式优先**,`reason:"callback_first_wait"`)。
|
|
329
|
+
显式启用时仍然是**两条路并存**:本地回调监听**继续保留**,**哪条先成功就用哪条** ——
|
|
330
|
+
你之后才点【同意授权】照样能完成登录。旧版在切换那一刻就把监听关了,于是你之后点同意
|
|
331
|
+
浏览器只会显示 **ERR_CONNECTION_REFUSED**("无法访问此网站",2026-10-03 已修);
|
|
332
|
+
若设备码先成功,之后到达的回调会看到一张「本次登录已经完成」的页面。
|
|
333
|
+
5. ⭐ **本地回调端口真的起不来**(防火墙/安全软件/端口非法)→ 自动兜底设备码
|
|
334
|
+
(`reason:"callback_listen_failed"`),**不会**直接失败退出;
|
|
319
335
|
**换票失败、用户点了拒绝这类"已经走到下一步"的错误不会降级**(避免让你白做一遍)。
|
|
320
|
-
|
|
336
|
+
6. **不想被自动改道**:加 `--no-auto-device`,或设 `WKONGAI_AUTO_DEVICE=0`。
|
|
321
337
|
⚠️ 此时 CLI 会**明确警告**"若代理会接管 127.0.0.1,点了【同意授权】也收不到结果",
|
|
322
|
-
并且**关掉**第 4
|
|
323
|
-
|
|
338
|
+
并且**关掉**第 4 条的自愈与第 5 条的端口兜底(你要求只走回调,就不会背着你偷偷改道)。
|
|
339
|
+
7. **排障入口**:`wkgeo config` 会直接打印本机代理情况与结论(`--json` 里是
|
|
324
340
|
`data.proxy` / `meta.proxy_warning`),支持同学让用户跑这一条就能判断。
|
|
325
341
|
|
|
326
342
|
`--json` 里的结构化字段(AI 端据此判断"是不是代理的锅、该让用户做什么"):
|
|
@@ -346,6 +362,13 @@ wkgeo login --device-continue --json
|
|
|
346
362
|
失败时的 `error.hint` 里也会带上同样的三选一(例如回调超时的 JSON 错误信封里,
|
|
347
363
|
`meta.proxy_warning` 依然在)。
|
|
348
364
|
|
|
365
|
+
> ★ **0.2.2 兼容字段**:`data.auto_device` 形状**不变**(`{applied, reason}`),`mode` 仍是
|
|
366
|
+
> `"callback"` / `"device"`,**没有删除任何既有字段**。`reason` 现在只会是:
|
|
367
|
+
> `"proxy_detected"`(检测到代理)/ `"callback_listen_failed"`(本地端口起不来)/
|
|
368
|
+
> `"callback_first_wait"`(仅用户显式 `--callback-first-wait` 时)/ `null`。
|
|
369
|
+
> 历史取值 `"callback_timeout"` **不再产生**(0.2.2 起超时不改道;老脚本把它当"回调失败后的
|
|
370
|
+
> 设备码兜底"读到的语义不变)。
|
|
371
|
+
|
|
349
372
|
> ⚠️ 两个容易误读的点:
|
|
350
373
|
> 1. **CLI 自己的出网不受系统代理影响**:Node 的 fetch(undici)默认既不读系统代理、
|
|
351
374
|
> 也不读 `HTTP_PROXY`(要 Node ≥ 24 的 `--use-env-proxy` / `NODE_USE_ENV_PROXY=1`)。
|
|
@@ -364,7 +387,7 @@ wkgeo login --device-continue --json
|
|
|
364
387
|
|
|
365
388
|
| 命令 | 作用 | 关键参数 |
|
|
366
389
|
| --- | --- | --- |
|
|
367
|
-
| `login` |
|
|
390
|
+
| `login` | 授权一次,令牌存本地(**默认浏览器回调**;设备码仅兜底) | `--device`、`--no-wait`、`--device-continue`、`--auto-device`/`--no-auto-device`、`--callback-first-wait <秒>`(可选,默认关闭)、`--no-browser`、`--open-browser`、`--scope`、`--callback-port`、`--timeout <秒>` |
|
|
368
391
|
| `whoami` | 当前登录的是谁(账号标识) | `--verbose`(额外显示令牌**掩码**与文件权限) |
|
|
369
392
|
| `logout` | 删本地凭据(默认还调服务端吊销) | `--local-only` |
|
|
370
393
|
| `config` | 显示生效配置与登录状态(无令牌) | — |
|
|
@@ -392,7 +415,7 @@ wkgeo login --device-continue --json
|
|
|
392
415
|
| `import` | 老站导入(**默认 dry-run**;走本机运维脚本,需运维权限) | `--url`(必填)、`--apply`、`--site`、`--status`、`--out`、`--max-pages`、`--delay`、`--include`、`--exclude`、`--overwrite`、`--no-probe` |
|
|
393
416
|
| `wallet` | 余额 + 最近流水 | `--limit`、`--entitlements` |
|
|
394
417
|
| `wallet txns` | 钱包流水(普通用户自己的那份) | `--limit` |
|
|
395
|
-
| `wallet recharge` | **充值下单**:返 `code_url` + **`pay_url`(自家收银台页面,带订单支付令牌 `token`;★
|
|
418
|
+
| `wallet recharge` | **充值下单**:返 `code_url` + **`pay_url`(自家收银台页面,带订单支付令牌 `token`;★ 人类终端默认自动打开,CI/非交互只输出链接;**支付成功后页面会自己关掉**)**(**不会自动扣款**) | `--amount-cents`(必填,单位分)、`--no-browser`、`--open-browser`、`--dry-run` |
|
|
396
419
|
| `products list` | 能买什么(找 `--product` 该填什么) | — |
|
|
397
420
|
| `orders create` | 下单(**余额够就当场用钱包付清**) | `--product <代码>` + `--units <份数>`,或 `--quota <键>` + `--amount <数量>`;`--allow-wechat` |
|
|
398
421
|
| `orders check` | 查支付结果(微信侧已付则就地履约)+ 最新余额 | `<订单ID>` |
|
|
@@ -588,7 +611,7 @@ wkgeo publish # ③ 看状态(见下)
|
|
|
588
611
|
| --- | --- | --- |
|
|
589
612
|
| 1 | `wkgeo wallet`(记下当前余额,例如 ¥100.00) | 打印余额与流水 |
|
|
590
613
|
| 2 | `wkgeo wallet recharge --amount-cents 1000 --dry-run` | 只打印"将创建 ¥10.00 订单",**不产生任何订单** |
|
|
591
|
-
| 3 | `wkgeo wallet recharge --amount-cents 1000` | 返回 `order_id` / `code_url` / **`pay_url
|
|
614
|
+
| 3 | `wkgeo wallet recharge --amount-cents 1000` | 返回 `order_id` / `code_url` / **`pay_url`**;**人类终端**下会自动在浏览器打开 `pay_url` —— 也就是我们自己的收银台 `https://pay.wkgeo.com/?order_id=…&token=…`(CI/非交互只输出链接,不弹) |
|
|
592
615
|
| 4 | 在**收银台页面**用手机微信扫码支付 ¥10.00(人工) | 页面自动检测到支付成功;**支付成功后页面会自动尝试关闭**(关不掉就手动关掉即可);`pay_url` 里的订单号在任何设备/浏览器打开都能看到同一笔单 |
|
|
593
616
|
| 5 | `wkgeo orders check <order_id>` | `paid:true` + **最新余额 = 原余额 + 1000 分** |
|
|
594
617
|
| 6 | `wkgeo wallet txns` | 出现一条 `kind=recharge`、金额 `+¥10.00` 的流水 |
|
|
@@ -597,12 +620,14 @@ wkgeo publish # ③ 看状态(见下)
|
|
|
597
620
|
> **为什么是"打开我们网站的收银台"而不是"在终端画二维码"**:
|
|
598
621
|
> 老板明确否掉了"本地生成一个二维码页面让别人扫"的做法 —— 充值必须走**我们网上的充值页面**。
|
|
599
622
|
> 终端二维码只在"浏览器确实打不开"时作为**兜底**出现(文案会写明这一点)。
|
|
600
|
-
> `--no-browser`
|
|
601
|
-
> ★
|
|
602
|
-
>
|
|
603
|
-
>
|
|
604
|
-
>
|
|
605
|
-
>
|
|
623
|
+
> `--no-browser` 用于无桌面环境:**不打开**,`pay_url` 照常输出。
|
|
624
|
+
> ★ **2026-10-21 起:人类终端默认尝试弹系统浏览器;非交互/CI 只输出链接**(与 login 同一道闸门):
|
|
625
|
+
> * 非交互判定(同 §3.1):stdout 非 TTY / CI 环境变量 / `WKONGAI_CLI_NO_BROWSER=1`
|
|
626
|
+
> ⇒ `open_browser.skipped_reason = "non_interactive"`,**一个进程都不 spawn**,并提示"把收银台链接交给用户";
|
|
627
|
+
> * 打开成功 → `✅ 已为你打开浏览器,请在那里完成支付(微信扫码)。`;
|
|
628
|
+
> * 打开失败 → ⚠️ 一段**含完整收银台地址**的醒目提示(走 stderr,stdout 仍然只有机器可读 JSON);
|
|
629
|
+
> * `--no-browser` → `skipped_reason = "no_browser"`(最高优先级);
|
|
630
|
+
> * `--open-browser` 保留,但只在终端里生效(非交互仍不弹)。
|
|
606
631
|
>
|
|
607
632
|
> ★ **`pay_url` 上的 `token=`(2026-10-02 新增)**:服务端在**下单那一刻**把"这一单是谁的"
|
|
608
633
|
> 签成一把**只对这一单有效**的令牌(`createRechargeOrder` 返回的 `pay_token`),
|
|
@@ -708,8 +733,10 @@ node tencent/functions/_tests/oauth-device-live-e2e.js # 结
|
|
|
708
733
|
| `WKONGAI_CREDENTIALS` | `~/.wkongai/credentials.json` | 凭据文件路径 |
|
|
709
734
|
| `WKONGAI_CALLBACK_PORT` | `53682` | 本地回调端口 |
|
|
710
735
|
| `WKONGAI_LOGIN_TIMEOUT_MS` | `300000` | 等待授权超时(⚠️ 变量名里带 `MS`,所以这里填**毫秒**;命令行 `--timeout` 填的是**秒**) |
|
|
711
|
-
| `WKONGAI_CALLBACK_FIRST_WAIT_MS` | 未设置 | ★
|
|
736
|
+
| `WKONGAI_CALLBACK_FIRST_WAIT_MS` | 未设置 | ★ **可选**的"首轮等待"(毫秒,**默认关闭**):只有显式设了它(或命令行 `--callback-first-wait`),这段时间没等到回调才会自动改用设备码继续(**本地回调监听会同时保留**);`0` 关闭(等价于 `--callback-first-wait 0`)。⚠️ **未设置时一律不启用**(0.2.2 起:等待超时不再自动改设备码;老板 2026-10-21:「默认的就是那个传统的授权方式」) |
|
|
712
737
|
| `WKONGAI_AUTO_DEVICE` | `1`(开) | 检测到系统代理时是否自动改用设备码;设 `0`/`false`/`no`/`off` 关闭(等价于 `--no-auto-device`,会打印"代理可能吃掉回调"的警告) |
|
|
738
|
+
| `WKONGAI_CLI_NO_BROWSER` | 未设置 | ★ **2026-10-21**:设 `1`/`true`/`yes`/`on` = **不自动打开浏览器**(`open_browser.skipped_reason = "non_interactive"`)。给"没有 CI 环境变量但确实在无浏览器环境"的容器/远程场景用 |
|
|
739
|
+
| `CI` / `GITHUB_ACTIONS` / `GITLAB_CI` / `JENKINS_URL` / `TF_BUILD` / `BUILDKITE` / `TEAMCITY_VERSION` | 未设置 | ★ **2026-10-21**:**存在任一即判定为非交互**(不看值)→ **不自动打开浏览器**,只输出链接/JSON |
|
|
713
740
|
| `NO_COLOR` | — | 设了就不上色 |
|
|
714
741
|
|
|
715
742
|
---
|
|
@@ -719,7 +746,7 @@ node tencent/functions/_tests/oauth-device-live-e2e.js # 结
|
|
|
719
746
|
| # | 项 | 现状 | 谁来处理 |
|
|
720
747
|
| --- | --- | --- | --- |
|
|
721
748
|
| 1 | **设备码端点** | ✅ **已上线并端到端真跑通过**(2026-10-02:metadata 声明 + 申请/确认/换票/重放全绿,真 CLI 子进程 `stage:"done"`)。协议细节见 `docs/设备码授权-服务端最小方案.md`(该文档已更新为"已实现") | 已完成;回归用 `_tests/oauth-device-live-e2e.js` |
|
|
722
|
-
| 1b | **代理自动降级 /
|
|
749
|
+
| 1b | **代理自动降级 / 默认回调** | ✅ 已实现(**检测到代理即改设备码**,不再相信绕过列表;本地回调端口起不来自动兜底)。2026-10-02 **真机翻车已复现并加固**(老板机器:绕过列表写了 `127.*` 却仍被代理接管,证据 `curl -x …7890 http://127.0.0.1:1/x` → 502)。★ 2026-10-21 **默认模式已改回传统回调**(0.2.2):**等待超时不再自动改设备码**;设备码只由①显式 `--device`②检测到代理③本地回调端口真的起不来触发;`--callback-first-wait` 变成**默认关闭**的显式可选自愈;切设备码时**保留本地回调监听**,两条路并存先到先得(旧版关掉监听 ⇒ 用户之后点【同意授权】得到 ERR_CONNECTION_REFUSED) | 已完成(单测:默认 callback / `--device` 强制 / 端口失败兜底 / `--no-auto-device` 不兜底 / 超时不改道);建议老板真机跑一次 `wkgeo login` 验收"默认就是浏览器回调" |
|
|
723
750
|
| 1c | **设备码页"没有完成态/不自动关"** | ⚠️ 老板 2026-10-02 反馈:点完设备码页的【同意授权】后页面**不给明确的完成态、也不自动关**,用户不知道要不要继续等。**并行改动**已在 `admin-web/src/pages/device/index.tsx` 补上终态卡片(`✅ 授权成功` + "本页面可以关闭了" + 返回首页)。本轮 CLI 侧**未改前端** | 见下面 §9.1 的补充建议(可选) |
|
|
724
751
|
| 2 | **错误码 40101/40102** | `/api/data` **已返回 40102**(实测"访问令牌已被吊销,请重新授权");但 `userinfo` 仍是标准 `401 invalid_token`(靠文案区分)。CLI 两条路都兼容 | 服务端(可选,统一到 40101/40102) |
|
|
725
752
|
| 3 | **`listMyOAuthAuthorizations.token_count` 虚高** | 该 SQL 用 `count(*)` 统计 `left join lateral unnest(t.scopes)` 后的行数 → **数的是 scope 个数,不是令牌数**(实测:1 把令牌、3 个 scope → 显示"3 把")。正确写法 `count(distinct t.access_token_hash)` | **主线程/服务端** |
|
package/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ version: 0.2.0
|
|
|
22
22
|
* 用户说"**帮我建个站**"/"创建网站"/"开一个站点" → `sites create`
|
|
23
23
|
* 用户说"**帮我下单**"/"买加量包"/"扩容空间"/"买流量" → `products list` → `orders create`
|
|
24
24
|
* 用户说"**用钱包付**"/"余额支付"/"帮我付钱" → `orders create`(余额够会**当场付清**)+ `wallet` 复核
|
|
25
|
-
* 用户说"**充值**"/"充 100 块" → `wallet recharge
|
|
25
|
+
* 用户说"**充值**"/"充 100 块" → `wallet recharge`(下单后在**人类终端**里会自动打开我们网站的收银台页面,用户在那里扫码支付;CI/非交互只输出 `pay_url`)
|
|
26
26
|
* 用户说"**充值**"但**没说金额**、或说"**我自己选金额**"/"给我个充值链接" → `wallet recharge-link`
|
|
27
27
|
(生成**独立充值页**链接:`https://pay.wkgeo.com/recharge/?ticket=…`,用户在网页上自己选金额
|
|
28
28
|
—— ¥10 / ¥20 快捷,或自定义,**最低 ¥9.90、不设上限** —— 然后扫码支付;**不需要登录会员中心**。
|
|
@@ -107,7 +107,7 @@ npx -y wkgeo-cli@latest skill > SKILL.md # 直接落一份到本地,离线
|
|
|
107
107
|
|
|
108
108
|
| 命令 | 作用 | 关键参数 |
|
|
109
109
|
| --- | --- | --- |
|
|
110
|
-
| `login` | 授权一次(令牌存本地 30
|
|
110
|
+
| `login` | 授权一次(令牌存本地 30 天;**默认浏览器回调**,设备码仅兜底) | `--device`、`--no-wait`、`--device-continue`、`--no-auto-device`、`--callback-first-wait <秒>`(可选,默认关闭)、`--no-browser`、`--open-browser`、`--scope`、`--timeout <秒>` |
|
|
111
111
|
| `whoami` | 当前是谁(账号标识) | `--verbose` |
|
|
112
112
|
| `logout` | 退出(删本地凭据 + 服务端吊销) | `--local-only` |
|
|
113
113
|
| `config` | 生效配置 / 登录状态 | — |
|
|
@@ -116,7 +116,7 @@ npx -y wkgeo-cli@latest skill > SKILL.md # 直接落一份到本地,离线
|
|
|
116
116
|
| `sites delete` | 删站(软删进回收站) | `<站点ID>` |
|
|
117
117
|
| `wallet` | 余额 + 最近流水 | `--limit` `--entitlements` |
|
|
118
118
|
| `wallet txns` | 钱包流水 | `--limit` |
|
|
119
|
-
| `wallet recharge` |
|
|
119
|
+
| `wallet recharge` | 充值下单,**人类终端会自动打开我们网站的收银台页面**扫码支付(CI/非交互只输出链接;**不扣款**) | `--amount-cents`(分,必填)、`--no-browser`、`--open-browser`、`--dry-run` |
|
|
120
120
|
| `wallet recharge-link` | 生成**独立充值页**链接(用户在网页上**自己选金额**并扫码支付;**不扣款**、不建单) | `--open-browser`(默认**不**弹)、`--no-browser` |
|
|
121
121
|
| `products list` | 能买什么 | — |
|
|
122
122
|
| `orders create` | 下单(余额够**当场付清**) | `--product <代码>` `--units <份数>`(或 `--quota <键>` `--amount <数量>`)`--allow-wechat` |
|
|
@@ -209,6 +209,10 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
209
209
|
|
|
210
210
|
它会**阻塞**(默认最多 5 分钟)并输出 JSONL:
|
|
211
211
|
|
|
212
|
+
> ★ **0.2.2 起:默认就是这条回调**,等多久都**不会**自动改成设备码;整段超时只报
|
|
213
|
+
> `kind:"timeout"`(并在 hint 里给出"显式改用 `--device`"的下一步)。设备码只在
|
|
214
|
+
> ① 显式 `--device` ② 检测到系统代理 ③ 本地回调端口真的起不来 时出现(见 §5.3 / §5.4)。
|
|
215
|
+
|
|
212
216
|
```jsonc
|
|
213
217
|
// 第一行 —— 拿到它就应该**立刻**把网址交给用户(不要等命令结束!)
|
|
214
218
|
// ⚠️ `authorize/` 的**尾斜杠不能丢**:它是托管侧 302(会丢掉全部 query)的分界线
|
|
@@ -228,17 +232,21 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
228
232
|
> 不要自己换算成毫秒。非法值(`--timeout 十分钟` / `--timeout 0`)直接报用法错误(退出码 `2`),
|
|
229
233
|
> 不会静默变成默认值。
|
|
230
234
|
|
|
231
|
-
> 🌐 **自动打开浏览器**(⚠️
|
|
232
|
-
>
|
|
233
|
-
>
|
|
234
|
-
>
|
|
235
|
-
>
|
|
236
|
-
>
|
|
237
|
-
>
|
|
238
|
-
>
|
|
239
|
-
>
|
|
235
|
+
> 🌐 **自动打开浏览器**(⚠️ **2026-10-21 有改动**:CI/非交互环境**不再弹**,详见 §5.5):
|
|
236
|
+
> * **人类终端**(stdout 是 TTY、且不在 CI 里)→ **尝试自动打开**系统浏览器;
|
|
237
|
+
> * **非交互 / CI**(stdout 非 TTY / 管道 / AI 子进程;或存在 `CI`、`GITHUB_ACTIONS`、
|
|
238
|
+
> `GITLAB_CI`、`JENKINS_URL`、`TF_BUILD`、`BUILDKITE`、`TEAMCITY_VERSION`;或
|
|
239
|
+
> `WKONGAI_CLI_NO_BROWSER=1`)→ **只输出链接/JSON,一个浏览器进程都不 spawn**,
|
|
240
|
+
> `open_browser.attempted=false`、`skipped_reason="non_interactive"`;
|
|
241
|
+
> * 实现是**多策略依次尝试**(每条都看退出码/错误/超时,失败静默试下一条):
|
|
242
|
+
> * 成功 → `✅ 已为你打开浏览器,请在那里完成授权。`(紧跟着一句"授权成功后页面会自动尝试关闭");
|
|
243
|
+
> * 全部失败 → `⚠️ 浏览器没能自动打开,请手动复制这个地址到浏览器完成授权:` + **紧跟一行完整 URL**
|
|
244
|
+
> (**绝不中断**授权;AI 场景再多一句"客户端支持内置浏览器也可以在那里打开"的**可选**提示);
|
|
245
|
+
> * `--no-browser` → **不打开**(`open_browser.skipped_reason = "no_browser"`,最高优先级,
|
|
246
|
+
> 压过 `--open-browser`);
|
|
247
|
+
> * `--open-browser` → 只在终端里保留显式语义(**压不过**非交互判定,CI 里写它也不弹)。
|
|
240
248
|
> 所以 AI **不需要**自己判断"该不该开浏览器",但**永远要**把 `authorization_url` 原文转述给用户
|
|
241
|
-
>
|
|
249
|
+
> (CI/非交互环境里,这一步就是**唯一**的交付方式)。
|
|
242
250
|
|
|
243
251
|
**AI 的正确做法**:
|
|
244
252
|
|
|
@@ -246,6 +254,8 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
246
254
|
2. 读到 `stage:"awaiting_callback"` 后:
|
|
247
255
|
* **先看 `open_browser`**:`opened:true` = CLI 已经把系统浏览器弹出来了 → 把 `paste_text`
|
|
248
256
|
**原样转述给用户**("已为你打开浏览器,请在那里完成授权…")即可,**不要**自己再开一遍;
|
|
257
|
+
* `skipped_reason:"non_interactive"`(CI / 管道 / AI 子进程)→ **CLI 没弹、也不该弹**:
|
|
258
|
+
把 `authorization_url` / `paste_text` 清楚地转给有浏览器的用户;本机收不到回调时改用 `--device`;
|
|
249
259
|
* `opened:false`(打开失败)→ 把那段**含完整地址**的提示念给用户,或**可选**地
|
|
250
260
|
在你自己客户端的内置浏览器里打开 `authorization_url`(见 §5.5,这只是备选);
|
|
251
261
|
* `skipped_reason:"no_browser"` → 用户/调用方明确不要弹窗,那就**只**把完整网址清楚地给用户;
|
|
@@ -264,7 +274,14 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
264
274
|
* 用户也可以在网页「我已授权的应用」里一键吊销(吊销后 CLI 会收到 40102)。
|
|
265
275
|
* 🔒 不要把这个文件内容读出来放进任何输出/日志/第三方请求里。
|
|
266
276
|
|
|
267
|
-
### 5.3 设备码模式(`login --device`)——
|
|
277
|
+
### 5.3 设备码模式(`login --device`)—— **本地读不了回调 / 代理 / 远程机器时的兜底**
|
|
278
|
+
|
|
279
|
+
> ★ **默认不是设备码**:`wkgeo login` 默认走浏览器回调(授权页 + 本地 `127.0.0.1` 回调,见 §5.1)。
|
|
280
|
+
> 只有这几种情况才用设备码:
|
|
281
|
+
> 1. 用户**显式** `--device` / `--no-wait` / `--device-continue`;
|
|
282
|
+
> 2. **检测到系统代理**(CLI 自动改道,见 §5.4;`--no-auto-device` 可关);
|
|
283
|
+
> 3. 本地回调端口**真的起不来**(CLI 自动兜底,`reason:"callback_listen_failed"`;`--no-auto-device` 可关)。
|
|
284
|
+
> **等待超时不会**自动改设备码(0.2.2 起)。
|
|
268
285
|
|
|
269
286
|
**什么时候用它**(不用问用户,直接选它就行):
|
|
270
287
|
|
|
@@ -337,7 +354,7 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
337
354
|
**机理**:回调要求**浏览器**访问 `http://127.0.0.1:<端口>/callback?code=…`;
|
|
338
355
|
全局/系统代理若没把回环地址排除,这条请求会被代理接管 → CLI 收不到 code。
|
|
339
356
|
|
|
340
|
-
**CLI 已经自己处理好了**(2026-10-02
|
|
357
|
+
**CLI 已经自己处理好了**(2026-10-02 第二版加固 + 2026-10-21 默认模式调整):
|
|
341
358
|
|
|
342
359
|
* ★ **只要检测到"生效中的系统代理"就直接改用设备码模式**(事件流里第一行就是
|
|
343
360
|
`stage:"awaiting_user"`,而不是 `awaiting_callback`);`data.auto_device.reason = "proxy_detected"`。
|
|
@@ -346,22 +363,22 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
346
363
|
证据 `curl -x http://127.0.0.1:7890 http://127.0.0.1:1/x` → 502)。
|
|
347
364
|
`proxy_warning.loopback_bypassed` / `risk` 现在**只是诊断字段**,不再代表"回调没问题";
|
|
348
365
|
`proxy_warning.loopback_bypass_trusted` 恒为 `false`。
|
|
349
|
-
*
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
⚠️ **切换时不再关掉本地回调监听**(同一次修复):两条路**并存**,哪条先成功就用哪条 ——
|
|
366
|
+
* ★★ **0.2.2 起:等待超时不再自动改设备码**(老板 2026-10-21 反馈):
|
|
367
|
+
老板原话「默认的也不应该是设备授权,默认的就是那个传统的授权方式(浏览器打开授权页 +
|
|
368
|
+
本地 localhost 回调)。设备码是本地读不了/代理时才用的。」
|
|
369
|
+
⇒ 没有代理时 `login` 会**一路等回调到 `--timeout`**(默认 300 秒),超时只报
|
|
370
|
+
`kind:"timeout"`,并在 `hint` 里给出"显式改用 `--device`"的下一步。
|
|
371
|
+
⚠️ 历史行为(0.2.1 及更早):有代理时"首轮等待 180 秒"到点会改设备码、整段超时也会改;
|
|
372
|
+
现在那个"首轮等待"**默认关闭**,只有用户**显式**写 `--callback-first-wait <秒>` /
|
|
373
|
+
`WKONGAI_CALLBACK_FIRST_WAIT_MS` 才启用(显式优先,`reason:"callback_first_wait"`)。
|
|
374
|
+
显式启用时仍然是**两条路并存**:本地回调监听继续保留,哪条先成功就用哪条 ——
|
|
359
375
|
用户之后才点的那次【同意授权】照样能完成登录;旧逻辑在切换那一刻关了监听,用户随后点同意
|
|
360
|
-
只会看到 **ERR_CONNECTION_REFUSED**(真机截图见过的"无法访问此网站"
|
|
376
|
+
只会看到 **ERR_CONNECTION_REFUSED**(真机截图见过的"无法访问此网站",2026-10-03 已修)。
|
|
361
377
|
若设备码先成功,之后到达的回调会拿到一张「本次登录已经完成」的页面(不是错误页);
|
|
362
378
|
若回调先成功,设备码轮询会被停掉并说明(`ℹ️ 本地回调已经拿到令牌:设备码那条路已停止…`)。
|
|
363
|
-
*
|
|
364
|
-
(`reason: "
|
|
379
|
+
* 本地回环端口**真的起不来**(防火墙/安全软件/端口非法)→ 自动兜底设备码
|
|
380
|
+
(`reason: "callback_listen_failed"`),**不会**直接失败退出;
|
|
381
|
+
`--no-auto-device` 可关闭这个兜底(此时如实报 `kind:"listen"`)。
|
|
365
382
|
* 换票失败 / 用户点了拒绝 → **不降级**(授权已经走到下一步了,别让用户白做一遍);
|
|
366
383
|
* 上述情况下,`meta.proxy_warning` / `data.proxy_warning` 都会告诉你原因与建议动作:
|
|
367
384
|
|
|
@@ -384,28 +401,34 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
384
401
|
* 看到 `proxy_warning.callback_may_fail: true`:**不要**叫用户去改代理设置(很多用户不懂),
|
|
385
402
|
直接按设备码流程走 —— CLI 通常已经自动切过去了;
|
|
386
403
|
只有当用户明确说"我就想用浏览器回调"时,才把 `actions` 里第 2/3 条念给他;
|
|
387
|
-
* 看到 `auto_device.applied: true
|
|
388
|
-
|
|
404
|
+
* 看到 `auto_device.applied: true`:按 `reason` 如实说明,别隐瞒:
|
|
405
|
+
* `"proxy_detected"` → 「检测到你开着代理,已自动改用设备码模式(不需要本地回调)」;
|
|
406
|
+
* `"callback_listen_failed"` → 「本地回调端口起不来,已自动改用设备码模式继续」;
|
|
407
|
+
* `"callback_first_wait"` → **只可能**是用户自己显式开了 `--callback-first-wait`:
|
|
408
|
+
「等待到点没收到回调,已同时改用设备码(两条路哪条先成功算哪条)」。
|
|
409
|
+
* 没有 `auto_device`(`applied:false`)就是**默认的浏览器回调**,不要主动提设备码。
|
|
389
410
|
* 想让 CLI **不要**自动改道(例如你确认回调一定通):加 `--no-auto-device`,或设
|
|
390
411
|
`WKONGAI_AUTO_DEVICE=0`。⚠️ 此时 CLI 会**打印警告**说"若代理接管 127.0.0.1,
|
|
391
|
-
点了【同意授权】也收不到结果"
|
|
392
|
-
|
|
412
|
+
点了【同意授权】也收不到结果",并且**不会**再用首轮等待自愈、端口起不来也**不兜底** ——
|
|
413
|
+
尊重用户的选择,但要**把这个风险如实转述**给他;
|
|
414
|
+
* 用户只是**等得久**(没代理、也没报错):**不要**自己改成设备码 —— 0.2.2 起默认就是回调,
|
|
415
|
+
等多久都不会自动变;要设备码必须**显式**加 `--device`(先问用户,别擅自换模式);
|
|
393
416
|
* 排障:`npx -y wkgeo-cli@latest config --json` 里有 `data.proxy` 与 `meta.proxy_warning`,
|
|
394
417
|
是"用户机器上是不是代理挡了回调"的**唯一权威判断**(只读检测,不会改用户任何设置)。
|
|
395
418
|
|
|
396
419
|
### 5.5 打开授权页 / 收银台(**给 AI 端**)
|
|
397
420
|
|
|
398
|
-
> ★ **2026-10
|
|
399
|
-
>
|
|
400
|
-
>
|
|
401
|
-
> 而是**看 `open_browser` 字段 + 把 `paste_text` 念给用户**:
|
|
421
|
+
> ★ **2026-10-21 起:人类终端会自动弹系统浏览器;CI/非交互环境只输出链接**(老板真机反馈:
|
|
422
|
+
> 「别人在 CI 里执行 `wkgeo-cli` 的 login,默认弹出了系统默认浏览器」)。所以你的第一动作**不是**
|
|
423
|
+
> 去开内置浏览器,而是**看 `open_browser` 字段 + 把 `paste_text` 念给用户**:
|
|
402
424
|
|
|
403
425
|
| `open_browser` | 你该做什么 |
|
|
404
426
|
| --- | --- |
|
|
405
427
|
| `opened: true` | ✅ CLI 已经把浏览器弹出来了 → 把 `paste_text` **原样**念给用户即可,**不要**再自己开一遍 |
|
|
406
|
-
| `
|
|
428
|
+
| `skipped_reason: "non_interactive"` | 🖥️ 这是 CI / 管道 / AI 子进程:CLI **没弹**(也不该弹)→ 把 `authorization_url` / `paste_text` 清楚地交给有浏览器的用户;本机收不到回调就用 `login --device` |
|
|
429
|
+
| `opened: false`(`attempted: true`) | ⚠️ 真的尝试过但没能自动打开 → 把那段**含完整地址**的提示念给用户(或可选地在你的内置浏览器里打开,见下) |
|
|
407
430
|
| `skipped_reason: "no_browser"` | 调用方/用户明确不要弹窗 → **只**把完整网址清楚地给用户,不要自己开浏览器 |
|
|
408
|
-
| `skipped_reason: null` | 真的尝试过打开(成没成看 `opened`)——
|
|
431
|
+
| `skipped_reason: null` | 真的尝试过打开(成没成看 `opened`)—— 人类终端的默认情况 |
|
|
409
432
|
|
|
410
433
|
**兜底手段(可选,**只在 CLI 没打开成功**时用)**——按 ① → ② → ③ 判断:
|
|
411
434
|
|
|
@@ -415,8 +438,9 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
415
438
|
③ 都不行 → 把**完整地址**交给用户复制(说清动作是"复制到浏览器地址栏打开")
|
|
416
439
|
```
|
|
417
440
|
|
|
418
|
-
* ⚠️
|
|
419
|
-
|
|
441
|
+
* ⚠️ **这只是备选**:人类终端里 CLI 已经自动弹系统浏览器了,**不要**再叠一层"请在右侧…"的话术 ——
|
|
442
|
+
那会让用户不知道该看哪个窗口。**CI / 非交互环境**里 CLI 不会弹(`skipped_reason:"non_interactive"`),
|
|
443
|
+
这时把链接清楚地交给用户才是主路径。
|
|
420
444
|
* ① **原生就支持**:直接打开,**不需要装任何东西**;已经原生支持"应用内侧边栏"的客户端
|
|
421
445
|
(DSH 自带"点链接进右栏"),**让用户点一下链接**也算数。
|
|
422
446
|
* ② **不支持 → 装桥接插件**(DSH 的 `dsh-browser-open`):
|
|
@@ -444,18 +468,21 @@ Set-Content -LiteralPath "$env:USERPROFILE\.dsh\browser-open.txt" -Value "<完
|
|
|
444
468
|
|
|
445
469
|
| 运行场景 | CLI 会不会自动弹**系统**浏览器 |
|
|
446
470
|
| --- | --- |
|
|
447
|
-
|
|
|
448
|
-
|
|
|
449
|
-
| 加了 `--
|
|
471
|
+
| **人类终端**(stdout 是 TTY、无 CI 信号) | ✅ **会尝试打开** |
|
|
472
|
+
| **CI / 非交互**(stdout 非 TTY / 管道 / AI 子进程;`CI`、`GITHUB_ACTIONS`、`GITLAB_CI`、`JENKINS_URL`、`TF_BUILD`、`BUILDKITE`、`TEAMCITY_VERSION` 任一存在;`WKONGAI_CLI_NO_BROWSER=1`) | ❌ **绝不弹**(`skipped_reason:"non_interactive"`,只输出链接/JSON) |
|
|
473
|
+
| 加了 `--no-browser` | ❌ **不打开**(**优先级最高**,压过 `--open-browser`;`skipped_reason:"no_browser"`) |
|
|
474
|
+
| 加了 `--open-browser` | ✅ 只在**终端**里会(⚠️ 压不过非交互判定;别依赖它去"开启") |
|
|
450
475
|
|
|
451
476
|
* **`--json` 里的结构化字段 `open_browser`**:`{attempted, opened, tried, url, skipped_reason}`
|
|
452
|
-
(结构与取值口径**没有变**)—— `skipped_reason`
|
|
477
|
+
(结构与取值口径**没有变**)—— `skipped_reason` 有三种取值:
|
|
453
478
|
* `null` = 真的尝试过打开(成没成看 `opened`);
|
|
479
|
+
* `"non_interactive"` = CI / 管道 / AI 子进程,**没弹**(也不该弹);
|
|
454
480
|
* `"no_browser"` = 调用方/用户明确要求别打开。
|
|
455
481
|
* ⚠️ 历史取值 `"non_tty_agent_mode"`(2026-10-01 那版"AI 场景默认不弹")**已弃用,不会再出现**
|
|
456
482
|
—— 看到它说明你在读旧文档/旧版本的输出。
|
|
457
483
|
* 同一条事件/数据里还有 **`paste_text`**(`login` 的 `awaiting_callback` 事件、`wallet recharge` 的 `data`):
|
|
458
|
-
|
|
484
|
+
**原样念给用户**,不要自己改写。⚠️ 非交互时它是「请打开以下链接完成授权:<url>」,
|
|
485
|
+
**不会**说"已为你打开浏览器"(因为确实没弹)——照念即可,别自己补一句"已打开"。
|
|
459
486
|
* ⚠️ **不弹浏览器 ≠ 失败**,**打开失败也 ≠ 失败**:流程照旧等回调、照旧返回 `pay_url`;
|
|
460
487
|
CLI **绝不会**因为"没打开浏览器"而让命令失败 —— 你只要把地址送到用户眼前就对了。
|
|
461
488
|
|
|
@@ -535,9 +562,10 @@ npx -y wkgeo-cli@latest wallet recharge --amount-cents 10000 # 生
|
|
|
535
562
|
# (老板:"以那个 token 为基础来进行支付,而不是还要他用用户名那一套")。
|
|
536
563
|
# ⚠️ 老服务端没有这个字段时 `pay_url` 就只有 `order_id`,那条路仍然要求网页登录。
|
|
537
564
|
# ⚠️ **原样打开 data.pay_url**,不要自己拆参/重拼(尾斜杠与编码都有讲究)。
|
|
538
|
-
# ★ 打开方式见 §5.5
|
|
565
|
+
# ★ 打开方式见 §5.5:**人类终端**里 CLI 会尝试弹系统浏览器;**CI/非交互**只输出链接,
|
|
539
566
|
# 你只要看 data.open_browser:opened:true → 把 data.paste_text(已含完整地址 +
|
|
540
|
-
# "已为你打开浏览器…")原样念给用户即可;
|
|
567
|
+
# "已为你打开浏览器…")原样念给用户即可;skipped_reason:"non_interactive" → CLI 没弹,
|
|
568
|
+
# 把 data.pay_url 交给用户;opened:false → 念那段**含完整地址**的提示,
|
|
541
569
|
# 或可选地在自己的内置浏览器里打开 data.pay_url。
|
|
542
570
|
# ⚠️ `data.open_browser.skipped_reason = "no_browser"` = 调用方明确要求别弹(那就只给地址)。
|
|
543
571
|
# ★ **支付成功后那个页面会自己关掉**(收银台会尝试 `window.close()`):
|
|
@@ -555,10 +583,10 @@ npx -y wkgeo-cli@latest orders check <order_id> # 用
|
|
|
555
583
|
**原样打开**即可,**不要**把 `token` 单独摘出来展示、更不要放进对话正文里当"验证码"念给用户;
|
|
556
584
|
* 它**只**授权"读这一单的支付参数":读不到钱包、读不到订单列表、不能下单、不能取消,
|
|
557
585
|
而且 30 分钟(= 订单有效期)后自动失效(失效时页面会明确说"支付链接已过期,请重新下单")。
|
|
558
|
-
⚠️
|
|
559
|
-
⚠️ `--open-browser`
|
|
560
|
-
⚠️ 打开 `pay_url` 的方式见 **§5.5
|
|
561
|
-
|
|
586
|
+
⚠️ 非交互/CI 环境(容器、管道、AI 子进程)**本来就不会弹**:`open_browser.skipped_reason = "non_interactive"`,`pay_url` 照常输出;
|
|
587
|
+
⚠️ 终端里 `--open-browser` 保留显式语义(**非交互/CI 仍不弹**);要显式不弹用 `--no-browser`(`skipped_reason = "no_browser"`,优先级最高)。
|
|
588
|
+
⚠️ 打开 `pay_url` 的方式见 **§5.5**:人类终端会自动弹系统浏览器;
|
|
589
|
+
只有它**没打开成功**(或非交互环境)时,才考虑可选地在客户端**内置浏览器 / 侧边面板**里打开。
|
|
562
590
|
|
|
563
591
|
#### 7.2.1 另一种:**让用户自己选金额**(`wallet recharge-link`,独立充值页)
|
|
564
592
|
|
|
@@ -575,7 +603,7 @@ npx -y wkgeo-cli@latest wallet recharge-link # 默认不弹浏览器
|
|
|
575
603
|
# ★ 有效期只有 10 分钟、且**用掉即失效**(一次性):过期/已用过时页面会明确提示"请重新获取",
|
|
576
604
|
# 这时**重新跑一次本命令**拿一条新链接即可(不要反复让用户刷新旧链接)。
|
|
577
605
|
# ★ 默认**不**打开浏览器(这条命令通常跑在服务器/容器里,且交接物是链接);
|
|
578
|
-
#
|
|
606
|
+
# 确实要在**人类终端**里弹出时加 --open-browser(CI/非交互仍不弹);--no-browser 优先级最高。
|
|
579
607
|
# ★ ticket 只是"建一笔充值单"的窄凭证:⛔ 不要把它当登录凭据、不要拿它去调别的 action。
|
|
580
608
|
npx -y wkgeo-cli@latest orders check <order_id> # 用户说付完了再查(order_id 从他那句"付完了"对应的单拿)
|
|
581
609
|
```
|
|
@@ -695,11 +723,11 @@ npx -y wkgeo-cli@latest publish --json
|
|
|
695
723
|
| --- | --- | --- |
|
|
696
724
|
| `whoami` 返回 exit 3 | 还没登录 / 令牌过期 / 被吊销 | 跑 `login`,把网址给用户点同意 |
|
|
697
725
|
| 浏览器授权后显示"无法访问此网站" | 那一刻本机没有 CLI 在监听(login 被 Ctrl+C 或已退出);**或代理接管了 127.0.0.1** | 重新 `login`(开着代理时 CLI 会自动改设备码);并**保持它在前台**直到点完「同意」 |
|
|
698
|
-
| `login` 等到超时(`kind:"timeout"`) | 用户没点完,**或代理把回调吃掉了** |
|
|
699
|
-
| 事件流第一行是 `awaiting_user` 而不是 `awaiting_callback` |
|
|
700
|
-
| 事件流是 `awaiting_callback`,然后 `data.auto_device.reason:"callback_first_wait"` | ★
|
|
701
|
-
| 用户说「我**没开代理**,为什么变成设备码了」 | ★
|
|
702
|
-
| 用户随后才点【同意授权】→ 浏览器显示 **ERR_CONNECTION_REFUSED** | ★ 2026-10-03
|
|
726
|
+
| `login` 等到超时(`kind:"timeout"`) | 用户没点完,**或代理把回调吃掉了** | ★ 0.2.2 起超时**不会**自动改设备码:看 `hint` 与 `meta.proxy_warning`。若用户确认本地读不了回调,让他**显式**加 `--device` 重跑;若检测到代理,CLI 默认**已经**在第一跳直接改设备码了(除非加了 `--no-auto-device`) |
|
|
727
|
+
| 事件流第一行是 `awaiting_user` 而不是 `awaiting_callback` | 只有两种可能:**检测到系统代理**(CLI 直接改设备码;绕过列表写了 127.* 也照样改)或用户显式 `--device` | 按 §5.3 把 `paste_text` 转述给用户;想强制回调加 `--no-auto-device`(CLI 会警告风险) |
|
|
728
|
+
| 事件流是 `awaiting_callback`,然后 `data.auto_device.reason:"callback_first_wait"` | ★ **0.2.2 起只可能**是用户/调用方**显式**写了 `--callback-first-wait`(默认关闭):首轮等待没收到回调 → CLI 并发改设备码(**本地回调监听同时保留**) | 正常自愈;如实告诉用户"已同时改用设备码,两条路哪条先成功算哪条,不用重来" |
|
|
729
|
+
| 用户说「我**没开代理**,为什么变成设备码了」 | ★ 0.2.2 起默认就是浏览器回调,正常情况下**不会**再发生。若真出现,只可能是:① `proxy_warning.detected:true`(真探到了系统代理);② `reason:"callback_listen_failed"`(本地端口起不来);③ 用户自己显式 `--device` / `--callback-first-wait` | 按 `data.auto_device.reason` 如实解释;先看 `data.proxy_warning.detected`(`config --json` 能看来源) |
|
|
730
|
+
| 用户随后才点【同意授权】→ 浏览器显示 **ERR_CONNECTION_REFUSED** | ★ 2026-10-03 已修:显式首轮等待切设备码时**不再关掉**本地回调监听(旧版一关就是这个报错)。若仍出现,说明 CLI 进程已经退出(Ctrl+C / 终端关掉了) | 让用户直接在**这一次**的浏览器页面上点同意即可(监听还在),或重跑 `login`;不要让他以为授权失败 |
|
|
703
731
|
| `login --device-continue` 报 `kind:"expired"` | 短码过期(默认 10 分钟)或已被换过票 | 重新 `login --device --no-wait` 申请新短码;**别**反复续跑 |
|
|
704
732
|
| 下单直接返回 `paid:true` | 服务端设计:下单时余额足够就直接扣款并履约 | 这是正常的"钱包支付";用 `wallet` 复核余额 |
|
|
705
733
|
| `sites create` 报 400「无法确定所属客户」 | `owner/admin` 这类账号必须显式指定客户 | 加 `--customer <客户ID>` |
|